1 //===-- LZMA.cpp ------------------------------------------------*- C++ -*-===// 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 llvm::inconvertibleErrorCode(), 72 "size of xz-compressed blob (%lu bytes) is smaller than the " 73 "LZMA_STREAM_HEADER_SIZE (%lu bytes)", 74 InputBuffer.size(), LZMA_STREAM_HEADER_SIZE); 75 } 76 77 // Decode xz footer. 78 lzma_ret xzerr = lzma_stream_footer_decode( 79 &opts, InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE).data()); 80 if (xzerr != LZMA_OK) { 81 return llvm::createStringError(llvm::inconvertibleErrorCode(), 82 "lzma_stream_footer_decode()=%s", 83 convertLZMACodeToString(xzerr)); 84 } 85 if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) { 86 return llvm::createStringError( 87 llvm::inconvertibleErrorCode(), 88 "xz-compressed buffer size (%lu bytes) too small (required at " 89 "least %lu bytes) ", 90 InputBuffer.size(), (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 &xzindex, &memlimit, nullptr, 99 InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE + opts.backward_size) 100 .data(), 101 &inpos, InputBuffer.size()); 102 if (xzerr != LZMA_OK) { 103 return llvm::createStringError(llvm::inconvertibleErrorCode(), 104 "lzma_index_buffer_decode()=%s", 105 convertLZMACodeToString(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(xzindex); 111 112 // Deallocate xz index as it is no longer needed. 113 lzma_index_end(xzindex, 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(*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, 0, nullptr, InputBuffer.data(), &inpos, InputBuffer.size(), 133 Uncompressed.data(), &outpos, Uncompressed.size()); 134 if (ret != LZMA_OK) { 135 return llvm::createStringError(llvm::inconvertibleErrorCode(), 136 "lzma_stream_buffer_decode()=%s", 137 convertLZMACodeToString(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