| 1 | //===-- LZMA.cpp ----------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/Host/Config.h" |
| 10 | #include "llvm/ADT/StringRef.h" |
| 11 | #include "llvm/Support/Error.h" |
| 12 | |
| 13 | #if LLDB_ENABLE_LZMA |
| 14 | #include <lzma.h> |
| 15 | #endif // LLDB_ENABLE_LZMA |
| 16 | |
| 17 | namespace lldb_private { |
| 18 | |
| 19 | namespace lzma { |
| 20 | |
| 21 | #if !LLDB_ENABLE_LZMA |
| 22 | bool isAvailable() { return false; } |
| 23 | llvm::Expected<uint64_t> |
| 24 | getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) { |
| 25 | llvm_unreachable("lzma::getUncompressedSize is unavailable" ); |
| 26 | } |
| 27 | |
| 28 | llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer, |
| 29 | llvm::SmallVectorImpl<uint8_t> &Uncompressed) { |
| 30 | llvm_unreachable("lzma::uncompress is unavailable" ); |
| 31 | } |
| 32 | |
| 33 | #else // LLDB_ENABLE_LZMA |
| 34 | |
| 35 | bool isAvailable() { return true; } |
| 36 | |
| 37 | static const char *convertLZMACodeToString(lzma_ret Code) { |
| 38 | switch (Code) { |
| 39 | case LZMA_STREAM_END: |
| 40 | return "lzma error: LZMA_STREAM_END" ; |
| 41 | case LZMA_NO_CHECK: |
| 42 | return "lzma error: LZMA_NO_CHECK" ; |
| 43 | case LZMA_UNSUPPORTED_CHECK: |
| 44 | return "lzma error: LZMA_UNSUPPORTED_CHECK" ; |
| 45 | case LZMA_GET_CHECK: |
| 46 | return "lzma error: LZMA_GET_CHECK" ; |
| 47 | case LZMA_MEM_ERROR: |
| 48 | return "lzma error: LZMA_MEM_ERROR" ; |
| 49 | case LZMA_MEMLIMIT_ERROR: |
| 50 | return "lzma error: LZMA_MEMLIMIT_ERROR" ; |
| 51 | case LZMA_FORMAT_ERROR: |
| 52 | return "lzma error: LZMA_FORMAT_ERROR" ; |
| 53 | case LZMA_OPTIONS_ERROR: |
| 54 | return "lzma error: LZMA_OPTIONS_ERROR" ; |
| 55 | case LZMA_DATA_ERROR: |
| 56 | return "lzma error: LZMA_DATA_ERROR" ; |
| 57 | case LZMA_BUF_ERROR: |
| 58 | return "lzma error: LZMA_BUF_ERROR" ; |
| 59 | case LZMA_PROG_ERROR: |
| 60 | return "lzma error: LZMA_PROG_ERROR" ; |
| 61 | default: |
| 62 | llvm_unreachable("unknown or unexpected lzma status code" ); |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | llvm::Expected<uint64_t> |
| 67 | getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) { |
| 68 | lzma_stream_flags opts{}; |
| 69 | if (InputBuffer.size() < LZMA_STREAM_HEADER_SIZE) { |
| 70 | return llvm::createStringError( |
| 71 | EC: llvm::inconvertibleErrorCode(), |
| 72 | Fmt: "size of xz-compressed blob (%lu bytes) is smaller than the " |
| 73 | "LZMA_STREAM_HEADER_SIZE (%lu bytes)" , |
| 74 | Vals: InputBuffer.size(), LZMA_STREAM_HEADER_SIZE); |
| 75 | } |
| 76 | |
| 77 | // Decode xz footer. |
| 78 | lzma_ret xzerr = lzma_stream_footer_decode( |
| 79 | options: &opts, in: InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE).data()); |
| 80 | if (xzerr != LZMA_OK) { |
| 81 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 82 | Fmt: "lzma_stream_footer_decode()=%s" , |
| 83 | Vals: convertLZMACodeToString(Code: xzerr)); |
| 84 | } |
| 85 | if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) { |
| 86 | return llvm::createStringError( |
| 87 | EC: llvm::inconvertibleErrorCode(), |
| 88 | Fmt: "xz-compressed buffer size (%lu bytes) too small (required at " |
| 89 | "least %lu bytes) " , |
| 90 | Vals: InputBuffer.size(), Vals: (opts.backward_size + LZMA_STREAM_HEADER_SIZE)); |
| 91 | } |
| 92 | |
| 93 | // Decode xz index. |
| 94 | lzma_index *xzindex; |
| 95 | uint64_t memlimit(UINT64_MAX); |
| 96 | size_t inpos = 0; |
| 97 | xzerr = lzma_index_buffer_decode( |
| 98 | i: &xzindex, memlimit: &memlimit, allocator: nullptr, |
| 99 | in: InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE + opts.backward_size) |
| 100 | .data(), |
| 101 | in_pos: &inpos, in_size: InputBuffer.size()); |
| 102 | if (xzerr != LZMA_OK) { |
| 103 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 104 | Fmt: "lzma_index_buffer_decode()=%s" , |
| 105 | Vals: convertLZMACodeToString(Code: xzerr)); |
| 106 | } |
| 107 | |
| 108 | // Get size of uncompressed file to construct an in-memory buffer of the |
| 109 | // same size on the calling end (if needed). |
| 110 | uint64_t uncompressedSize = lzma_index_uncompressed_size(i: xzindex); |
| 111 | |
| 112 | // Deallocate xz index as it is no longer needed. |
| 113 | lzma_index_end(i: xzindex, allocator: nullptr); |
| 114 | |
| 115 | return uncompressedSize; |
| 116 | } |
| 117 | |
| 118 | llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer, |
| 119 | llvm::SmallVectorImpl<uint8_t> &Uncompressed) { |
| 120 | llvm::Expected<uint64_t> uncompressedSize = getUncompressedSize(InputBuffer); |
| 121 | |
| 122 | if (auto err = uncompressedSize.takeError()) |
| 123 | return err; |
| 124 | |
| 125 | Uncompressed.resize(N: *uncompressedSize); |
| 126 | |
| 127 | // Decompress xz buffer to buffer. |
| 128 | uint64_t memlimit = UINT64_MAX; |
| 129 | size_t inpos = 0; |
| 130 | size_t outpos = 0; |
| 131 | lzma_ret ret = lzma_stream_buffer_decode( |
| 132 | memlimit: &memlimit, flags: 0, allocator: nullptr, in: InputBuffer.data(), in_pos: &inpos, in_size: InputBuffer.size(), |
| 133 | out: Uncompressed.data(), out_pos: &outpos, out_size: Uncompressed.size()); |
| 134 | if (ret != LZMA_OK) { |
| 135 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 136 | Fmt: "lzma_stream_buffer_decode()=%s" , |
| 137 | Vals: convertLZMACodeToString(Code: ret)); |
| 138 | } |
| 139 | |
| 140 | return llvm::Error::success(); |
| 141 | } |
| 142 | |
| 143 | #endif // LLDB_ENABLE_LZMA |
| 144 | |
| 145 | } // end of namespace lzma |
| 146 | } // namespace lldb_private |
| 147 | |