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

     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/. */
     5 #include <algorithm>
     6 #include "SeekableZStream.h"
     7 #include "Logging.h"
     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   }
    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);
    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   }
    40   return true;
    41 }
    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 }
    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   }
    66   bool isLastChunk = (chunk == offsetTable.numElements() - 1);
    68   size_t chunkLen = isLastChunk ? lastChunkSize : chunkSize;
    70   if (length == 0 || length > chunkLen)
    71     length = chunkLen;
    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);
    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);
   105   return true;
   106 }
   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);
   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 }
   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);
   157       dest >>= 2;
   158       buf[i] = dest;
   159       buf[i + 1] = dest >> 8;
   160       buf[i + 2] = dest >> 16;
   161     }
   162   }
   163 }
   165 /* Branch/Call/Jump conversion filter for x86, derived from xz-utils
   166  * by Igor Pavlov and Lasse Collin, published in the public domain */
   168 #define Test86MSByte(b) ((b) == 0 || (b) == 0xff)
   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 };
   177   static const uint32_t MASK_TO_BIT_NUMBER[8] =
   178     { 0, 1, 2, 2, 3, 3, 3, 3 };
   180   uint32_t prev_mask = 0;
   181   uint32_t prev_pos = 0;
   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     }
   190     const uint32_t off = offset + (uint32_t)(i) - prev_pos;
   191     prev_pos = offset + (uint32_t)(i);
   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     }
   202     b = buf[i + 4];
   204     if (Test86MSByte(b) && MASK_TO_ALLOWED_STATUS[(prev_mask >> 1) & 0x7]
   205         && (prev_mask >> 1) < 0x10) {
   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]);
   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);
   219         if (prev_mask == 0)
   220           break;
   222         const uint32_t i = MASK_TO_BIT_NUMBER[prev_mask >> 1];
   224         b = (uint8_t)(dest >> (24 - i * 8));
   226         if (!Test86MSByte(b))
   227           break;
   229         src = dest ^ ((1 << (32 - i * 8)) - 1);
   230       }
   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;
   239     } else {
   240       ++i;
   241       prev_mask |= 1;
   242       if (Test86MSByte(b))
   243         prev_mask |= 0x10;
   244     }
   245   }
   246 }
   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