mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
371 lines
15 KiB
C++
371 lines
15 KiB
C++
/*
|
|
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of Oracle nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "jni.h"
|
|
#include "jvm.h"
|
|
#include "imageDecompressor.hpp"
|
|
#include "endian.hpp"
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
typedef jboolean (*ZipInflateFully_t)(void *inBuf, jlong inLen,
|
|
void *outBuf, jlong outLen, char **pmsg);
|
|
static ZipInflateFully_t ZipInflateFully = NULL;
|
|
|
|
#ifndef WIN32
|
|
#define JNI_LIB_PREFIX "lib"
|
|
#ifdef __APPLE__
|
|
#define JNI_LIB_SUFFIX ".dylib"
|
|
#else
|
|
#define JNI_LIB_SUFFIX ".so"
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* Return the address of the entry point named in the zip shared library.
|
|
* @param name - the name of the entry point
|
|
* @return the address of the entry point or NULL
|
|
*/
|
|
static void* findEntry(const char* name) {
|
|
void *addr = JVM_LoadZipLibrary();
|
|
if (addr == NULL) {
|
|
return NULL;
|
|
}
|
|
#ifdef WIN32
|
|
return (void*) GetProcAddress(static_cast<HMODULE>(addr), name);
|
|
#else
|
|
return dlsym(addr, name);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Initialize the array of decompressors.
|
|
*/
|
|
int ImageDecompressor::_decompressors_num = 0;
|
|
ImageDecompressor** ImageDecompressor::_decompressors = NULL;
|
|
void ImageDecompressor::image_decompressor_init() {
|
|
if (_decompressors == NULL) {
|
|
ZipInflateFully = (ZipInflateFully_t) findEntry("ZIP_InflateFully");
|
|
assert(ZipInflateFully != NULL && "ZIP decompressor not found.");
|
|
_decompressors_num = 2;
|
|
_decompressors = new ImageDecompressor*[_decompressors_num];
|
|
_decompressors[0] = new ZipDecompressor("zip");
|
|
_decompressors[1] = new SharedStringDecompressor("compact-cp");
|
|
}
|
|
}
|
|
|
|
void ImageDecompressor::image_decompressor_close() {
|
|
delete[] _decompressors;
|
|
}
|
|
|
|
/*
|
|
* Locate decompressor.
|
|
*/
|
|
ImageDecompressor* ImageDecompressor::get_decompressor(const char * decompressor_name) {
|
|
image_decompressor_init();
|
|
for (int i = 0; i < _decompressors_num; i++) {
|
|
ImageDecompressor* decompressor = _decompressors[i];
|
|
assert(decompressor != NULL && "Decompressors not initialized.");
|
|
if (strcmp(decompressor->get_name(), decompressor_name) == 0) {
|
|
return decompressor;
|
|
}
|
|
}
|
|
assert(false && "No decompressor found.");
|
|
return NULL;
|
|
}
|
|
|
|
// Sparc to read unaligned content
|
|
// u8 l = (*(u8*) ptr);
|
|
// If ptr is not aligned, sparc will fail.
|
|
u8 ImageDecompressor::getU8(u1* ptr, Endian *endian) {
|
|
u8 ret;
|
|
if (endian->is_big_endian()) {
|
|
ret = (u8)ptr[0] << 56 | (u8)ptr[1] << 48 | (u8)ptr[2] << 40 | (u8)ptr[3] << 32 |
|
|
(u8)ptr[4] << 24 | (u8)ptr[5] << 16 | (u8)ptr[6] << 8 | (u8)ptr[7];
|
|
} else {
|
|
ret = (u8)ptr[0] | (u8)ptr[1] << 8 | (u8)ptr[2] << 16 | (u8)ptr[3] << 24 |
|
|
(u8)ptr[4] << 32 | (u8)ptr[5] << 40 | (u8)ptr[6] << 48 | (u8)ptr[7] << 56;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
u4 ImageDecompressor::getU4(u1* ptr, Endian *endian) {
|
|
u4 ret;
|
|
if (endian->is_big_endian()) {
|
|
ret = (u4)ptr[0] << 24 | (u4)ptr[1] << 16 | (u4)ptr[2] << 8 | (u4)ptr[3];
|
|
} else {
|
|
ret = (u4)ptr[0] | (u4)ptr[1] << 8 | (u4)ptr[2] << 16 | (u4)ptr[3] << 24;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Decompression entry point. Called from ImageFileReader::get_resource.
|
|
*/
|
|
void ImageDecompressor::decompress_resource(u1* compressed, u1* uncompressed,
|
|
u8 uncompressed_size, const ImageStrings* strings, Endian *endian) {
|
|
bool has_header = false;
|
|
u1* decompressed_resource = compressed;
|
|
u1* compressed_resource = compressed;
|
|
// Resource could have been transformed by a stack of decompressors.
|
|
// Iterate and decompress resources until there is no more header.
|
|
do {
|
|
ResourceHeader _header;
|
|
u1* compressed_resource_base = compressed_resource;
|
|
_header._magic = getU4(compressed_resource, endian);
|
|
compressed_resource += 4;
|
|
_header._size = getU8(compressed_resource, endian);
|
|
compressed_resource += 8;
|
|
_header._uncompressed_size = getU8(compressed_resource, endian);
|
|
compressed_resource += 8;
|
|
_header._decompressor_name_offset = getU4(compressed_resource, endian);
|
|
compressed_resource += 4;
|
|
_header._decompressor_config_offset = getU4(compressed_resource, endian);
|
|
compressed_resource += 4;
|
|
_header._is_terminal = *compressed_resource;
|
|
compressed_resource += 1;
|
|
has_header = _header._magic == ResourceHeader::resource_header_magic;
|
|
if (has_header) {
|
|
// decompressed_resource array contains the result of decompression
|
|
decompressed_resource = new u1[(size_t) _header._uncompressed_size];
|
|
// Retrieve the decompressor name
|
|
const char* decompressor_name = strings->get(_header._decompressor_name_offset);
|
|
assert(decompressor_name && "image decompressor not found");
|
|
// Retrieve the decompressor instance
|
|
ImageDecompressor* decompressor = get_decompressor(decompressor_name);
|
|
assert(decompressor && "image decompressor not found");
|
|
// Ask the decompressor to decompress the compressed content
|
|
decompressor->decompress_resource(compressed_resource, decompressed_resource,
|
|
&_header, strings);
|
|
if (compressed_resource_base != compressed) {
|
|
delete[] compressed_resource_base;
|
|
}
|
|
compressed_resource = decompressed_resource;
|
|
}
|
|
} while (has_header);
|
|
memcpy(uncompressed, decompressed_resource, (size_t) uncompressed_size);
|
|
delete[] decompressed_resource;
|
|
}
|
|
|
|
// Zip decompressor
|
|
|
|
void ZipDecompressor::decompress_resource(u1* data, u1* uncompressed,
|
|
ResourceHeader* header, const ImageStrings* strings) {
|
|
char* msg = NULL;
|
|
jboolean res = ZipDecompressor::decompress(data, header->_size, uncompressed,
|
|
header->_uncompressed_size, &msg);
|
|
assert(res && "decompression failed");
|
|
}
|
|
|
|
jboolean ZipDecompressor::decompress(void *in, u8 inSize, void *out, u8 outSize, char **pmsg) {
|
|
return (*ZipInflateFully)(in, inSize, out, outSize, pmsg);
|
|
}
|
|
|
|
// END Zip Decompressor
|
|
|
|
// Shared String decompressor
|
|
|
|
// array index is the constant pool tag. value is size.
|
|
// eg: array[5] = 8; means size of long is 8 bytes.
|
|
const u1 SharedStringDecompressor::sizes[] = {
|
|
0, 0, 0, 4, 4, 8, 8, 2, 2, 4, 4, 4, 4, 0, 0, 3, 2, 0, 4
|
|
};
|
|
/**
|
|
* Recreate the class by reconstructing the constant pool.
|
|
*/
|
|
void SharedStringDecompressor::decompress_resource(u1* data,
|
|
u1* uncompressed_resource,
|
|
ResourceHeader* header, const ImageStrings* strings) {
|
|
u1* uncompressed_base = uncompressed_resource;
|
|
u1* data_base = data;
|
|
int header_size = 8; // magic + major + minor
|
|
memcpy(uncompressed_resource, data, header_size + 2); //+ cp count
|
|
uncompressed_resource += header_size + 2;
|
|
data += header_size;
|
|
u2 cp_count = Endian::get_java(data);
|
|
data += 2;
|
|
for (int i = 1; i < cp_count; i++) {
|
|
u1 tag = *data;
|
|
data += 1;
|
|
switch (tag) {
|
|
|
|
case externalized_string:
|
|
{ // String in Strings table
|
|
*uncompressed_resource = 1;
|
|
uncompressed_resource += 1;
|
|
int k = decompress_int(data);
|
|
const char * string = strings->get(k);
|
|
int str_length = (int) strlen(string);
|
|
Endian::set_java(uncompressed_resource, str_length);
|
|
uncompressed_resource += 2;
|
|
memcpy(uncompressed_resource, string, str_length);
|
|
uncompressed_resource += str_length;
|
|
break;
|
|
}
|
|
// Descriptor String has been split and types added to Strings table
|
|
case externalized_string_descriptor:
|
|
{
|
|
*uncompressed_resource = 1;
|
|
uncompressed_resource += 1;
|
|
int descriptor_index = decompress_int(data);
|
|
int indexes_length = decompress_int(data);
|
|
u1* length_address = uncompressed_resource;
|
|
uncompressed_resource += 2;
|
|
int desc_length = 0;
|
|
const char * desc_string = strings->get(descriptor_index);
|
|
if (indexes_length > 0) {
|
|
u1* indexes_base = data;
|
|
data += indexes_length;
|
|
char c = *desc_string;
|
|
do {
|
|
*uncompressed_resource = c;
|
|
uncompressed_resource++;
|
|
desc_length += 1;
|
|
/*
|
|
* Every L character is the marker we are looking at in order
|
|
* to reconstruct the descriptor. Each time an L is found, then
|
|
* we retrieve the couple token/token at the current index and
|
|
* add it to the descriptor.
|
|
* "(L;I)V" and "java/lang","String" couple of tokens,
|
|
* this becomes "(Ljava/lang/String;I)V"
|
|
*/
|
|
if (c == 'L') {
|
|
int index = decompress_int(indexes_base);
|
|
const char * pkg = strings->get(index);
|
|
int str_length = (int) strlen(pkg);
|
|
// the case where we have a package.
|
|
// reconstruct the type full name
|
|
if (str_length > 0) {
|
|
int len = str_length + 1;
|
|
char* fullpkg = new char[len];
|
|
char* pkg_base = fullpkg;
|
|
memcpy(fullpkg, pkg, str_length);
|
|
fullpkg += str_length;
|
|
*fullpkg = '/';
|
|
memcpy(uncompressed_resource, pkg_base, len);
|
|
uncompressed_resource += len;
|
|
delete[] pkg_base;
|
|
desc_length += len;
|
|
} else { // Empty package
|
|
// Nothing to do.
|
|
}
|
|
int classIndex = decompress_int(indexes_base);
|
|
const char * clazz = strings->get(classIndex);
|
|
int clazz_length = (int) strlen(clazz);
|
|
memcpy(uncompressed_resource, clazz, clazz_length);
|
|
uncompressed_resource += clazz_length;
|
|
desc_length += clazz_length;
|
|
}
|
|
desc_string += 1;
|
|
c = *desc_string;
|
|
} while (c != '\0');
|
|
} else {
|
|
desc_length = (int) strlen(desc_string);
|
|
memcpy(uncompressed_resource, desc_string, desc_length);
|
|
uncompressed_resource += desc_length;
|
|
}
|
|
Endian::set_java(length_address, desc_length);
|
|
break;
|
|
}
|
|
|
|
case constant_utf8:
|
|
{ // UTF-8
|
|
*uncompressed_resource = tag;
|
|
uncompressed_resource += 1;
|
|
u2 str_length = Endian::get_java(data);
|
|
int len = str_length + 2;
|
|
memcpy(uncompressed_resource, data, len);
|
|
uncompressed_resource += len;
|
|
data += len;
|
|
break;
|
|
}
|
|
|
|
case constant_long:
|
|
case constant_double:
|
|
{
|
|
i++;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
{
|
|
*uncompressed_resource = tag;
|
|
uncompressed_resource += 1;
|
|
int size = sizes[tag];
|
|
memcpy(uncompressed_resource, data, size);
|
|
uncompressed_resource += size;
|
|
data += size;
|
|
}
|
|
}
|
|
}
|
|
u8 remain = header->_size - (int)(data - data_base);
|
|
u8 computed = (u8)(uncompressed_resource - uncompressed_base) + remain;
|
|
if (header->_uncompressed_size != computed)
|
|
printf("Failure, expecting %llu but getting %llu\n", header->_uncompressed_size,
|
|
computed);
|
|
assert(header->_uncompressed_size == computed &&
|
|
"Constant Pool reconstruction failed");
|
|
memcpy(uncompressed_resource, data, (size_t) remain);
|
|
}
|
|
|
|
/*
|
|
* Decompress integers. Compressed integers are negative.
|
|
* If positive, the integer is not decompressed.
|
|
* If negative, length extracted from the first byte, then reconstruct the integer
|
|
* from the following bytes.
|
|
* Example of compression: 1 is compressed on 1 byte: 10100001
|
|
*/
|
|
int SharedStringDecompressor::decompress_int(unsigned char*& value) {
|
|
int len = 4;
|
|
int res = 0;
|
|
char b1 = *value;
|
|
if (is_compressed((signed char)b1)) { // compressed
|
|
len = get_compressed_length(b1);
|
|
char clearedValue = b1 &= 0x1F;
|
|
if (len == 1) {
|
|
res = clearedValue;
|
|
} else {
|
|
res = (clearedValue & 0xFF) << 8 * (len - 1);
|
|
for (int i = 1; i < len; i++) {
|
|
res |= (value[i]&0xFF) << 8 * (len - i - 1);
|
|
}
|
|
}
|
|
} else {
|
|
res = (value[0] & 0xFF) << 24 | (value[1]&0xFF) << 16 |
|
|
(value[2]&0xFF) << 8 | (value[3]&0xFF);
|
|
}
|
|
value += len;
|
|
return res;
|
|
}
|
|
// END Shared String decompressor
|