mozglue/linker/SeekableZStream.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial