mozglue/linker/SeekableZStream.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:273821f7a9d2
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <algorithm>
6 #include "SeekableZStream.h"
7 #include "Logging.h"
8
9 bool
10 SeekableZStream::Init(const void *buf, size_t length)
11 {
12 const SeekableZStreamHeader *header = SeekableZStreamHeader::validate(buf);
13 if (!header) {
14 LOG("Not a seekable zstream");
15 return false;
16 }
17
18 buffer = reinterpret_cast<const unsigned char *>(buf);
19 totalSize = header->totalSize;
20 chunkSize = header->chunkSize;
21 lastChunkSize = header->lastChunkSize;
22 windowBits = header->windowBits;
23 dictionary.Init(buffer + sizeof(SeekableZStreamHeader), header->dictSize);
24 offsetTable.Init(buffer + sizeof(SeekableZStreamHeader) + header->dictSize,
25 header->nChunks);
26 filter = GetFilter(header->filter);
27
28 /* Sanity check */
29 if ((chunkSize == 0) ||
30 (!IsPageAlignedSize(chunkSize)) ||
31 (chunkSize > 8 * PageSize()) ||
32 (offsetTable.numElements() < 1) ||
33 (lastChunkSize == 0) ||
34 (lastChunkSize > chunkSize) ||
35 (length < totalSize)) {
36 LOG("Malformed or broken seekable zstream");
37 return false;
38 }
39
40 return true;
41 }
42
43 bool
44 SeekableZStream::Decompress(void *where, size_t chunk, size_t length)
45 {
46 while (length) {
47 size_t len = std::min(length, static_cast<size_t>(chunkSize));
48 if (!DecompressChunk(where, chunk, len))
49 return false;
50 where = reinterpret_cast<unsigned char *>(where) + len;
51 length -= len;
52 chunk++;
53 }
54 return true;
55 }
56
57 bool
58 SeekableZStream::DecompressChunk(void *where, size_t chunk, size_t length)
59 {
60 if (chunk >= offsetTable.numElements()) {
61 LOG("DecompressChunk: chunk #%" PRIdSize " out of range [0-%" PRIdSize ")",
62 chunk, offsetTable.numElements());
63 return false;
64 }
65
66 bool isLastChunk = (chunk == offsetTable.numElements() - 1);
67
68 size_t chunkLen = isLastChunk ? lastChunkSize : chunkSize;
69
70 if (length == 0 || length > chunkLen)
71 length = chunkLen;
72
73 DEBUG_LOG("DecompressChunk #%" PRIdSize " @%p (%" PRIdSize "/% " PRIdSize ")",
74 chunk, where, length, chunkLen);
75 z_stream zStream;
76 memset(&zStream, 0, sizeof(zStream));
77 zStream.avail_in = (isLastChunk ? totalSize : uint32_t(offsetTable[chunk + 1]))
78 - uint32_t(offsetTable[chunk]);
79 zStream.next_in = const_cast<Bytef *>(buffer + uint32_t(offsetTable[chunk]));
80 zStream.avail_out = length;
81 zStream.next_out = reinterpret_cast<Bytef *>(where);
82
83 /* Decompress chunk */
84 if (inflateInit2(&zStream, windowBits) != Z_OK) {
85 LOG("inflateInit failed: %s", zStream.msg);
86 return false;
87 }
88 if (dictionary && inflateSetDictionary(&zStream, dictionary,
89 dictionary.numElements()) != Z_OK) {
90 LOG("inflateSetDictionary failed: %s", zStream.msg);
91 return false;
92 }
93 if (inflate(&zStream, (length == chunkLen) ? Z_FINISH : Z_SYNC_FLUSH)
94 != (length == chunkLen) ? Z_STREAM_END : Z_OK) {
95 LOG("inflate failed: %s", zStream.msg);
96 return false;
97 }
98 if (inflateEnd(&zStream) != Z_OK) {
99 LOG("inflateEnd failed: %s", zStream.msg);
100 return false;
101 }
102 if (filter)
103 filter(chunk * chunkSize, UNFILTER, (unsigned char *)where, chunkLen);
104
105 return true;
106 }
107
108 /* Branch/Call/Jump conversion filter for Thumb, derived from xz-utils
109 * by Igor Pavlov and Lasse Collin, published in the public domain */
110 static void
111 BCJ_Thumb_filter(off_t offset, SeekableZStream::FilterDirection dir,
112 unsigned char *buf, size_t size)
113 {
114 size_t i;
115 for (i = 0; i + 4 <= size; i += 2) {
116 if ((buf[i + 1] & 0xf8) == 0xf0 && (buf[i + 3] & 0xf8) == 0xf8) {
117 uint32_t src = (buf[i] << 11)
118 | ((buf[i + 1] & 0x07) << 19)
119 | buf[i + 2]
120 | ((buf[i + 3] & 0x07) << 8);
121 src <<= 1;
122 uint32_t dest;
123 if (dir == SeekableZStream::FILTER)
124 dest = offset + (uint32_t)(i) + 4 + src;
125 else
126 dest = src - (offset + (uint32_t)(i) + 4);
127
128 dest >>= 1;
129 buf[i] = dest >> 11;
130 buf[i + 1] = 0xf0 | ((dest >> 19) & 0x07);
131 buf[i + 2] = dest;
132 buf[i + 3] = 0xf8 | ((dest >> 8) & 0x07);
133 i += 2;
134 }
135 }
136 }
137
138 /* Branch/Call/Jump conversion filter for ARM, derived from xz-utils
139 * by Igor Pavlov and Lasse Collin, published in the public domain */
140 static void
141 BCJ_ARM_filter(off_t offset, SeekableZStream::FilterDirection dir,
142 unsigned char *buf, size_t size)
143 {
144 size_t i;
145 for (i = 0; i + 4 <= size; i += 4) {
146 if (buf[i + 3] == 0xeb) {
147 uint32_t src = buf[i]
148 | (buf[i + 1] << 8)
149 | (buf[i + 2] << 16);
150 src <<= 2;
151 uint32_t dest;
152 if (dir == SeekableZStream::FILTER)
153 dest = offset + (uint32_t)(i) + 8 + src;
154 else
155 dest = src - (offset + (uint32_t)(i) + 8);
156
157 dest >>= 2;
158 buf[i] = dest;
159 buf[i + 1] = dest >> 8;
160 buf[i + 2] = dest >> 16;
161 }
162 }
163 }
164
165 /* Branch/Call/Jump conversion filter for x86, derived from xz-utils
166 * by Igor Pavlov and Lasse Collin, published in the public domain */
167
168 #define Test86MSByte(b) ((b) == 0 || (b) == 0xff)
169
170 static void
171 BCJ_X86_filter(off_t offset, SeekableZStream::FilterDirection dir,
172 unsigned char *buf, size_t size)
173 {
174 static const bool MASK_TO_ALLOWED_STATUS[8] =
175 { true, true, true, false, true, false, false, false };
176
177 static const uint32_t MASK_TO_BIT_NUMBER[8] =
178 { 0, 1, 2, 2, 3, 3, 3, 3 };
179
180 uint32_t prev_mask = 0;
181 uint32_t prev_pos = 0;
182
183 for (size_t i = 0; i + 5 <= size;) {
184 uint8_t b = buf[i];
185 if (b != 0xe8 && b != 0xe9) {
186 ++i;
187 continue;
188 }
189
190 const uint32_t off = offset + (uint32_t)(i) - prev_pos;
191 prev_pos = offset + (uint32_t)(i);
192
193 if (off > 5) {
194 prev_mask = 0;
195 } else {
196 for (uint32_t i = 0; i < off; ++i) {
197 prev_mask &= 0x77;
198 prev_mask <<= 1;
199 }
200 }
201
202 b = buf[i + 4];
203
204 if (Test86MSByte(b) && MASK_TO_ALLOWED_STATUS[(prev_mask >> 1) & 0x7]
205 && (prev_mask >> 1) < 0x10) {
206
207 uint32_t src = ((uint32_t)(b) << 24)
208 | ((uint32_t)(buf[i + 3]) << 16)
209 | ((uint32_t)(buf[i + 2]) << 8)
210 | (buf[i + 1]);
211
212 uint32_t dest;
213 while (true) {
214 if (dir == SeekableZStream::FILTER)
215 dest = src + (offset + (uint32_t)(i) + 5);
216 else
217 dest = src - (offset + (uint32_t)(i) + 5);
218
219 if (prev_mask == 0)
220 break;
221
222 const uint32_t i = MASK_TO_BIT_NUMBER[prev_mask >> 1];
223
224 b = (uint8_t)(dest >> (24 - i * 8));
225
226 if (!Test86MSByte(b))
227 break;
228
229 src = dest ^ ((1 << (32 - i * 8)) - 1);
230 }
231
232 buf[i + 4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
233 buf[i + 3] = (uint8_t)(dest >> 16);
234 buf[i + 2] = (uint8_t)(dest >> 8);
235 buf[i + 1] = (uint8_t)(dest);
236 i += 5;
237 prev_mask = 0;
238
239 } else {
240 ++i;
241 prev_mask |= 1;
242 if (Test86MSByte(b))
243 prev_mask |= 0x10;
244 }
245 }
246 }
247
248 SeekableZStream::ZStreamFilter
249 SeekableZStream::GetFilter(SeekableZStream::FilterId id)
250 {
251 switch (id) {
252 case BCJ_THUMB:
253 return BCJ_Thumb_filter;
254 case BCJ_ARM:
255 return BCJ_ARM_filter;
256 case BCJ_X86:
257 return BCJ_X86_filter;
258 default:
259 return nullptr;
260 }
261 return nullptr;
262 }

mercurial