1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mozglue/linker/SeekableZStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,262 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <algorithm> 1.9 +#include "SeekableZStream.h" 1.10 +#include "Logging.h" 1.11 + 1.12 +bool 1.13 +SeekableZStream::Init(const void *buf, size_t length) 1.14 +{ 1.15 + const SeekableZStreamHeader *header = SeekableZStreamHeader::validate(buf); 1.16 + if (!header) { 1.17 + LOG("Not a seekable zstream"); 1.18 + return false; 1.19 + } 1.20 + 1.21 + buffer = reinterpret_cast<const unsigned char *>(buf); 1.22 + totalSize = header->totalSize; 1.23 + chunkSize = header->chunkSize; 1.24 + lastChunkSize = header->lastChunkSize; 1.25 + windowBits = header->windowBits; 1.26 + dictionary.Init(buffer + sizeof(SeekableZStreamHeader), header->dictSize); 1.27 + offsetTable.Init(buffer + sizeof(SeekableZStreamHeader) + header->dictSize, 1.28 + header->nChunks); 1.29 + filter = GetFilter(header->filter); 1.30 + 1.31 + /* Sanity check */ 1.32 + if ((chunkSize == 0) || 1.33 + (!IsPageAlignedSize(chunkSize)) || 1.34 + (chunkSize > 8 * PageSize()) || 1.35 + (offsetTable.numElements() < 1) || 1.36 + (lastChunkSize == 0) || 1.37 + (lastChunkSize > chunkSize) || 1.38 + (length < totalSize)) { 1.39 + LOG("Malformed or broken seekable zstream"); 1.40 + return false; 1.41 + } 1.42 + 1.43 + return true; 1.44 +} 1.45 + 1.46 +bool 1.47 +SeekableZStream::Decompress(void *where, size_t chunk, size_t length) 1.48 +{ 1.49 + while (length) { 1.50 + size_t len = std::min(length, static_cast<size_t>(chunkSize)); 1.51 + if (!DecompressChunk(where, chunk, len)) 1.52 + return false; 1.53 + where = reinterpret_cast<unsigned char *>(where) + len; 1.54 + length -= len; 1.55 + chunk++; 1.56 + } 1.57 + return true; 1.58 +} 1.59 + 1.60 +bool 1.61 +SeekableZStream::DecompressChunk(void *where, size_t chunk, size_t length) 1.62 +{ 1.63 + if (chunk >= offsetTable.numElements()) { 1.64 + LOG("DecompressChunk: chunk #%" PRIdSize " out of range [0-%" PRIdSize ")", 1.65 + chunk, offsetTable.numElements()); 1.66 + return false; 1.67 + } 1.68 + 1.69 + bool isLastChunk = (chunk == offsetTable.numElements() - 1); 1.70 + 1.71 + size_t chunkLen = isLastChunk ? lastChunkSize : chunkSize; 1.72 + 1.73 + if (length == 0 || length > chunkLen) 1.74 + length = chunkLen; 1.75 + 1.76 + DEBUG_LOG("DecompressChunk #%" PRIdSize " @%p (%" PRIdSize "/% " PRIdSize ")", 1.77 + chunk, where, length, chunkLen); 1.78 + z_stream zStream; 1.79 + memset(&zStream, 0, sizeof(zStream)); 1.80 + zStream.avail_in = (isLastChunk ? totalSize : uint32_t(offsetTable[chunk + 1])) 1.81 + - uint32_t(offsetTable[chunk]); 1.82 + zStream.next_in = const_cast<Bytef *>(buffer + uint32_t(offsetTable[chunk])); 1.83 + zStream.avail_out = length; 1.84 + zStream.next_out = reinterpret_cast<Bytef *>(where); 1.85 + 1.86 + /* Decompress chunk */ 1.87 + if (inflateInit2(&zStream, windowBits) != Z_OK) { 1.88 + LOG("inflateInit failed: %s", zStream.msg); 1.89 + return false; 1.90 + } 1.91 + if (dictionary && inflateSetDictionary(&zStream, dictionary, 1.92 + dictionary.numElements()) != Z_OK) { 1.93 + LOG("inflateSetDictionary failed: %s", zStream.msg); 1.94 + return false; 1.95 + } 1.96 + if (inflate(&zStream, (length == chunkLen) ? Z_FINISH : Z_SYNC_FLUSH) 1.97 + != (length == chunkLen) ? Z_STREAM_END : Z_OK) { 1.98 + LOG("inflate failed: %s", zStream.msg); 1.99 + return false; 1.100 + } 1.101 + if (inflateEnd(&zStream) != Z_OK) { 1.102 + LOG("inflateEnd failed: %s", zStream.msg); 1.103 + return false; 1.104 + } 1.105 + if (filter) 1.106 + filter(chunk * chunkSize, UNFILTER, (unsigned char *)where, chunkLen); 1.107 + 1.108 + return true; 1.109 +} 1.110 + 1.111 +/* Branch/Call/Jump conversion filter for Thumb, derived from xz-utils 1.112 + * by Igor Pavlov and Lasse Collin, published in the public domain */ 1.113 +static void 1.114 +BCJ_Thumb_filter(off_t offset, SeekableZStream::FilterDirection dir, 1.115 + unsigned char *buf, size_t size) 1.116 +{ 1.117 + size_t i; 1.118 + for (i = 0; i + 4 <= size; i += 2) { 1.119 + if ((buf[i + 1] & 0xf8) == 0xf0 && (buf[i + 3] & 0xf8) == 0xf8) { 1.120 + uint32_t src = (buf[i] << 11) 1.121 + | ((buf[i + 1] & 0x07) << 19) 1.122 + | buf[i + 2] 1.123 + | ((buf[i + 3] & 0x07) << 8); 1.124 + src <<= 1; 1.125 + uint32_t dest; 1.126 + if (dir == SeekableZStream::FILTER) 1.127 + dest = offset + (uint32_t)(i) + 4 + src; 1.128 + else 1.129 + dest = src - (offset + (uint32_t)(i) + 4); 1.130 + 1.131 + dest >>= 1; 1.132 + buf[i] = dest >> 11; 1.133 + buf[i + 1] = 0xf0 | ((dest >> 19) & 0x07); 1.134 + buf[i + 2] = dest; 1.135 + buf[i + 3] = 0xf8 | ((dest >> 8) & 0x07); 1.136 + i += 2; 1.137 + } 1.138 + } 1.139 +} 1.140 + 1.141 +/* Branch/Call/Jump conversion filter for ARM, derived from xz-utils 1.142 + * by Igor Pavlov and Lasse Collin, published in the public domain */ 1.143 +static void 1.144 +BCJ_ARM_filter(off_t offset, SeekableZStream::FilterDirection dir, 1.145 + unsigned char *buf, size_t size) 1.146 +{ 1.147 + size_t i; 1.148 + for (i = 0; i + 4 <= size; i += 4) { 1.149 + if (buf[i + 3] == 0xeb) { 1.150 + uint32_t src = buf[i] 1.151 + | (buf[i + 1] << 8) 1.152 + | (buf[i + 2] << 16); 1.153 + src <<= 2; 1.154 + uint32_t dest; 1.155 + if (dir == SeekableZStream::FILTER) 1.156 + dest = offset + (uint32_t)(i) + 8 + src; 1.157 + else 1.158 + dest = src - (offset + (uint32_t)(i) + 8); 1.159 + 1.160 + dest >>= 2; 1.161 + buf[i] = dest; 1.162 + buf[i + 1] = dest >> 8; 1.163 + buf[i + 2] = dest >> 16; 1.164 + } 1.165 + } 1.166 +} 1.167 + 1.168 +/* Branch/Call/Jump conversion filter for x86, derived from xz-utils 1.169 + * by Igor Pavlov and Lasse Collin, published in the public domain */ 1.170 + 1.171 +#define Test86MSByte(b) ((b) == 0 || (b) == 0xff) 1.172 + 1.173 +static void 1.174 +BCJ_X86_filter(off_t offset, SeekableZStream::FilterDirection dir, 1.175 + unsigned char *buf, size_t size) 1.176 +{ 1.177 + static const bool MASK_TO_ALLOWED_STATUS[8] = 1.178 + { true, true, true, false, true, false, false, false }; 1.179 + 1.180 + static const uint32_t MASK_TO_BIT_NUMBER[8] = 1.181 + { 0, 1, 2, 2, 3, 3, 3, 3 }; 1.182 + 1.183 + uint32_t prev_mask = 0; 1.184 + uint32_t prev_pos = 0; 1.185 + 1.186 + for (size_t i = 0; i + 5 <= size;) { 1.187 + uint8_t b = buf[i]; 1.188 + if (b != 0xe8 && b != 0xe9) { 1.189 + ++i; 1.190 + continue; 1.191 + } 1.192 + 1.193 + const uint32_t off = offset + (uint32_t)(i) - prev_pos; 1.194 + prev_pos = offset + (uint32_t)(i); 1.195 + 1.196 + if (off > 5) { 1.197 + prev_mask = 0; 1.198 + } else { 1.199 + for (uint32_t i = 0; i < off; ++i) { 1.200 + prev_mask &= 0x77; 1.201 + prev_mask <<= 1; 1.202 + } 1.203 + } 1.204 + 1.205 + b = buf[i + 4]; 1.206 + 1.207 + if (Test86MSByte(b) && MASK_TO_ALLOWED_STATUS[(prev_mask >> 1) & 0x7] 1.208 + && (prev_mask >> 1) < 0x10) { 1.209 + 1.210 + uint32_t src = ((uint32_t)(b) << 24) 1.211 + | ((uint32_t)(buf[i + 3]) << 16) 1.212 + | ((uint32_t)(buf[i + 2]) << 8) 1.213 + | (buf[i + 1]); 1.214 + 1.215 + uint32_t dest; 1.216 + while (true) { 1.217 + if (dir == SeekableZStream::FILTER) 1.218 + dest = src + (offset + (uint32_t)(i) + 5); 1.219 + else 1.220 + dest = src - (offset + (uint32_t)(i) + 5); 1.221 + 1.222 + if (prev_mask == 0) 1.223 + break; 1.224 + 1.225 + const uint32_t i = MASK_TO_BIT_NUMBER[prev_mask >> 1]; 1.226 + 1.227 + b = (uint8_t)(dest >> (24 - i * 8)); 1.228 + 1.229 + if (!Test86MSByte(b)) 1.230 + break; 1.231 + 1.232 + src = dest ^ ((1 << (32 - i * 8)) - 1); 1.233 + } 1.234 + 1.235 + buf[i + 4] = (uint8_t)(~(((dest >> 24) & 1) - 1)); 1.236 + buf[i + 3] = (uint8_t)(dest >> 16); 1.237 + buf[i + 2] = (uint8_t)(dest >> 8); 1.238 + buf[i + 1] = (uint8_t)(dest); 1.239 + i += 5; 1.240 + prev_mask = 0; 1.241 + 1.242 + } else { 1.243 + ++i; 1.244 + prev_mask |= 1; 1.245 + if (Test86MSByte(b)) 1.246 + prev_mask |= 0x10; 1.247 + } 1.248 + } 1.249 +} 1.250 + 1.251 +SeekableZStream::ZStreamFilter 1.252 +SeekableZStream::GetFilter(SeekableZStream::FilterId id) 1.253 +{ 1.254 + switch (id) { 1.255 + case BCJ_THUMB: 1.256 + return BCJ_Thumb_filter; 1.257 + case BCJ_ARM: 1.258 + return BCJ_ARM_filter; 1.259 + case BCJ_X86: 1.260 + return BCJ_X86_filter; 1.261 + default: 1.262 + return nullptr; 1.263 + } 1.264 + return nullptr; 1.265 +}