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
17namespace lldb_private {
18
19namespace lzma {
20
21#if !LLDB_ENABLE_LZMA
22bool isAvailable() { return false; }
23llvm::Expected<uint64_t>
24getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
25 llvm_unreachable("lzma::getUncompressedSize is unavailable");
26}
27
28llvm::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
35bool isAvailable() { return true; }
36
37static 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
66llvm::Expected<uint64_t>
67getUncompressedSize(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
118llvm::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

source code of lldb/source/Host/common/LZMA.cpp