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 | |