Path: blob/master/src/java.base/share/native/libjimage/imageDecompressor.cpp
41149 views
/*1* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031#include "jni.h"32#include "imageDecompressor.hpp"33#include "endian.hpp"34#ifdef WIN3235#include <windows.h>36#else37#include <dlfcn.h>38#endif3940typedef jboolean (*ZipInflateFully_t)(void *inBuf, jlong inLen,41void *outBuf, jlong outLen, char **pmsg);42static ZipInflateFully_t ZipInflateFully = NULL;4344#ifndef WIN3245#define JNI_LIB_PREFIX "lib"46#ifdef __APPLE__47#define JNI_LIB_SUFFIX ".dylib"48#else49#define JNI_LIB_SUFFIX ".so"50#endif51#endif5253/**54* Return the address of the entry point named in the zip shared library.55* @param name - the name of the entry point56* @return the address of the entry point or NULL57*/58static void* findEntry(const char* name) {59void *addr = NULL;60#ifdef WIN3261HMODULE handle = GetModuleHandle("zip.dll");62if (handle == NULL) {63handle = LoadLibrary("zip.dll");64}65if (handle == NULL) {66return NULL;67}68addr = (void*) GetProcAddress(handle, name);69return addr;70#else71addr = dlopen(JNI_LIB_PREFIX "zip" JNI_LIB_SUFFIX, RTLD_GLOBAL|RTLD_LAZY);72if (addr == NULL) {73return NULL;74}75addr = dlsym(addr, name);76return addr;77#endif78}7980/*81* Initialize the array of decompressors.82*/83int ImageDecompressor::_decompressors_num = 0;84ImageDecompressor** ImageDecompressor::_decompressors = NULL;85void ImageDecompressor::image_decompressor_init() {86if (_decompressors == NULL) {87ZipInflateFully = (ZipInflateFully_t) findEntry("ZIP_InflateFully");88assert(ZipInflateFully != NULL && "ZIP decompressor not found.");89_decompressors_num = 2;90_decompressors = new ImageDecompressor*[_decompressors_num];91_decompressors[0] = new ZipDecompressor("zip");92_decompressors[1] = new SharedStringDecompressor("compact-cp");93}94}9596void ImageDecompressor::image_decompressor_close() {97delete[] _decompressors;98}99100/*101* Locate decompressor.102*/103ImageDecompressor* ImageDecompressor::get_decompressor(const char * decompressor_name) {104image_decompressor_init();105for (int i = 0; i < _decompressors_num; i++) {106ImageDecompressor* decompressor = _decompressors[i];107assert(decompressor != NULL && "Decompressors not initialized.");108if (strcmp(decompressor->get_name(), decompressor_name) == 0) {109return decompressor;110}111}112assert(false && "No decompressor found.");113return NULL;114}115116// Sparc to read unaligned content117// u8 l = (*(u8*) ptr);118// If ptr is not aligned, sparc will fail.119u8 ImageDecompressor::getU8(u1* ptr, Endian *endian) {120u8 ret;121if (endian->is_big_endian()) {122ret = (u8)ptr[0] << 56 | (u8)ptr[1] << 48 | (u8)ptr[2]<<40 | (u8)ptr[3]<<32 |123ptr[4]<<24 | ptr[5]<<16 | ptr[6]<<8 | ptr[7];124} else {125ret = ptr[0] | ptr[1]<<8 | ptr[2]<<16 | ptr[3]<<24 | (u8)ptr[4]<<32 |126(u8)ptr[5]<<40 | (u8)ptr[6]<<48 | (u8)ptr[7]<<56;127}128return ret;129}130131u4 ImageDecompressor::getU4(u1* ptr, Endian *endian) {132u4 ret;133if (endian->is_big_endian()) {134ret = ptr[0] << 24 | ptr[1]<<16 | (ptr[2]<<8) | ptr[3];135} else {136ret = ptr[0] | ptr[1]<<8 | (ptr[2]<<16) | ptr[3]<<24;137}138return ret;139}140141/*142* Decompression entry point. Called from ImageFileReader::get_resource.143*/144void ImageDecompressor::decompress_resource(u1* compressed, u1* uncompressed,145u8 uncompressed_size, const ImageStrings* strings, Endian *endian) {146bool has_header = false;147u1* decompressed_resource = compressed;148u1* compressed_resource = compressed;149// Resource could have been transformed by a stack of decompressors.150// Iterate and decompress resources until there is no more header.151do {152ResourceHeader _header;153u1* compressed_resource_base = compressed_resource;154_header._magic = getU4(compressed_resource, endian);155compressed_resource += 4;156_header._size = getU8(compressed_resource, endian);157compressed_resource += 8;158_header._uncompressed_size = getU8(compressed_resource, endian);159compressed_resource += 8;160_header._decompressor_name_offset = getU4(compressed_resource, endian);161compressed_resource += 4;162_header._decompressor_config_offset = getU4(compressed_resource, endian);163compressed_resource += 4;164_header._is_terminal = *compressed_resource;165compressed_resource += 1;166has_header = _header._magic == ResourceHeader::resource_header_magic;167if (has_header) {168// decompressed_resource array contains the result of decompression169decompressed_resource = new u1[(size_t) _header._uncompressed_size];170// Retrieve the decompressor name171const char* decompressor_name = strings->get(_header._decompressor_name_offset);172assert(decompressor_name && "image decompressor not found");173// Retrieve the decompressor instance174ImageDecompressor* decompressor = get_decompressor(decompressor_name);175assert(decompressor && "image decompressor not found");176// Ask the decompressor to decompress the compressed content177decompressor->decompress_resource(compressed_resource, decompressed_resource,178&_header, strings);179if (compressed_resource_base != compressed) {180delete[] compressed_resource_base;181}182compressed_resource = decompressed_resource;183}184} while (has_header);185memcpy(uncompressed, decompressed_resource, (size_t) uncompressed_size);186delete[] decompressed_resource;187}188189// Zip decompressor190191void ZipDecompressor::decompress_resource(u1* data, u1* uncompressed,192ResourceHeader* header, const ImageStrings* strings) {193char* msg = NULL;194jboolean res = ZipDecompressor::decompress(data, header->_size, uncompressed,195header->_uncompressed_size, &msg);196assert(res && "decompression failed");197}198199jboolean ZipDecompressor::decompress(void *in, u8 inSize, void *out, u8 outSize, char **pmsg) {200return (*ZipInflateFully)(in, inSize, out, outSize, pmsg);201}202203// END Zip Decompressor204205// Shared String decompressor206207// array index is the constant pool tag. value is size.208// eg: array[5] = 8; means size of long is 8 bytes.209const u1 SharedStringDecompressor::sizes[] = {2100, 0, 0, 4, 4, 8, 8, 2, 2, 4, 4, 4, 4, 0, 0, 3, 2, 0, 4211};212/**213* Recreate the class by reconstructing the constant pool.214*/215void SharedStringDecompressor::decompress_resource(u1* data,216u1* uncompressed_resource,217ResourceHeader* header, const ImageStrings* strings) {218u1* uncompressed_base = uncompressed_resource;219u1* data_base = data;220int header_size = 8; // magic + major + minor221memcpy(uncompressed_resource, data, header_size + 2); //+ cp count222uncompressed_resource += header_size + 2;223data += header_size;224u2 cp_count = Endian::get_java(data);225data += 2;226for (int i = 1; i < cp_count; i++) {227u1 tag = *data;228data += 1;229switch (tag) {230231case externalized_string:232{ // String in Strings table233*uncompressed_resource = 1;234uncompressed_resource += 1;235int k = decompress_int(data);236const char * string = strings->get(k);237int str_length = (int) strlen(string);238Endian::set_java(uncompressed_resource, str_length);239uncompressed_resource += 2;240memcpy(uncompressed_resource, string, str_length);241uncompressed_resource += str_length;242break;243}244// Descriptor String has been split and types added to Strings table245case externalized_string_descriptor:246{247*uncompressed_resource = 1;248uncompressed_resource += 1;249int descriptor_index = decompress_int(data);250int indexes_length = decompress_int(data);251u1* length_address = uncompressed_resource;252uncompressed_resource += 2;253int desc_length = 0;254const char * desc_string = strings->get(descriptor_index);255if (indexes_length > 0) {256u1* indexes_base = data;257data += indexes_length;258char c = *desc_string;259do {260*uncompressed_resource = c;261uncompressed_resource++;262desc_length += 1;263/*264* Every L character is the marker we are looking at in order265* to reconstruct the descriptor. Each time an L is found, then266* we retrieve the couple token/token at the current index and267* add it to the descriptor.268* "(L;I)V" and "java/lang","String" couple of tokens,269* this becomes "(Ljava/lang/String;I)V"270*/271if (c == 'L') {272int index = decompress_int(indexes_base);273const char * pkg = strings->get(index);274int str_length = (int) strlen(pkg);275// the case where we have a package.276// reconstruct the type full name277if (str_length > 0) {278int len = str_length + 1;279char* fullpkg = new char[len];280char* pkg_base = fullpkg;281memcpy(fullpkg, pkg, str_length);282fullpkg += str_length;283*fullpkg = '/';284memcpy(uncompressed_resource, pkg_base, len);285uncompressed_resource += len;286delete[] pkg_base;287desc_length += len;288} else { // Empty package289// Nothing to do.290}291int classIndex = decompress_int(indexes_base);292const char * clazz = strings->get(classIndex);293int clazz_length = (int) strlen(clazz);294memcpy(uncompressed_resource, clazz, clazz_length);295uncompressed_resource += clazz_length;296desc_length += clazz_length;297}298desc_string += 1;299c = *desc_string;300} while (c != '\0');301} else {302desc_length = (int) strlen(desc_string);303memcpy(uncompressed_resource, desc_string, desc_length);304uncompressed_resource += desc_length;305}306Endian::set_java(length_address, desc_length);307break;308}309310case constant_utf8:311{ // UTF-8312*uncompressed_resource = tag;313uncompressed_resource += 1;314u2 str_length = Endian::get_java(data);315int len = str_length + 2;316memcpy(uncompressed_resource, data, len);317uncompressed_resource += len;318data += len;319break;320}321322case constant_long:323case constant_double:324{325i++;326}327/* fall through */328default:329{330*uncompressed_resource = tag;331uncompressed_resource += 1;332int size = sizes[tag];333memcpy(uncompressed_resource, data, size);334uncompressed_resource += size;335data += size;336}337}338}339u8 remain = header->_size - (int)(data - data_base);340u8 computed = (u8)(uncompressed_resource - uncompressed_base) + remain;341if (header->_uncompressed_size != computed)342printf("Failure, expecting %llu but getting %llu\n", header->_uncompressed_size,343computed);344assert(header->_uncompressed_size == computed &&345"Constant Pool reconstruction failed");346memcpy(uncompressed_resource, data, (size_t) remain);347}348349/*350* Decompress integers. Compressed integers are negative.351* If positive, the integer is not decompressed.352* If negative, length extracted from the first byte, then reconstruct the integer353* from the following bytes.354* Example of compression: 1 is compressed on 1 byte: 10100001355*/356int SharedStringDecompressor::decompress_int(unsigned char*& value) {357int len = 4;358int res = 0;359char b1 = *value;360if (is_compressed((signed char)b1)) { // compressed361len = get_compressed_length(b1);362char clearedValue = b1 &= 0x1F;363if (len == 1) {364res = clearedValue;365} else {366res = (clearedValue & 0xFF) << 8 * (len - 1);367for (int i = 1; i < len; i++) {368res |= (value[i]&0xFF) << 8 * (len - i - 1);369}370}371} else {372res = (value[0] & 0xFF) << 24 | (value[1]&0xFF) << 16 |373(value[2]&0xFF) << 8 | (value[3]&0xFF);374}375value += len;376return res;377}378// END Shared String decompressor379380381