# HG changeset patch # User jlaskey # Date 1441372303 10800 # Node ID ac2c73b45253aae24ed9935525290a1725c97aaa # Parent e00364b38376be8640f3d6b2e6a0dd2117825edc 8087181: Move native jimage code to its own library (maybe libjimage) Reviewed-by: alanb, lfoltan, hseigel, acorn Contributed-by: james.laskey@oracle.com, jean-francois.denise@oracle.com, roger.riggs@oracle.com diff -r e00364b38376 -r ac2c73b45253 jdk/make/lib/CoreLibraries.gmk --- a/jdk/make/lib/CoreLibraries.gmk Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/make/lib/CoreLibraries.gmk Fri Sep 04 10:11:43 2015 -0300 @@ -238,6 +238,48 @@ ########################################################################################## +$(eval $(call SetupNativeCompilation,BUILD_LIBJIMAGE, \ + LIBRARY := jimage, \ + OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ + OPTIMIZATION := LOW, \ + SRC := $(JDK_TOPDIR)/src/java.base/share/native/libjimage \ + $(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjimage, \ + EXCLUDES := $(LIBJIMAGE_EXCLUDES), \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(JIMAGELIB_CPPFLAGS) \ + -I$(JDK_TOPDIR)/src/java.base/share/native/libjava \ + -I$(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjava \ + -I$(JDK_TOPDIR)/src/java.base/share/native/libjimage \ + -I$(SUPPORT_OUTPUTDIR)/headers/java.base, \ + CFLAGS_unix := -UDEBUG, \ + MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjimage/mapfile-vers, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ + $(call SET_SHARED_LIBRARY_ORIGIN) \ + $(EXPORT_JIMAGE_FUNCS), \ + LDFLAGS_windows := -export:JIMAGE_Open -export:JIMAGE_Close \ + -export:JIMAGE_PackageToModule \ + -export:JIMAGE_FindResource -export:JIMAGE_GetResource \ + -export:JIMAGE_ResourceIterator, \ + LDFLAGS_SUFFIX_unix := -ljvm -ldl $(LIBCXX), \ + LDFLAGS_SUFFIX_linux := , \ + LDFLAGS_SUFFIX_solaris := -lc, \ + LDFLAGS_SUFFIX_aix := ,\ + LDFLAGS_SUFFIX_macosx := -lc++, \ + LDFLAGS_SUFFIX_windows := jvm.lib, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ + RC_FLAGS := $(RC_FLAGS) \ + -D "JDK_FNAME=jimage.dll" \ + -D "JDK_INTERNAL_NAME=jimage" \ + -D "JDK_FTYPE=0x2L", \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjimage, \ + DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) + +$(BUILD_LIBJIMAGE): $(BUILD_LIBJAVA) + +TARGETS += $(BUILD_LIBJIMAGE) + +########################################################################################## + LIBJLI_SRC_DIRS := $(call FindSrcDirsForLib, java.base, jli) LIBJLI_CFLAGS := $(CFLAGS_JDKLIB) diff -r e00364b38376 -r ac2c73b45253 jdk/make/mapfiles/libjava/mapfile-vers --- a/jdk/make/mapfiles/libjava/mapfile-vers Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/make/mapfiles/libjava/mapfile-vers Fri Sep 04 10:11:43 2015 -0300 @@ -240,16 +240,6 @@ Java_java_util_TimeZone_getSystemTimeZoneID; Java_java_util_TimeZone_getSystemGMTOffsetID; Java_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8; - Java_jdk_internal_jimage_ImageNativeSubstrate_openImage; - Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage; - Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress; - Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress; - Java_jdk_internal_jimage_ImageNativeSubstrate_read; - Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed; - Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes; - Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes; - Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes; - Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets; Java_sun_misc_MessageUtils_toStderr; Java_sun_misc_MessageUtils_toStdout; Java_sun_misc_NativeSignalHandler_handle0; diff -r e00364b38376 -r ac2c73b45253 jdk/make/mapfiles/libjimage/mapfile-vers --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/mapfiles/libjimage/mapfile-vers Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,55 @@ +# +# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + JNI_OnLoad; + Java_jdk_internal_jimage_ImageNativeSubstrate_openImage; + Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage; + Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress; + Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress; + Java_jdk_internal_jimage_ImageNativeSubstrate_read; + Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed; + Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes; + Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes; + Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes; + Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets; + Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Open; + Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Close; + Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1FindResource; + Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1GetResource; + Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1PackageToModule; + Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Resources; + JIMAGE_Open; + JIMAGE_Close; + JIMAGE_PackageToModule; + JIMAGE_FindResource; + JIMAGE_GetResource; + JIMAGE_ResourceIterator; + local: + *; +}; diff -r e00364b38376 -r ac2c73b45253 jdk/make/mapfiles/libzip/reorder-sparc --- a/jdk/make/mapfiles/libzip/reorder-sparc Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/make/mapfiles/libzip/reorder-sparc Fri Sep 04 10:11:43 2015 -0300 @@ -12,6 +12,7 @@ text: .text%addMetaName: OUTPUTDIR/zip_util.o; text: .text%ZIP_FindEntry; text: .text%ZIP_GetEntry; +text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; diff -r e00364b38376 -r ac2c73b45253 jdk/make/mapfiles/libzip/reorder-sparcv9 --- a/jdk/make/mapfiles/libzip/reorder-sparcv9 Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/make/mapfiles/libzip/reorder-sparcv9 Fri Sep 04 10:11:43 2015 -0300 @@ -11,6 +11,7 @@ text: .text%addMetaName: OUTPUTDIR/zip_util.o; text: .text%ZIP_FindEntry; text: .text%ZIP_GetEntry; +text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; diff -r e00364b38376 -r ac2c73b45253 jdk/make/mapfiles/libzip/reorder-x86 --- a/jdk/make/mapfiles/libzip/reorder-x86 Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/make/mapfiles/libzip/reorder-x86 Fri Sep 04 10:11:43 2015 -0300 @@ -12,6 +12,7 @@ text: .text%addMetaName: OUTPUTDIR/zip_util.o; text: .text%ZIP_FindEntry; text: .text%ZIP_GetEntry; +text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java --- a/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java Fri Sep 04 10:11:43 2015 -0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import java.util.Comparator; import java.util.stream.IntStream; -public class BasicImageReader { +public class BasicImageReader implements AutoCloseable { private final String imagePath; private final ImageSubstrate substrate; private final ByteOrder byteOrder; diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/classes/jdk/internal/jimage/ImageNativeSubstrate.java --- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageNativeSubstrate.java Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageNativeSubstrate.java Fri Sep 04 10:11:43 2015 -0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,18 @@ import sun.misc.JavaNioAccess; import sun.misc.SharedSecrets; -final class ImageNativeSubstrate implements ImageSubstrate { +public final class ImageNativeSubstrate implements ImageSubstrate { + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public Void run() { + System.loadLibrary("jimage"); + return null; + } + }); + } + private static final JavaNioAccess NIOACCESS = SharedSecrets.getJavaNioAccess(); @@ -52,6 +63,20 @@ native static long[] findAttributes(long id, byte[] path); native static int[] attributeOffsets(long id); + public native static long JIMAGE_Open(String path) throws IOException; + public native static void JIMAGE_Close(long jimageHandle); + public native static long JIMAGE_FindResource(long jimageHandle, + String moduleName, String Version, String path, + long[] size); + public native static long JIMAGE_GetResource(long jimageHandle, + long locationHandle, byte[] buffer, long size); + // Get an array of names that match; return the count found upto array size + public native static int JIMAGE_Resources(long jimageHandle, + String[] outputNames); + // Return the module name for the package + public native static String JIMAGE_PackageToModule(long imageHandle, + String packageName); + static ByteBuffer newDirectByteBuffer(long address, long capacity) { assert capacity < Integer.MAX_VALUE; return NIOACCESS.newDirectByteBuffer(address, (int)capacity, null); diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/include/jvm.h --- a/jdk/src/java.base/share/native/include/jvm.h Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/src/java.base/share/native/include/jvm.h Fri Sep 04 10:11:43 2015 -0300 @@ -557,48 +557,6 @@ JVM_SupportsCX8(void); /* - * jdk.internal.jimage - */ - -JNIEXPORT jlong JNICALL -JVM_ImageOpen(JNIEnv *env, const char *nativePath, jboolean big_endian); - -JNIEXPORT void JNICALL -JVM_ImageClose(JNIEnv *env, jlong id); - -JNIEXPORT jlong JNICALL -JVM_ImageGetIndexAddress(JNIEnv *env, jlong id); - -JNIEXPORT jlong JNICALL -JVM_ImageGetDataAddress(JNIEnv *env,jlong id); - -JNIEXPORT jboolean JNICALL -JVM_ImageRead(JNIEnv *env, jlong id, jlong offset, - unsigned char* uncompressedAddress, jlong uncompressed_size); - -JNIEXPORT jboolean JNICALL -JVM_ImageReadCompressed(JNIEnv *env, jlong id, jlong offset, - unsigned char* compressedBuffer, jlong compressed_size, - unsigned char* uncompressedBuffer, jlong uncompressed_size); - -JNIEXPORT const char* JNICALL -JVM_ImageGetStringBytes(JNIEnv *env, jlong id, jint offset); - -JNIEXPORT jlong* JNICALL -JVM_ImageGetAttributes(JNIEnv *env, jlong* rawAttributes, jlong id, jint offset); - -JNIEXPORT jsize JNICALL -JVM_ImageGetAttributesCount(JNIEnv *env); - -JNIEXPORT jlong* JNICALL -JVM_ImageFindAttributes(JNIEnv *env, jlong* rawAttributes, jbyte* rawBytes, jsize size, jlong id); - -JNIEXPORT jint* JNICALL -JVM_ImageAttributeOffsets(JNIEnv *env, jint* rawOffsets, unsigned int length, jlong id); - -JNIEXPORT unsigned int JNICALL -JVM_ImageAttributeOffsetsLength(JNIEnv *env, jlong id); -/* * com.sun.dtrace.jsdt support */ diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjava/Image.c --- a/jdk/src/java.base/share/native/libjava/Image.c Mon Aug 31 21:48:00 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include - -#include "jni.h" -#include "jvm.h" -#include "jdk_internal_jimage_ImageNativeSubstrate.h" - -JNIEXPORT jlong JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_openImage(JNIEnv *env, - jclass cls, jstring path, jboolean big_endian) { - const char *nativePath; - jlong ret; - - nativePath = (*env)->GetStringUTFChars(env, path, NULL); - ret = JVM_ImageOpen(env, nativePath, big_endian); - (*env)->ReleaseStringUTFChars(env, path, nativePath); - return ret; -} - -JNIEXPORT void JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage(JNIEnv *env, - jclass cls, jlong id) { - JVM_ImageClose(env, id); -} - -JNIEXPORT jlong JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress(JNIEnv *env, - jclass cls, jlong id) { - return JVM_ImageGetIndexAddress(env, id); -} - -JNIEXPORT jlong JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress(JNIEnv *env, - jclass cls, jlong id) { - return JVM_ImageGetDataAddress(env, id); -} - -JNIEXPORT jboolean JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_read(JNIEnv *env, - jclass cls, jlong id, jlong offset, - jobject uncompressedBuffer, jlong uncompressed_size) { - unsigned char* uncompressedAddress; - - uncompressedAddress = (unsigned char*) (*env)->GetDirectBufferAddress(env, uncompressedBuffer); - if (uncompressedBuffer == NULL) { - return JNI_FALSE; - } - return JVM_ImageRead(env, id, offset, uncompressedAddress, uncompressed_size); -} - -JNIEXPORT jboolean JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed(JNIEnv *env, - jclass cls, jlong id, jlong offset, - jobject compressedBuffer, jlong compressed_size, - jobject uncompressedBuffer, jlong uncompressed_size) { - // Get address of read direct buffer. - unsigned char* compressedAddress; - unsigned char* uncompressedAddress; - - compressedAddress = (unsigned char*) (*env)->GetDirectBufferAddress(env, compressedBuffer); - // Get address of decompression direct buffer. - uncompressedAddress = (unsigned char*) (*env)->GetDirectBufferAddress(env, uncompressedBuffer); - if (uncompressedBuffer == NULL || compressedBuffer == NULL) { - return JNI_FALSE; - } - return JVM_ImageReadCompressed(env, id, offset, compressedAddress, compressed_size, - uncompressedAddress, uncompressed_size); -} - -JNIEXPORT jbyteArray JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes(JNIEnv *env, - jclass cls, jlong id, jint offset) { - const char* data; - size_t size; - jbyteArray byteArray; - jbyte* rawBytes; - - data = JVM_ImageGetStringBytes(env, id, offset); - // Determine String length. - size = strlen(data); - // Allocate byte array. - byteArray = (*env)->NewByteArray(env, (jsize) size); - if (byteArray == NULL) { - return NULL; - } - // Get array base address. - rawBytes = (*env)->GetByteArrayElements(env, byteArray, NULL); - // Copy bytes from image string table. - memcpy(rawBytes, data, size); - // Release byte array base address. - (*env)->ReleaseByteArrayElements(env, byteArray, rawBytes, 0); - return byteArray; -} - -JNIEXPORT jlongArray JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes(JNIEnv *env, - jclass cls, jlong id, jint offset) { - // Allocate a jlong large enough for all location attributes. - jlongArray attributes; - jlong* rawAttributes; - jlong* ret; - - attributes = (*env)->NewLongArray(env, JVM_ImageGetAttributesCount(env)); - if (attributes == NULL) { - return NULL; - } - // Get base address for jlong array. - rawAttributes = (*env)->GetLongArrayElements(env, attributes, NULL); - ret = JVM_ImageGetAttributes(env, rawAttributes, id, offset); - // Release jlong array base address. - (*env)->ReleaseLongArrayElements(env, attributes, rawAttributes, 0); - return ret == NULL ? NULL : attributes; -} - -JNIEXPORT jlongArray JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes(JNIEnv *env, - jclass cls, jlong id, jbyteArray utf8) { - // Allocate a jlong large enough for all location attributes. - jsize count; - jlongArray attributes; - jlong* rawAttributes; - jsize size; - jbyte* rawBytes; - jlong* ret; - - count = JVM_ImageGetAttributesCount(env); - attributes = (*env)->NewLongArray(env, JVM_ImageGetAttributesCount(env)); - if (attributes == NULL) { - return NULL; - } - // Get base address for jlong array. - rawAttributes = (*env)->GetLongArrayElements(env, attributes, NULL); - size = (*env)->GetArrayLength(env, utf8); - rawBytes = (*env)->GetByteArrayElements(env, utf8, NULL); - ret = JVM_ImageFindAttributes(env, rawAttributes, rawBytes, size, id); - (*env)->ReleaseByteArrayElements(env, utf8, rawBytes, 0); - // Release jlong array base address. - (*env)->ReleaseLongArrayElements(env, attributes, rawAttributes, 0); - return ret == NULL ? NULL : attributes; - -} - -JNIEXPORT jintArray JNICALL -Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets(JNIEnv *env, - jclass cls, jlong id) { - unsigned int length; - jintArray offsets; - jint* rawOffsets; - jint* ret; - - length = JVM_ImageAttributeOffsetsLength(env, id); - offsets = (*env)->NewIntArray(env, length); - if (offsets == NULL) { - return NULL; - } - // Get base address of result. - rawOffsets = (*env)->GetIntArrayElements(env, offsets, NULL); - ret = JVM_ImageAttributeOffsets(env, rawOffsets, length, id); - if (length == 0) { - return NULL; - } - // Release result base address. - (*env)->ReleaseIntArrayElements(env, offsets, rawOffsets, 0); - return ret == NULL ? NULL : offsets; -} diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include + +#include "jni.h" +#include "jni_util.h" +#include "jdk_util.h" +#include "endian.hpp" +#include "imageDecompressor.hpp" +#include "imageFile.hpp" +#include "inttypes.hpp" +#include "jimage.hpp" +#include "osSupport.hpp" + +#include "jdk_internal_jimage_ImageNativeSubstrate.h" + +extern bool MemoryMapImage; + +// jdk.internal.jimage ///////////////////////////////////////////////////////// + +// Java entry to open an image file for sharing. + +static jlong JIMAGE_Open(JNIEnv *env, const char *nativePath, jboolean big_endian) { + // Open image file for reading. + ImageFileReader* reader = ImageFileReader::open(nativePath, big_endian != JNI_FALSE); + // Return image ID as a jlong. + return ImageFileReader::readerToID(reader); +} + +// Java entry for closing a shared image file. + +static void JIMAGE_Close(JNIEnv *env, jlong id) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // If valid reader the close. + if (reader != NULL) { + ImageFileReader::close(reader); + } +} + +// Java entry for accessing the base address of the image index. + +static jlong JIMAGE_GetIndexAddress(JNIEnv *env, jlong id) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // If valid reader return index base address (as jlong) else zero. + return reader != NULL ? (jlong) reader->get_index_address() : 0L; +} + +// Java entry for accessing the base address of the image data. + +static jlong JIMAGE_GetDataAddress(JNIEnv *env, jlong id) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // If valid reader return data base address (as jlong) else zero. + return MemoryMapImage && reader != NULL ? (jlong) reader->get_data_address() : 0L; +} + +// Java entry for reading an uncompressed resource from the image. + +static jboolean JIMAGE_Read(JNIEnv *env, jlong id, jlong offset, + unsigned char* uncompressedAddress, jlong uncompressed_size) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id);\ + // If not a valid reader the fail the read. + if (reader == NULL) return false; + // Get the file offset of resource data. + u8 file_offset = reader->get_index_size() + offset; + // Check validity of arguments. + if (offset < 0 || + uncompressed_size < 0 || + file_offset > reader->file_size() - uncompressed_size) { + return false; + } + // Read file content into buffer. + return (jboolean) reader->read_at((u1*) uncompressedAddress, uncompressed_size, + file_offset); +} + +// Java entry for reading a compressed resource from the image. + +static jboolean JIMAGE_ReadCompressed(JNIEnv *env, + jlong id, jlong offset, + unsigned char* compressedAddress, jlong compressed_size, + unsigned char* uncompressedAddress, jlong uncompressed_size) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // If not a valid reader the fail the read. + if (reader == NULL) return false; + // Get the file offset of resource data. + u8 file_offset = reader->get_index_size() + offset; + // Check validity of arguments. + if (offset < 0 || + compressed_size < 0 || + uncompressed_size < 0 || + file_offset > reader->file_size() - compressed_size) { + return false; + } + + // Read file content into buffer. + bool is_read = reader->read_at(compressedAddress, compressed_size, + file_offset); + // If successfully read then decompress. + if (is_read) { + const ImageStrings strings = reader->get_strings(); + ImageDecompressor::decompress_resource(compressedAddress, uncompressedAddress, + (u4) uncompressed_size, &strings); + } + return (jboolean) is_read; +} + +// Java entry for retrieving UTF-8 bytes from image string table. + +static const char* JIMAGE_GetStringBytes(JNIEnv *env, jlong id, jint offset) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // Fail if not valid reader. + if (reader == NULL) return NULL; + // Manage image string table. + ImageStrings strings = reader->get_strings(); + // Retrieve string adrress from table. + const char* data = strings.get(offset); + return data; +} + +// Utility function to copy location information into a jlong array. +// WARNING: This function is experimental and temporary during JDK 9 development +// cycle. It will not be supported in the eventual JDK 9 release. + +static void image_expand_location(JNIEnv *env, jlong* rawAttributes, ImageLocation& location) { + // Copy attributes from location. + for (int kind = ImageLocation::ATTRIBUTE_END + 1; + kind < ImageLocation::ATTRIBUTE_COUNT; + kind++) { + rawAttributes[kind] = location.get_attribute(kind); + } +} + +// Java entry for retrieving location attributes for attribute offset. + +static jlong* JIMAGE_GetAttributes(JNIEnv *env, jlong* rawAttributes, jlong id, jint offset) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // Fail if not valid reader. + if (reader == NULL) return NULL; + // Retrieve first byte address of resource's location attribute stream. + u1* data = reader->get_location_offset_data(offset); + // Fail if not valid offset. + if (data == NULL) return NULL; + // Expand stream into array. + ImageLocation location(data); + image_expand_location(env, rawAttributes, location); + return rawAttributes; +} + +// Java entry for retrieving location attributes count for attribute offset. + +static jsize JIMAGE_GetAttributesCount(JNIEnv *env) { + return ImageLocation::ATTRIBUTE_COUNT; +} + +// Java entry for retrieving location attributes for named resource. + +static jlong* JIMAGE_FindAttributes(JNIEnv *env, jlong* rawAttributes, jbyte* rawBytes, jsize size, jlong id) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // Fail if not valid reader. + if (reader == NULL) return NULL; + // Convert byte array to a cstring. + char* path = new char[size + 1]; + memcpy(path, rawBytes, size); + path[size] = '\0'; + // Locate resource location data. + ImageLocation location; + bool found = reader->find_location(path, location); + delete path; + // Resource not found. + if (!found) return NULL; + // Expand stream into array. + image_expand_location(env, rawAttributes, location); + return rawAttributes; +} + +// Java entry for retrieving all the attribute stream offsets from an image. + +static jint* JIMAGE_AttributeOffsets(JNIEnv *env, jint* rawOffsets, unsigned int length, jlong id) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // Fail if not valid reader. + if (reader == NULL) return NULL; + // Determine endian for reader. + Endian* endian = reader->endian(); + // Get base address of attribute stream offsets table. + u4* offsets_table = reader->offsets_table(); + // Allocate int array result. + // Copy values to result (converting endian.) + for (u4 i = 0; i < length; i++) { + rawOffsets[i] = endian->get(offsets_table[i]); + } + return rawOffsets; +} + +// Java entry for retrieving all the attribute stream offsets length from an image. + +static unsigned int JIMAGE_AttributeOffsetsLength(JNIEnv *env, jlong id) { + // Convert image ID to image reader structure. + ImageFileReader* reader = ImageFileReader::idToReader(id); + // Fail if not valid reader. + if (reader == NULL) return 0; + // Get perfect hash table length. + u4 length = reader->table_length(); + return (jint) length; +} + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved) { + JNIEnv *env; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK) { + return JNI_EVERSION; /* JNI version not supported */ + } + + return JNI_VERSION_1_2; +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_openImage(JNIEnv *env, + jclass cls, jstring path, jboolean big_endian) { + const char *nativePath; + jlong ret; + + nativePath = env->GetStringUTFChars(path, NULL); + ret = JIMAGE_Open(env, nativePath, big_endian); + env->ReleaseStringUTFChars(path, nativePath); + return ret; +} + +JNIEXPORT void JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage(JNIEnv *env, + jclass cls, jlong id) { + JIMAGE_Close(env, id); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress(JNIEnv *env, + jclass cls, jlong id) { + return JIMAGE_GetIndexAddress(env, id); +} + +JNIEXPORT jlong JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress(JNIEnv *env, + jclass cls, jlong id) { + return JIMAGE_GetDataAddress(env, id); +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_read(JNIEnv *env, + jclass cls, jlong id, jlong offset, + jobject uncompressedBuffer, jlong uncompressed_size) { + unsigned char* uncompressedAddress; + + uncompressedAddress = (unsigned char*) env->GetDirectBufferAddress(uncompressedBuffer); + if (uncompressedAddress == NULL) { + return JNI_FALSE; + } + return JIMAGE_Read(env, id, offset, uncompressedAddress, uncompressed_size); +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed(JNIEnv *env, + jclass cls, jlong id, jlong offset, + jobject compressedBuffer, jlong compressed_size, + jobject uncompressedBuffer, jlong uncompressed_size) { + // Get address of read direct buffer. + unsigned char* compressedAddress; + unsigned char* uncompressedAddress; + + compressedAddress = (unsigned char*) env->GetDirectBufferAddress(compressedBuffer); + // Get address of decompression direct buffer. + uncompressedAddress = (unsigned char*) env->GetDirectBufferAddress(uncompressedBuffer); + if (compressedAddress == NULL || uncompressedAddress == NULL) { + return JNI_FALSE; + } + return JIMAGE_ReadCompressed(env, id, offset, compressedAddress, compressed_size, + uncompressedAddress, uncompressed_size); +} + +JNIEXPORT jbyteArray JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes(JNIEnv *env, + jclass cls, jlong id, jint offset) { + const char* data; + size_t size; + jbyteArray byteArray; + jbyte* rawBytes; + + data = JIMAGE_GetStringBytes(env, id, offset); + // Determine String length. + size = strlen(data); + // Allocate byte array. + byteArray = env->NewByteArray((jsize) size); + if (byteArray == NULL) { + return NULL; + } + // Get array base address. + rawBytes = env->GetByteArrayElements(byteArray, NULL); + // Copy bytes from image string table. + memcpy(rawBytes, data, size); + // Release byte array base address. + env->ReleaseByteArrayElements(byteArray, rawBytes, 0); + return byteArray; +} + +JNIEXPORT jlongArray JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes(JNIEnv *env, + jclass cls, jlong id, jint offset) { + // Allocate a jlong large enough for all location attributes. + jlongArray attributes; + jlong* rawAttributes; + jlong* ret; + + attributes = env->NewLongArray(JIMAGE_GetAttributesCount(env)); + if (attributes == NULL) { + return NULL; + } + // Get base address for jlong array. + rawAttributes = env->GetLongArrayElements(attributes, NULL); + ret = JIMAGE_GetAttributes(env, rawAttributes, id, offset); + // Release jlong array base address. + env->ReleaseLongArrayElements(attributes, rawAttributes, 0); + return ret == NULL ? NULL : attributes; +} + +JNIEXPORT jlongArray JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes(JNIEnv *env, + jclass cls, jlong id, jbyteArray utf8) { + // Allocate a jlong large enough for all location attributes. + jsize count; + jlongArray attributes; + jlong* rawAttributes; + jsize size; + jbyte* rawBytes; + jlong* ret; + + count = JIMAGE_GetAttributesCount(env); + attributes = env->NewLongArray(JIMAGE_GetAttributesCount(env)); + if (attributes == NULL) { + return NULL; + } + // Get base address for jlong array. + rawAttributes = env->GetLongArrayElements(attributes, NULL); + size = env->GetArrayLength(utf8); + rawBytes = env->GetByteArrayElements(utf8, NULL); + ret = JIMAGE_FindAttributes(env, rawAttributes, rawBytes, size, id); + env->ReleaseByteArrayElements(utf8, rawBytes, 0); + // Release jlong array base address. + env->ReleaseLongArrayElements(attributes, rawAttributes, 0); + return ret == NULL ? NULL : attributes; + +} + +JNIEXPORT jintArray JNICALL +Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets(JNIEnv *env, + jclass cls, jlong id) { + unsigned int length; + jintArray offsets; + jint* rawOffsets; + jint* ret; + + length = JIMAGE_AttributeOffsetsLength(env, id); + offsets = env->NewIntArray(length); + if (offsets == NULL) { + return NULL; + } + // Get base address of result. + rawOffsets = env->GetIntArrayElements(offsets, NULL); + ret = JIMAGE_AttributeOffsets(env, rawOffsets, length, id); + if (length == 0) { + return NULL; + } + // Release result base address. + env->ReleaseIntArrayElements(offsets, rawOffsets, 0); + return ret == NULL ? NULL : offsets; +} + +/* + * Class: jdk_internal_jimage_ImageNativeSubstrate + * Method: JIMAGE_open + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Open +(JNIEnv *env, jclass, jstring path) { + const char *nativePath = env->GetStringUTFChars(path, NULL); + if (nativePath == NULL) + return 0; // Exception already thrown + jint error; + jlong ret = (jlong) JIMAGE_Open(nativePath, &error); + env->ReleaseStringUTFChars(path, nativePath); + return ret; +} + +/* + * Class: jdk_internal_jimage_ImageNativeSubstrate + * Method: JIMAGE_Close + * Signature: (J)J + */ +JNIEXPORT void JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Close +(JNIEnv *env, jclass, jlong jimageHandle) { + JIMAGE_Close((JImageFile*) jimageHandle); +} + +/* + * Class: jdk_internal_jimage_ImageNativeSubstrate + * Method: JIMAGE_FindResource + * Signature: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[J)J + */ +JNIEXPORT jlong JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1FindResource +(JNIEnv *env, jclass, jlong jimageHandle, jstring moduleName, + jstring version, jstring path, jlongArray output_size) { + const char *native_module = NULL; + const char *native_version = NULL; + const char *native_path = NULL; + jlong * native_array = NULL; + jlong size = 0; + jlong ret = 0; + + do { + native_module = env->GetStringUTFChars(moduleName, NULL); + if (native_module == NULL) + break; + native_version = env->GetStringUTFChars(version, NULL); + if (native_version == NULL) + break; + native_path = env->GetStringUTFChars(path, NULL); + if (native_path == NULL) + break; + if (env->GetArrayLength(output_size) < 1) + break; + // Get base address for jlong array. + native_array = env->GetLongArrayElements(output_size, NULL); + if (native_array == NULL) + break; + + ret = (jlong) JIMAGE_FindResource((JImageFile *) jimageHandle, + native_module, native_version, native_path, &size); + if (ret != 0) + *native_array = size; + } while (0); + + if (native_array != NULL) + env->ReleaseLongArrayElements(output_size, native_array, 0); + if (native_path != NULL) + env->ReleaseStringUTFChars(path, native_path); + if (native_version != NULL) + env->ReleaseStringUTFChars(path, native_version); + if (native_module != NULL) + env->ReleaseStringUTFChars(path, native_module); + + return ret; +} + +/* + * Class: jdk_internal_jimage_ImageNativeSubstrate + * Method: JIMAGE_GetResource + * Signature: (JJ[BJ)J + */ +JNIEXPORT jlong JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1GetResource +(JNIEnv *env, jclass, jlong jimageHandle, jlong jlocationHandle, jbyteArray buffer, jlong size) { + jbyte * native_buffer = NULL; + jlong actual_size = 0; + do { + if (env->GetArrayLength(buffer) < size) + break; + + native_buffer = env->GetByteArrayElements(buffer, NULL); + if (native_buffer == NULL) + break; + + actual_size = JIMAGE_GetResource((JImageFile*) jimageHandle, + (JImageLocationRef) jlocationHandle, + (char *) native_buffer, size); + } while (0); + // Release byte array + if (native_buffer != NULL) + env->ReleaseByteArrayElements(buffer, native_buffer, 0); + + return actual_size; +} + +// Structure passed from iterator to a visitor to accumulate the results + +struct VisitorData { + JNIEnv *env; + int size; // current number of strings + int max; // Maximum number of strings + jobjectArray array; // String array to store the strings +}; + +// Visitor to accumulate fully qualified resource names + +static bool resourceVisitor(JImageFile* image, + const char* module, const char* version, const char* package, + const char* name, const char* extension, void* arg) { + struct VisitorData *vdata = (struct VisitorData *) arg; + JNIEnv* env = vdata->env; + if (vdata->size < vdata->max) { + // Store if there is room in the array + // Concatenate to get full path + char fullpath[IMAGE_MAX_PATH]; + fullpath[0] = '\0'; + if (*module != '\0') { + strncpy(fullpath, "/", IMAGE_MAX_PATH - 1); + strncat(fullpath, module, IMAGE_MAX_PATH - 1); + strncat(fullpath, "/", IMAGE_MAX_PATH - 1); + } + if (*package != '\0') { + strncat(fullpath, package, IMAGE_MAX_PATH - 1); + strncat(fullpath, "/", IMAGE_MAX_PATH - 1); + } + strncat(fullpath, name, IMAGE_MAX_PATH - 1); + if (*extension != '\0') { + strncat(fullpath, ".", IMAGE_MAX_PATH - 1); + strncat(fullpath, extension, IMAGE_MAX_PATH - 1); + } + jobject str = env->NewStringUTF(fullpath); + JNU_CHECK_EXCEPTION_RETURN(env, true); + env->SetObjectArrayElement(vdata->array, vdata->size, str); + JNU_CHECK_EXCEPTION_RETURN(env, true); + } + vdata->size++; // always count so the total size is returned + return true; +} + +/* + * Class: jdk_internal_jimage_ImageNativeSubstrate + * Method: JIMAGE_Resources + * Signature: (J)V + */ +JNIEXPORT jint JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1Resources +(JNIEnv *env, jclass, jlong jimageHandle, + jobjectArray outputNames) { + struct VisitorData vdata; + vdata.env = env; + vdata.max = 0; + vdata.size = 0; + vdata.array = outputNames; + + vdata.max = (outputNames != NULL) ? env->GetArrayLength(outputNames) : 0; + JIMAGE_ResourceIterator((JImageFile*) jimageHandle, &resourceVisitor, &vdata); + return vdata.size; +} + +/* + * Class: jdk_internal_jimage_ImageNativeSubstrate + * Method: JIMAGE_PackageToModule + * Signature: (JLjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1PackageToModule +(JNIEnv *env, jclass, jlong jimageHandle, jstring package_name) { + const char *native_package = NULL; + const char *native_module = NULL; + jstring module = NULL; + + native_package = env->GetStringUTFChars(package_name, NULL); + JNU_CHECK_EXCEPTION_RETURN(env, NULL); + + native_module = JIMAGE_PackageToModule((JImageFile*) jimageHandle, native_package); + if (native_module != NULL) { + module = env->NewStringUTF(native_module); + } + env->ReleaseStringUTFChars(package_name, native_package); + return module; +} + +JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { + ImageDecompressor::image_decompressor_close(); +} diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/endian.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/endian.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "endian.hpp" +#include "inttypes.hpp" + +// Most modern compilers optimize the bswap routines to native instructions. +inline static u2 bswap_16(u2 x) { + return ((x & 0xFF) << 8) | + ((x >> 8) & 0xFF); +} + +inline static u4 bswap_32(u4 x) { + return ((x & 0xFF) << 24) | + ((x & 0xFF00) << 8) | + ((x >> 8) & 0xFF00) | + ((x >> 24) & 0xFF); +} + +inline static u8 bswap_64(u8 x) { + return (u8)bswap_32((u4)x) << 32 | + (u8)bswap_32((u4)(x >> 32)); +} + +u2 NativeEndian::get(u2 x) { return x; } +u4 NativeEndian::get(u4 x) { return x; } +u8 NativeEndian::get(u8 x) { return x; } +s2 NativeEndian::get(s2 x) { return x; } +s4 NativeEndian::get(s4 x) { return x; } +s8 NativeEndian::get(s8 x) { return x; } + +void NativeEndian::set(u2& x, u2 y) { x = y; } +void NativeEndian::set(u4& x, u4 y) { x = y; } +void NativeEndian::set(u8& x, u8 y) { x = y; } +void NativeEndian::set(s2& x, s2 y) { x = y; } +void NativeEndian::set(s4& x, s4 y) { x = y; } +void NativeEndian::set(s8& x, s8 y) { x = y; } + +NativeEndian NativeEndian::_native; + +u2 SwappingEndian::get(u2 x) { return bswap_16(x); } +u4 SwappingEndian::get(u4 x) { return bswap_32(x); } +u8 SwappingEndian::get(u8 x) { return bswap_64(x); } +s2 SwappingEndian::get(s2 x) { return bswap_16(x); } +s4 SwappingEndian::get(s4 x) { return bswap_32(x); } +s8 SwappingEndian::get(s8 x) { return bswap_64(x); } + +void SwappingEndian::set(u2& x, u2 y) { x = bswap_16(y); } +void SwappingEndian::set(u4& x, u4 y) { x = bswap_32(y); } +void SwappingEndian::set(u8& x, u8 y) { x = bswap_64(y); } +void SwappingEndian::set(s2& x, s2 y) { x = bswap_16(y); } +void SwappingEndian::set(s4& x, s4 y) { x = bswap_32(y); } +void SwappingEndian::set(s8& x, s8 y) { x = bswap_64(y); } + +SwappingEndian SwappingEndian::_swapping; + +Endian* Endian::get_handler(bool big_endian) { + // If requesting little endian on a little endian machine or + // big endian on a big endian machine use native handler + if (big_endian == is_big_endian()) { + return NativeEndian::get_native(); + } else { + // Use swapping handler. + return SwappingEndian::get_swapping(); + } +} + +// Return a platform u2 from an array in which Big Endian is applied. +u2 Endian::get_java(u1* x) { + return (u2) (x[0]<<8 | x[1]); +} + +// Add a platform u2 to the array as a Big Endian u2 +void Endian::set_java(u1* p, u2 x) { + p[0] = (x >> 8) & 0xff; + p[1] = x & 0xff; +} + +Endian* Endian::get_native_handler() { + return NativeEndian::get_native(); +} diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/endian.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/endian.hpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef LIBJIMAGE_ENDIAN_HPP +#define LIBJIMAGE_ENDIAN_HPP + +#include "inttypes.hpp" + +// Selectable endian handling. Endian handlers are used when accessing values +// that are of unknown (until runtime) endian. The only requirement of the values +// accessed are that they are aligned to proper size boundaries (no misalignment.) +// To select an endian handler, one should call Endian::get_handler(big_endian); +// Where big_endian is true if big endian is required and false otherwise. The +// native endian handler can be fetched with Endian::get_native_handler(); +// To retrieve a value using the approprate endian, use one of the overloaded +// calls to get. To set a value, then use one of the overloaded set calls. +// Ex. +// s4 value; // Imported value; +// ... +// Endian* endian = Endian::get_handler(true); // Use big endian +// s4 corrected = endian->get(value); +// endian->set(value, 1); +// +class Endian { +public: + virtual u2 get(u2 x) = 0; + virtual u4 get(u4 x) = 0; + virtual u8 get(u8 x) = 0; + virtual s2 get(s2 x) = 0; + virtual s4 get(s4 x) = 0; + virtual s8 get(s8 x) = 0; + + virtual void set(u2& x, u2 y) = 0; + virtual void set(u4& x, u4 y) = 0; + virtual void set(u8& x, u8 y) = 0; + virtual void set(s2& x, s2 y) = 0; + virtual void set(s4& x, s4 y) = 0; + virtual void set(s8& x, s8 y) = 0; + + // Quick little endian test. + static bool is_little_endian() { u4 x = 1; return *(u1 *)&x != 0; } + + // Quick big endian test. + static bool is_big_endian() { return !is_little_endian(); } + + // Select an appropriate endian handler. + static Endian* get_handler(bool big_endian); + + // Return the native endian handler. + static Endian* get_native_handler(); + + // get platform u2 from Java Big endian + static u2 get_java(u1* x); + // set platform u2 to Java Big endian + static void set_java(u1* p, u2 x); +}; + +// Normal endian handling. +class NativeEndian : public Endian { +private: + static NativeEndian _native; + +public: + u2 get(u2 x); + u4 get(u4 x); + u8 get(u8 x); + s2 get(s2 x); + s4 get(s4 x); + s8 get(s8 x); + + void set(u2& x, u2 y); + void set(u4& x, u4 y); + void set(u8& x, u8 y); + void set(s2& x, s2 y); + void set(s4& x, s4 y); + void set(s8& x, s8 y); + + static Endian* get_native() { return &_native; } +}; + +// Swapping endian handling. +class SwappingEndian : public Endian { +private: + static SwappingEndian _swapping; + +public: + u2 get(u2 x); + u4 get(u4 x); + u8 get(u8 x); + s2 get(s2 x); + s4 get(s4 x); + s8 get(s8 x); + + void set(u2& x, u2 y); + void set(u4& x, u4 y); + void set(u8& x, u8 y); + void set(s2& x, s2 y); + void set(s4& x, s4 y); + void set(s8& x, s8 y); + + static Endian* get_swapping() { return &_swapping; } +}; +#endif // LIBJIMAGE_ENDIAN_HPP diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/imageDecompressor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/imageDecompressor.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +#include "jni.h" +#include "imageDecompressor.hpp" +#include "endian.hpp" +#ifdef WIN32 +#include +#else +#include +#endif + +typedef jboolean (JNICALL *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 = NULL; +#ifdef WIN32 + HMODULE handle = GetModuleHandle("zip.dll"); + if (handle == NULL) { + return NULL; + } + addr = (void*) GetProcAddress(handle, name); + return addr; +#else + addr = dlopen(JNI_LIB_PREFIX "zip" JNI_LIB_SUFFIX, RTLD_GLOBAL|RTLD_LAZY); + if (addr == NULL) { + return NULL; + } + addr = dlsym(addr, name); + return addr; +#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; +} + +/* + * Decompression entry point. Called from ImageFileReader::get_resource. + */ +void ImageDecompressor::decompress_resource(u1* compressed, u1* uncompressed, + u4 uncompressed_size, const ImageStrings* strings) { + 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; + memcpy(&_header, compressed_resource, sizeof (ResourceHeader)); + has_header = _header._magic == ResourceHeader::resource_header_magic; + if (has_header) { + // decompressed_resource array contains the result of decompression + decompressed_resource = new u1[_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"); + u1* compressed_resource_base = compressed_resource; + compressed_resource += ResourceHeader::resource_header_length; + // 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, 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 i = decompress_int(data); + const char * string = strings->get(i); + 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++; + } + default: + { + *uncompressed_resource = tag; + uncompressed_resource += 1; + int size = sizes[tag]; + memcpy(uncompressed_resource, data, size); + uncompressed_resource += size; + data += size; + } + } + } + u4 remain = header->_size - (int)(data - data_base); + u4 computed = (u4)(uncompressed_resource - uncompressed_base) + remain; + if (header->_uncompressed_size != computed) + printf("Failure, expecting %d but getting %d\n", header->_uncompressed_size, + computed); + assert(header->_uncompressed_size == computed && + "Constant Pool reconstruction failed"); + memcpy(uncompressed_resource, data, 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 diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/imageDecompressor.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/imageDecompressor.hpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef LIBJIMAGE_IMAGEDECOMPRESSOR_HPP +#define LIBJIMAGE_IMAGEDECOMPRESSOR_HPP + +#include +#include + +#include "imageFile.hpp" +#include "inttypes.hpp" +#include "jni.h" + +/* + * Compressed resources located in image have an header. + * This header contains: + * - _magic: A magic u4, required to retrieved the header in the compressed content + * - _size: The size of the compressed resource. + * - _uncompressed_size: The uncompressed size of the compressed resource. + * - _decompressor_name_offset: The ImageDecompressor instance name StringsTable offset. + * - _decompressor_config_offset: StringsTable offset of configuration that could be needed by + * the decompressor in order to decompress. + * - _is_terminal: 1: the compressed content is terminal. Uncompressing it would + * create the actual resource. 0: the compressed content is not terminal. Uncompressing it + * will result in a compressed content to be decompressed (This occurs when a stack of compressors + * have been used to compress the resource. + */ +struct ResourceHeader { + /* Length of header, needed to retrieve content offset */ + static const u1 resource_header_length = 21; + /* magic bytes that identifies a compressed resource header*/ + static const u4 resource_header_magic = 0xCAFEFAFA; + u4 _magic; // Resource header + u4 _size; // Resource size + u4 _uncompressed_size; // Expected uncompressed size + u4 _decompressor_name_offset; // Strings table decompressor offset + u4 _decompressor_config_offset; // Strings table config offset + u1 _is_terminal; // Last decompressor 1, otherwise 0. +}; + +/* + * Resources located in jimage file can be compressed. Compression occurs at + * jimage file creation time. When compressed a resource is added an header that + * contains the name of the compressor that compressed it. + * Various compression strategies can be applied to compress a resource. + * The same resource can even be compressed multiple time by a stack of compressors. + * At runtime, a resource is decompressed in a loop until there is no more header + * meaning that the resource is equivalent to the not compressed resource. + * In each iteration, the name of the compressor located in the current header + * is used to retrieve the associated instance of ImageDecompressor. + * For example “zip” is the name of the compressor that compresses resources + * using the zip algorithm. The ZipDecompressor class name is also “zip”. + * ImageDecompressor instances are retrieved from a static array in which + * they are registered. + */ +class ImageDecompressor { + +private: + const char* _name; + + /* + * Array of concrete decompressors. This array is used to retrieve the decompressor + * that can handle resource decompression. + */ + static ImageDecompressor** _decompressors; + /** + * Num of decompressors + */ + static int _decompressors_num; + /* + * Identifier of a decompressor. This name is the identification key to retrieve + * decompressor from a resource header. + */ + inline const char* get_name() const { return _name; } + + +protected: + ImageDecompressor(const char* name) : _name(name) { + } + virtual void decompress_resource(u1* data, u1* uncompressed, + ResourceHeader* header, const ImageStrings* strings) = 0; + +public: + static void image_decompressor_init(); + static void image_decompressor_close(); + static ImageDecompressor* get_decompressor(const char * decompressor_name) ; + static void decompress_resource(u1* compressed, u1* uncompressed, + u4 uncompressed_size, const ImageStrings* strings); +}; + +/** + * Zip decompressor. + */ +class ZipDecompressor : public ImageDecompressor { +public: + ZipDecompressor(const char* sym) : ImageDecompressor(sym) { } + void decompress_resource(u1* data, u1* uncompressed, ResourceHeader* header, + const ImageStrings* strings); + static jboolean decompress(void *in, u8 inSize, void *out, u8 outSize, char **pmsg); +}; + +/* + * Shared Strings decompressor. This decompressor reconstruct the class + * constant pool UTF_U entries by retrieving strings stored in jimage strings table. + * In addition, if the UTF_8 entry is a descriptor, the descriptor has to be rebuilt, + * all java type having been removed from the descriptor and added to the sting table. + * eg: "(Ljava/lang/String;I)V" ==> "(L;I)V" and "java/lang", "String" + * stored in string table. offsets to the 2 strings are compressed and stored in the + * constantpool entry. + */ +class SharedStringDecompressor : public ImageDecompressor { +private: + // the constant pool tag for UTF8 string located in strings table + static const int externalized_string = 23; + // the constant pool tag for UTF8 descriptors string located in strings table + static const int externalized_string_descriptor = 25; + // the constant pool tag for UTF8 + static const int constant_utf8 = 1; + // the constant pool tag for long + static const int constant_long = 5; + // the constant pool tag for double + static const int constant_double = 6; + // array index is the constant pool tag. value is size. + // eg: array[5] = 8; means size of long is 8 bytes. + static const u1 sizes[]; + // bit 5 and 6 are used to store the length of the compressed integer. + // size can be 1 (01), 2 (10), 3 (11). + // 0x60 ==> 0110000 + static const int compressed_index_size_mask = 0x60; + /* + * mask the length bits (5 and 6) and move to the right 5 bits. + */ + inline static int get_compressed_length(char c) { return ((char) (c & compressed_index_size_mask) >> 5); } + inline static bool is_compressed(signed char b1) { return b1 < 0; } + static int decompress_int(unsigned char*& value); +public: + SharedStringDecompressor(const char* sym) : ImageDecompressor(sym){} + void decompress_resource(u1* data, u1* uncompressed, ResourceHeader* header, + const ImageStrings* strings); +}; +#endif // LIBJIMAGE_IMAGEDECOMPRESSOR_HPP diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/imageFile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/imageFile.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include +#include +#include + +#include "endian.hpp" +#include "imageDecompressor.hpp" +#include "imageFile.hpp" +#include "inttypes.hpp" +#include "jni.h" +#include "osSupport.hpp" + +// Map the full jimage, only with 64 bit addressing. +bool MemoryMapImage = sizeof(void *) == 8; + +#ifdef WIN32 +const char FileSeparator = '\\'; +#else +const char FileSeparator = '/'; +#endif + +// Image files are an alternate file format for storing classes and resources. The +// goal is to supply file access which is faster and smaller than the jar format. +// +// (More detailed nodes in the header.) +// + +// Compute the Perfect Hashing hash code for the supplied UTF-8 string. +s4 ImageStrings::hash_code(const char* string, s4 seed) { + // Access bytes as unsigned. + u1* bytes = (u1*)string; + // Compute hash code. + for (u1 byte = *bytes++; byte; byte = *bytes++) { + seed = (seed * HASH_MULTIPLIER) ^ byte; + } + // Ensure the result is not signed. + return seed & 0x7FFFFFFF; +} + +// Match up a string in a perfect hash table. +// Returns the index where the name should be. +// Result still needs validation for precise match (false positive.) +s4 ImageStrings::find(Endian* endian, const char* name, s4* redirect, u4 length) { + // If the table is empty, then short cut. + if (!redirect || !length) { + return NOT_FOUND; + } + // Compute the basic perfect hash for name. + s4 hash_code = ImageStrings::hash_code(name); + // Modulo table size. + s4 index = hash_code % length; + // Get redirect entry. + // value == 0 then not found + // value < 0 then -1 - value is true index + // value > 0 then value is seed for recomputing hash. + s4 value = endian->get(redirect[index]); + // if recompute is required. + if (value > 0 ) { + // Entry collision value, need to recompute hash. + hash_code = ImageStrings::hash_code(name, value); + // Modulo table size. + return hash_code % length; + } else if (value < 0) { + // Compute direct index. + return -1 - value; + } + // No entry found. + return NOT_FOUND; +} + +// Test to see if UTF-8 string begins with the start UTF-8 string. If so, +// return non-NULL address of remaining portion of string. Otherwise, return +// NULL. Used to test sections of a path without copying from image string +// table. +const char* ImageStrings::starts_with(const char* string, const char* start) { + char ch1, ch2; + // Match up the strings the best we can. + while ((ch1 = *string) && (ch2 = *start)) { + if (ch1 != ch2) { + // Mismatch, return NULL. + return NULL; + } + // Next characters. + string++, start++; + } + // Return remainder of string. + return string; +} + +// Inflates the attribute stream into individual values stored in the long +// array _attributes. This allows an attribute value to be quickly accessed by +// direct indexing. Unspecified values default to zero (from constructor.) +void ImageLocation::set_data(u1* data) { + // Deflate the attribute stream into an array of attributes. + u1 byte; + // Repeat until end header is found. + while ((byte = *data)) { + // Extract kind from header byte. + u1 kind = attribute_kind(byte); + assert(kind < ATTRIBUTE_COUNT && "invalid image location attribute"); + // Extract length of data (in bytes). + u1 n = attribute_length(byte); + // Read value (most significant first.) + _attributes[kind] = attribute_value(data + 1, n); + // Position to next attribute by skipping attribute header and data bytes. + data += n + 1; + } +} + +// Zero all attribute values. +void ImageLocation::clear_data() { + // Set defaults to zero. + memset(_attributes, 0, sizeof(_attributes)); +} + +// ImageModuleData constructor maps out sub-tables for faster access. +ImageModuleData::ImageModuleData(const ImageFileReader* image_file, + const char* module_data_name) : + _image_file(image_file), + _endian(image_file->endian()), + _strings(image_file->get_strings()) { + // Retrieve the resource containing the module data for the image file. + ImageLocation location; + bool found = image_file->find_location(module_data_name, location); + if (found) { + u8 data_size = location.get_attribute(ImageLocation::ATTRIBUTE_UNCOMPRESSED); + _data = new u1[(size_t)data_size]; + _image_file->get_resource(location, _data); + // Map out the header. + _header = (Header*)_data; + // Get the package to module entry count. + u4 ptm_count = _header->ptm_count(_endian); + // Get the module to package entry count. + u4 mtp_count = _header->mtp_count(_endian); + // Compute the offset of the package to module perfect hash redirect. + u4 ptm_redirect_offset = sizeof(Header); + // Compute the offset of the package to module data. + u4 ptm_data_offset = ptm_redirect_offset + ptm_count * sizeof(s4); + // Compute the offset of the module to package perfect hash redirect. + u4 mtp_redirect_offset = ptm_data_offset + ptm_count * sizeof(PTMData); + // Compute the offset of the module to package data. + u4 mtp_data_offset = mtp_redirect_offset + mtp_count * sizeof(s4); + // Compute the offset of the module to package tables. + u4 mtp_packages_offset = mtp_data_offset + mtp_count * sizeof(MTPData); + // Compute the address of the package to module perfect hash redirect. + _ptm_redirect = (s4*)(_data + ptm_redirect_offset); + // Compute the address of the package to module data. + _ptm_data = (PTMData*)(_data + ptm_data_offset); + // Compute the address of the module to package perfect hash redirect. + _mtp_redirect = (s4*)(_data + mtp_redirect_offset); + // Compute the address of the module to package data. + _mtp_data = (MTPData*)(_data + mtp_data_offset); + // Compute the address of the module to package tables. + _mtp_packages = (s4*)(_data + mtp_packages_offset); + } else { + // No module data present. + _data = NULL; + _header = NULL; + _ptm_redirect = NULL; + _ptm_data = NULL; + _mtp_redirect = NULL; + _mtp_data = NULL; + _mtp_packages = NULL; + } +} + +// Release module data resource. +ImageModuleData::~ImageModuleData() { + if (_data) { + delete _data; + } +} + +// Return the name of the module data resource. Ex. "./lib/modules/file.jimage" +// yields "file.jdata" +void ImageModuleData::module_data_name(char* buffer, const char* image_file_name) { + // Locate the last slash in the file name path. + const char* slash = strrchr(image_file_name, FileSeparator); + // Trim the path to name and extension. + const char* name = slash ? slash + 1 : (char *)image_file_name; + // Locate the extension period. + const char* dot = strrchr(name, '.'); + assert(dot && "missing extension on jimage name"); + // Trim to only base name. + int length = (int)(dot - name); + strncpy(buffer, name, length); + buffer[length] = '\0'; + // Append extension. + strcat(buffer, ".jdata"); +} + +// Return the module in which a package resides. Returns NULL if not found. +const char* ImageModuleData::package_to_module(const char* package_name) { + // Test files may contain no module data. + if (_data != NULL) { + // Search the package to module table. + s4 index = ImageStrings::find(_endian, package_name, _ptm_redirect, + _header->ptm_count(_endian)); + // If entry is found. + if (index != ImageStrings::NOT_FOUND) { + // Retrieve the package to module entry. + PTMData* data = _ptm_data + index; + // Verify that it is the correct data. + if (strcmp(package_name, get_string(data->name_offset(_endian))) != 0) { + return NULL; + } + // Return the module name. + return get_string(data->module_name_offset(_endian)); + } + } + return NULL; +} + +// Returns all the package names in a module in a NULL terminated array. +// Returns NULL if module not found. +const char** ImageModuleData::module_to_packages(const char* module_name) { + // Test files may contain no module data. + if (_data != NULL) { + // Search the module to package table. + s4 index = ImageStrings::find(_endian, module_name, _mtp_redirect, + _header->mtp_count(_endian)); + // If entry is found. + if (index != ImageStrings::NOT_FOUND) { + // Retrieve the module to package entry. + MTPData* data = _mtp_data + index; + // Verify that it is the correct data. + if (strcmp(module_name, get_string(data->name_offset(_endian))) != 0) { + return NULL; + } + // Construct an array of all the package entries. + u4 count = data->package_count(_endian); + const char** packages = new const char*[count + 1]; + s4 package_offset = data->package_offset(_endian); + for (u4 i = 0; i < count; i++) { + u4 package_name_offset = mtp_package(package_offset + i); + const char* package_name = get_string(package_name_offset); + packages[i] = package_name; + } + packages[count] = NULL; + return packages; + } + } + return NULL; +} + +// Manage a table of open image files. This table allows multiple access points +// to share an open image. +ImageFileReaderTable::ImageFileReaderTable() : _count(0), _max(_growth) { + _table = new ImageFileReader*[_max]; +} + +ImageFileReaderTable::~ImageFileReaderTable() { + delete _table; +} + +// Add a new image entry to the table. +void ImageFileReaderTable::add(ImageFileReader* image) { + if (_count == _max) { + _max += _growth; + _table = static_cast(realloc(_table, _max * sizeof(ImageFileReader*))); + } + _table[_count++] = image; +} + +// Remove an image entry from the table. +void ImageFileReaderTable::remove(ImageFileReader* image) { + s4 last = _count - 1; + for (s4 i = 0; _count; i++) { + if (_table[i] == image) { + if (i != last) { + _table[i] = _table[last]; + _count = last; + } + break; + } + } + + if (_count != 0 && _count == _max - _growth) { + _max -= _growth; + _table = static_cast(realloc(_table, _max * sizeof(ImageFileReader*))); + } +} + +// Determine if image entry is in table. +bool ImageFileReaderTable::contains(ImageFileReader* image) { + for (s4 i = 0; _count; i++) { + if (_table[i] == image) { + return true; + } + } + return false; +} + +// Table to manage multiple opens of an image file. +ImageFileReaderTable ImageFileReader::_reader_table; + +SimpleCriticalSection _reader_table_lock; + +// Open an image file, reuse structure if file already open. +ImageFileReader* ImageFileReader::open(const char* name, bool big_endian) { + { + // Lock out _reader_table. + SimpleCriticalSectionLock cs(&_reader_table_lock); + // Search for an exist image file. + for (u4 i = 0; i < _reader_table.count(); i++) { + // Retrieve table entry. + ImageFileReader* reader = _reader_table.get(i); + // If name matches, then reuse (bump up use count.) + if (strcmp(reader->name(), name) == 0) { + reader->inc_use(); + return reader; + } + } + } // Unlock the mutex + + // Need a new image reader. + ImageFileReader* reader = new ImageFileReader(name, big_endian); + bool opened = reader->open(); + // If failed to open. + if (!opened) { + delete reader; + return NULL; + } + + // Lock to update + SimpleCriticalSectionLock cs(&_reader_table_lock); + // Search for an exist image file. + for (u4 i = 0; i < _reader_table.count(); i++) { + // Retrieve table entry. + ImageFileReader* existing_reader = _reader_table.get(i); + // If name matches, then reuse (bump up use count.) + if (strcmp(existing_reader->name(), name) == 0) { + existing_reader->inc_use(); + reader->close(); + delete reader; + return existing_reader; + } + } + // Bump use count and add to table. + reader->inc_use(); + _reader_table.add(reader); + return reader; +} + +// Close an image file if the file is not in use elsewhere. +void ImageFileReader::close(ImageFileReader *reader) { + // Lock out _reader_table. + SimpleCriticalSectionLock cs(&_reader_table_lock); + // If last use then remove from table and then close. + if (reader->dec_use()) { + _reader_table.remove(reader); + delete reader; + } +} + +// Return an id for the specifed ImageFileReader. +u8 ImageFileReader::readerToID(ImageFileReader *reader) { + // ID is just the cloaked reader address. + return (u8)reader; +} + +// Validate the image id. +bool ImageFileReader::idCheck(u8 id) { + // Make sure the ID is a managed (_reader_table) reader. + SimpleCriticalSectionLock cs(&_reader_table_lock); + return _reader_table.contains((ImageFileReader*)id); +} + +// Return an id for the specifed ImageFileReader. +ImageFileReader* ImageFileReader::idToReader(u8 id) { + assert(idCheck(id) && "invalid image id"); + return (ImageFileReader*)id; +} + +// Constructor intializes to a closed state. +ImageFileReader::ImageFileReader(const char* name, bool big_endian) { + // Copy the image file name. + int len = (int) strlen(name) + 1; + _name = new char[len]; + strncpy(_name, name, len); + // Initialize for a closed file. + _fd = -1; + _endian = Endian::get_handler(big_endian); + _index_data = NULL; +} + +// Close image and free up data structures. +ImageFileReader::~ImageFileReader() { + // Ensure file is closed. + close(); + // Free up name. + if (_name) { + delete _name; + _name = NULL; + } +} + +// Open image file for read access. +bool ImageFileReader::open() { + char buffer[IMAGE_MAX_PATH]; + + // If file exists open for reading. + _fd = osSupport::openReadOnly(_name); + if (_fd == -1) { + return false; + } + // Retrieve the file size. + _file_size = osSupport::size(_name); + // Read image file header and verify it has a valid header. + size_t header_size = sizeof(ImageHeader); + if (_file_size < header_size || + !read_at((u1*)&_header, header_size, 0) || + _header.magic(_endian) != IMAGE_MAGIC || + _header.major_version(_endian) != MAJOR_VERSION || + _header.minor_version(_endian) != MINOR_VERSION) { + close(); + return false; + } + // Size of image index. + _index_size = index_size(); + // Make sure file is large enough to contain the index. + if (_file_size < _index_size) { + return false; + } + // Determine how much of the image is memory mapped. + size_t map_size = (size_t)(MemoryMapImage ? _file_size : _index_size); + // Memory map image (minimally the index.) + _index_data = (u1*)osSupport::map_memory(_fd, _name, 0, map_size); + assert(_index_data && "image file not memory mapped"); + // Retrieve length of index perfect hash table. + u4 length = table_length(); + // Compute offset of the perfect hash table redirect table. + u4 redirect_table_offset = (u4)header_size; + // Compute offset of index attribute offsets. + u4 offsets_table_offset = redirect_table_offset + length * sizeof(s4); + // Compute offset of index location attribute data. + u4 location_bytes_offset = offsets_table_offset + length * sizeof(u4); + // Compute offset of index string table. + u4 string_bytes_offset = location_bytes_offset + locations_size(); + // Compute address of the perfect hash table redirect table. + _redirect_table = (s4*)(_index_data + redirect_table_offset); + // Compute address of index attribute offsets. + _offsets_table = (u4*)(_index_data + offsets_table_offset); + // Compute address of index location attribute data. + _location_bytes = _index_data + location_bytes_offset; + // Compute address of index string table. + _string_bytes = _index_data + string_bytes_offset; + + // Initialize the module data + ImageModuleData::module_data_name(buffer, _name); + module_data = new ImageModuleData(this, buffer); + // Successful open. + return true; +} + +// Close image file. +void ImageFileReader::close() { + // Deallocate the index. + if (_index_data) { + osSupport::unmap_memory((char*)_index_data, _index_size); + _index_data = NULL; + } + // Close file. + if (_fd != -1) { + osSupport::close(_fd); + _fd = -1; + } +} + +// Read directly from the file. +bool ImageFileReader::read_at(u1* data, u8 size, u8 offset) const { + return (u8)osSupport::read(_fd, (char*)data, size, offset) == size; +} + +// Find the location attributes associated with the path. Returns true if +// the location is found, false otherwise. +bool ImageFileReader::find_location(const char* path, ImageLocation& location) const { + // Locate the entry in the index perfect hash table. + s4 index = ImageStrings::find(_endian, path, _redirect_table, table_length()); + // If is found. + if (index != ImageStrings::NOT_FOUND) { + // Get address of first byte of location attribute stream. + u1* data = get_location_data(index); + // Expand location attributes. + location.set_data(data); + // Make sure result is not a false positive. + return verify_location(location, path); + } + return false; +} + +// Find the location index and size associated with the path. +// Returns the location index and size if the location is found, 0 otherwise. +u4 ImageFileReader::find_location_index(const char* path, u8 *size) const { + // Locate the entry in the index perfect hash table. + s4 index = ImageStrings::find(_endian, path, _redirect_table, table_length()); + // If found. + if (index != ImageStrings::NOT_FOUND) { + // Get address of first byte of location attribute stream. + u4 offset = get_location_offset(index); + u1* data = get_location_offset_data(offset); + // Expand location attributes. + ImageLocation location(data); + // Make sure result is not a false positive. + if (verify_location(location, path)) { + *size = (jlong)location.get_attribute(ImageLocation::ATTRIBUTE_UNCOMPRESSED); + return offset; + } + } + return 0; // not found +} + +// Assemble the location path from the string fragments indicated in the location attributes. +void ImageFileReader::location_path(ImageLocation& location, char* path, size_t max) const { + // Manage the image string table. + ImageStrings strings(_string_bytes, _header.strings_size(_endian)); + // Position to first character of the path buffer. + char* next = path; + // Temp for string length. + size_t length; + // Get module string. + const char* module = location.get_attribute(ImageLocation::ATTRIBUTE_MODULE, strings); + // If module string is not empty string. + if (*module != '\0') { + // Get length of module name. + length = strlen(module); + // Make sure there is no buffer overflow. + assert(next - path + length + 2 < max && "buffer overflow"); + // Append '/module/'. + *next++ = '/'; + strncpy(next, module, length); next += length; + *next++ = '/'; + } + // Get parent (package) string. + const char* parent = location.get_attribute(ImageLocation::ATTRIBUTE_PARENT, strings); + // If parent string is not empty string. + if (*parent != '\0') { + // Get length of module string. + length = strlen(parent); + // Make sure there is no buffer overflow. + assert(next - path + length + 1 < max && "buffer overflow"); + // Append 'patent/' . + strncpy(next, parent, length); next += length; + *next++ = '/'; + } + // Get base name string. + const char* base = location.get_attribute(ImageLocation::ATTRIBUTE_BASE, strings); + // Get length of base name. + length = strlen(base); + // Make sure there is no buffer overflow. + assert(next - path + length < max && "buffer overflow"); + // Append base name. + strncpy(next, base, length); next += length; + // Get extension string. + const char* extension = location.get_attribute(ImageLocation::ATTRIBUTE_EXTENSION, strings); + // If extension string is not empty string. + if (*extension != '\0') { + // Get length of extension string. + length = strlen(extension); + // Make sure there is no buffer overflow. + assert(next - path + length + 1 < max && "buffer overflow"); + // Append '.extension' . + *next++ = '.'; + strncpy(next, extension, length); next += length; + } + // Make sure there is no buffer overflow. + assert((size_t)(next - path) < max && "buffer overflow"); + // Terminate string. + *next = '\0'; +} + +// Verify that a found location matches the supplied path (without copying.) +bool ImageFileReader::verify_location(ImageLocation& location, const char* path) const { + // Manage the image string table. + ImageStrings strings(_string_bytes, _header.strings_size(_endian)); + // Position to first character of the path string. + const char* next = path; + // Get module name string. + const char* module = location.get_attribute(ImageLocation::ATTRIBUTE_MODULE, strings); + // If module string is not empty. + if (*module != '\0') { + // Compare '/module/' . + if (*next++ != '/') return false; + if (!(next = ImageStrings::starts_with(next, module))) return false; + if (*next++ != '/') return false; + } + // Get parent (package) string + const char* parent = location.get_attribute(ImageLocation::ATTRIBUTE_PARENT, strings); + // If parent string is not empty string. + if (*parent != '\0') { + // Compare 'parent/' . + if (!(next = ImageStrings::starts_with(next, parent))) return false; + if (*next++ != '/') return false; + } + // Get base name string. + const char* base = location.get_attribute(ImageLocation::ATTRIBUTE_BASE, strings); + // Compare with basne name. + if (!(next = ImageStrings::starts_with(next, base))) return false; + // Get extension string. + const char* extension = location.get_attribute(ImageLocation::ATTRIBUTE_EXTENSION, strings); + // If extension is not empty. + if (*extension != '\0') { + // Compare '.extension' . + if (*next++ != '.') return false; + if (!(next = ImageStrings::starts_with(next, extension))) return false; + } + // True only if complete match and no more characters. + return *next == '\0'; +} + +// Return the resource for the supplied location offset. +void ImageFileReader::get_resource(u4 offset, u1* uncompressed_data) const { + // Get address of first byte of location attribute stream. + u1* data = get_location_offset_data(offset); + // Expand location attributes. + ImageLocation location(data); + // Read the data + get_resource(location, uncompressed_data); +} + +// Return the resource for the supplied location. +void ImageFileReader::get_resource(ImageLocation& location, u1* uncompressed_data) const { + // Retrieve the byte offset and size of the resource. + u8 offset = location.get_attribute(ImageLocation::ATTRIBUTE_OFFSET); + u8 uncompressed_size = location.get_attribute(ImageLocation::ATTRIBUTE_UNCOMPRESSED); + u8 compressed_size = location.get_attribute(ImageLocation::ATTRIBUTE_COMPRESSED); + // If the resource is compressed. + if (compressed_size != 0) { + u1* compressed_data; + // If not memory mapped read in bytes. + if (!MemoryMapImage) { + // Allocate buffer for compression. + compressed_data = new u1[(u4)compressed_size]; + // Read bytes from offset beyond the image index. + bool is_read = read_at(compressed_data, compressed_size, _index_size + offset); + assert(is_read && "error reading from image or short read"); + } else { + compressed_data = get_data_address() + offset; + } + // Get image string table. + const ImageStrings strings = get_strings(); + // Decompress resource. + ImageDecompressor::decompress_resource(compressed_data, uncompressed_data, (u4)uncompressed_size, + &strings); + // If not memory mapped then release temporary buffer. + if (!MemoryMapImage) { + delete compressed_data; + } + } else { + // Read bytes from offset beyond the image index. + bool is_read = read_at(uncompressed_data, uncompressed_size, _index_size + offset); + assert(is_read && "error reading from image or short read"); + } +} + +// Return the ImageModuleData for this image +ImageModuleData * ImageFileReader::get_image_module_data() { + return module_data; +} diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/imageFile.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/imageFile.hpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef LIBJIMAGE_IMAGEFILE_HPP +#define LIBJIMAGE_IMAGEFILE_HPP + +#include + +#include "endian.hpp" +#include "inttypes.hpp" + +// Image files are an alternate file format for storing classes and resources. The +// goal is to supply file access which is faster and smaller than the jar format. +// It should be noted that unlike jars, information stored in an image is in native +// endian format. This allows the image to be mapped into memory without endian +// translation. This also means that images are platform dependent. +// +// Image files are structured as three sections; +// +// +-----------+ +// | Header | +// +-----------+ +// | | +// | Index | +// | | +// +-----------+ +// | | +// | | +// | Resources | +// | | +// | | +// +-----------+ +// +// The header contains information related to identification and description of +// contents. +// +// +-------------------------+ +// | Magic (0xCAFEDADA) | +// +------------+------------+ +// | Major Vers | Minor Vers | +// +------------+------------+ +// | Flags | +// +-------------------------+ +// | Resource Count | +// +-------------------------+ +// | Table Length | +// +-------------------------+ +// | Attributes Size | +// +-------------------------+ +// | Strings Size | +// +-------------------------+ +// +// Magic - means of identifying validity of the file. This avoids requiring a +// special file extension. +// Major vers, minor vers - differences in version numbers indicate structural +// changes in the image. +// Flags - various image wide flags (future). +// Resource count - number of resources in the file. +// Table length - the length of lookup tables used in the index. +// Attributes size - number of bytes in the region used to store location attribute +// streams. +// Strings size - the size of the region used to store strings used by the +// index and meta data. +// +// The index contains information related to resource lookup. The algorithm +// used for lookup is "A Practical Minimal Perfect Hashing Method" +// (http://homepages.dcc.ufmg.br/~nivio/papers/wea05.pdf). Given a path string +// in the form ///. return the resource location +// information; +// +// redirectIndex = hash(path, DEFAULT_SEED) % table_length; +// redirect = redirectTable[redirectIndex]; +// if (redirect == 0) return not found; +// locationIndex = redirect < 0 ? -1 - redirect : hash(path, redirect) % table_length; +// location = locationTable[locationIndex]; +// if (!verify(location, path)) return not found; +// return location; +// +// Note: The hash function takes an initial seed value. A different seed value +// usually returns a different result for strings that would otherwise collide with +// other seeds. The verify function guarantees the found resource location is +// indeed the resource we are looking for. +// +// The following is the format of the index; +// +// +-------------------+ +// | Redirect Table | +// +-------------------+ +// | Attribute Offsets | +// +-------------------+ +// | Attribute Data | +// +-------------------+ +// | Strings | +// +-------------------+ +// +// Redirect Table - Array of 32-bit signed values representing actions that +// should take place for hashed strings that map to that +// value. Negative values indicate no hash collision and can be +// quickly converted to indices into attribute offsets. Positive +// values represent a new seed for hashing an index into attribute +// offsets. Zero indicates not found. +// Attribute Offsets - Array of 32-bit unsigned values representing offsets into +// attribute data. Attribute offsets can be iterated to do a +// full survey of resources in the image. Offset of zero +// indicates no attributes. +// Attribute Data - Bytes representing compact attribute data for locations. (See +// comments in ImageLocation.) +// Strings - Collection of zero terminated UTF-8 strings used by the index and +// image meta data. Each string is accessed by offset. Each string is +// unique. Offset zero is reserved for the empty string. +// +// Note that the memory mapped index assumes 32 bit alignment of each component +// in the index. +// +// Endianness of an image. +// An image booted by hotspot is always in native endian. However, it is possible +// to read (by the JDK) in alternate endian format. Primarily, this is during +// cross platform scenarios. Ex, where javac needs to read an embedded image +// to access classes for crossing compilation. +// + +class ImageFileReader; // forward declaration + +// Manage image file string table. +class ImageStrings { +private: + u1* _data; // Data bytes for strings. + u4 _size; // Number of bytes in the string table. +public: + enum { + // Not found result from find routine. + NOT_FOUND = -1, + // Prime used to generate hash for Perfect Hashing. + HASH_MULTIPLIER = 0x01000193 + }; + + ImageStrings(u1* data, u4 size) : _data(data), _size(size) {} + + // Return the UTF-8 string beginning at offset. + inline const char* get(u4 offset) const { + assert(offset < _size && "offset exceeds string table size"); + return (const char*)(_data + offset); + } + + // Compute the Perfect Hashing hash code for the supplied UTF-8 string. + inline static u4 hash_code(const char* string) { + return hash_code(string, HASH_MULTIPLIER); + } + + // Compute the Perfect Hashing hash code for the supplied string, starting at seed. + static s4 hash_code(const char* string, s4 seed); + + // Match up a string in a perfect hash table. Result still needs validation + // for precise match. + static s4 find(Endian* endian, const char* name, s4* redirect, u4 length); + + // Test to see if UTF-8 string begins with the start UTF-8 string. If so, + // return non-NULL address of remaining portion of string. Otherwise, return + // NULL. Used to test sections of a path without copying from image string + // table. + static const char* starts_with(const char* string, const char* start); + + // Test to see if UTF-8 string begins with start char. If so, return non-NULL + // address of remaining portion of string. Otherwise, return NULL. Used + // to test a character of a path without copying. + inline static const char* starts_with(const char* string, const char ch) { + return *string == ch ? string + 1 : NULL; + } +}; + +// Manage image file location attribute data. Within an image, a location's +// attributes are compressed into a stream of bytes. An attribute stream is +// composed of individual attribute sequences. Each attribute sequence begins with +// a header byte containing the attribute 'kind' (upper 5 bits of header) and the +// 'length' less 1 (lower 3 bits of header) of bytes that follow containing the +// attribute value. Attribute values present as most significant byte first. +// +// Ex. Container offset (ATTRIBUTE_OFFSET) 0x33562 would be represented as 0x22 +// (kind = 4, length = 3), 0x03, 0x35, 0x62. +// +// An attribute stream is terminated with a header kind of ATTRIBUTE_END (header +// byte of zero.) +// +// ImageLocation inflates the stream into individual values stored in the long +// array _attributes. This allows an attribute value can be quickly accessed by +// direct indexing. Unspecified values default to zero. +// +// Notes: +// - Even though ATTRIBUTE_END is used to mark the end of the attribute stream, +// streams will contain zero byte values to represent lesser significant bits. +// Thus, detecting a zero byte is not sufficient to detect the end of an attribute +// stream. +// - ATTRIBUTE_OFFSET represents the number of bytes from the beginning of the region +// storing the resources. Thus, in an image this represents the number of bytes +// after the index. +// - Currently, compressed resources are represented by having a non-zero +// ATTRIBUTE_COMPRESSED value. This represents the number of bytes stored in the +// image, and the value of ATTRIBUTE_UNCOMPRESSED represents number of bytes of the +// inflated resource in memory. If the ATTRIBUTE_COMPRESSED is zero then the value +// of ATTRIBUTE_UNCOMPRESSED represents both the number of bytes in the image and +// in memory. In the future, additional compression techniques will be used and +// represented differently. +// - Package strings include trailing slash and extensions include prefix period. +// +class ImageLocation { +public: + enum { + ATTRIBUTE_END, // End of attribute stream marker + ATTRIBUTE_MODULE, // String table offset of module name + ATTRIBUTE_PARENT, // String table offset of resource path parent + ATTRIBUTE_BASE, // String table offset of resource path base + ATTRIBUTE_EXTENSION, // String table offset of resource path extension + ATTRIBUTE_OFFSET, // Container byte offset of resource + ATTRIBUTE_COMPRESSED, // In image byte size of the compressed resource + ATTRIBUTE_UNCOMPRESSED, // In memory byte size of the uncompressed resource + ATTRIBUTE_COUNT // Number of attribute kinds + }; + +private: + // Values of inflated attributes. + u8 _attributes[ATTRIBUTE_COUNT]; + + // Return the attribute value number of bytes. + inline static u1 attribute_length(u1 data) { + return (data & 0x7) + 1; + } + + // Return the attribute kind. + inline static u1 attribute_kind(u1 data) { + u1 kind = data >> 3; + assert(kind < ATTRIBUTE_COUNT && "invalid attribute kind"); + return kind; + } + + // Return the attribute length. + inline static u8 attribute_value(u1* data, u1 n) { + assert(0 < n && n <= 8 && "invalid attribute value length"); + u8 value = 0; + // Most significant bytes first. + for (u1 i = 0; i < n; i++) { + value <<= 8; + value |= data[i]; + } + return value; + } + +public: + ImageLocation() { + clear_data(); + } + + ImageLocation(u1* data) { + clear_data(); + set_data(data); + } + + // Inflates the attribute stream into individual values stored in the long + // array _attributes. This allows an attribute value to be quickly accessed by + // direct indexing. Unspecified values default to zero. + void set_data(u1* data); + + // Zero all attribute values. + void clear_data(); + + // Retrieve an attribute value from the inflated array. + inline u8 get_attribute(u1 kind) const { + assert(ATTRIBUTE_END < kind && kind < ATTRIBUTE_COUNT && "invalid attribute kind"); + return _attributes[kind]; + } + + // Retrieve an attribute string value from the inflated array. + inline const char* get_attribute(u4 kind, const ImageStrings& strings) const { + return strings.get((u4)get_attribute(kind)); + } +}; + +// +// NOTE: needs revision. +// Each loader requires set of module meta data to identify which modules and +// packages are managed by that loader. Currently, there is one image file per +// builtin loader, so only one module meta data resource per file. +// +// Each element in the module meta data is a native endian 4 byte integer. Note +// that entries with zero offsets for string table entries should be ignored ( +// padding for hash table lookup.) +// +// Format: +// Count of package to module entries +// Count of module to package entries +// Perfect Hash redirect table[Count of package to module entries] +// Package to module entries[Count of package to module entries] +// Offset to package name in string table +// Offset to module name in string table +// Perfect Hash redirect table[Count of module to package entries] +// Module to package entries[Count of module to package entries] +// Offset to module name in string table +// Count of packages in module +// Offset to first package in packages table +// Packages[] +// Offset to package name in string table +// +// Manage the image module meta data. +class ImageModuleData { + class Header { + private: + u4 _ptm_count; // Count of package to module entries + u4 _mtp_count; // Count of module to package entries + public: + inline u4 ptm_count(Endian* endian) const { return endian->get(_ptm_count); } + inline u4 mtp_count(Endian* endian) const { return endian->get(_mtp_count); } + }; + + // Hashtable entry + class HashData { + private: + u4 _name_offset; // Name offset in string table + public: + inline s4 name_offset(Endian* endian) const { return endian->get(_name_offset); } + }; + + // Package to module hashtable entry + class PTMData : public HashData { + private: + u4 _module_name_offset; // Module name offset in string table + public: + inline s4 module_name_offset(Endian* endian) const { return endian->get(_module_name_offset); } + }; + + // Module to package hashtable entry + class MTPData : public HashData { + private: + u4 _package_count; // Number of packages in module + u4 _package_offset; // Offset in package list + public: + inline u4 package_count(Endian* endian) const { return endian->get(_package_count); } + inline u4 package_offset(Endian* endian) const { return endian->get(_package_offset); } + }; + + const ImageFileReader* _image_file; // Source image file + Endian* _endian; // Endian handler + ImageStrings _strings; // Image file strings + u1* _data; // Module data resource data + u8 _data_size; // Size of resource data + Header* _header; // Module data header + s4* _ptm_redirect; // Package to module hashtable redirect + PTMData* _ptm_data; // Package to module data + s4* _mtp_redirect; // Module to packages hashtable redirect + MTPData* _mtp_data; // Module to packages data + s4* _mtp_packages; // Package data (name offsets) + + // Return a string from the string table. + inline const char* get_string(u4 offset) { + return _strings.get(offset); + } + + inline u4 mtp_package(u4 index) { + return _endian->get(_mtp_packages[index]); + } + +public: + ImageModuleData(const ImageFileReader* image_file, const char* module_data_name); + ~ImageModuleData(); + + // Return the name of the module data resource. + static void module_data_name(char* buffer, const char* image_file_name); + + // Return the module in which a package resides. Returns NULL if not found. + const char* package_to_module(const char* package_name); + + // Returns all the package names in a module in a NULL terminated array. + // Returns NULL if module not found. + const char** module_to_packages(const char* module_name); +}; + +// Image file header, starting at offset 0. +class ImageHeader { +private: + u4 _magic; // Image file marker + u4 _version; // Image file major version number + u4 _flags; // Image file flags + u4 _resource_count; // Number of resources in file + u4 _table_length; // Number of slots in index tables + u4 _locations_size; // Number of bytes in attribute table + u4 _strings_size; // Number of bytes in string table + +public: + u4 magic() const { return _magic; } + u4 magic(Endian* endian) const { return endian->get(_magic); } + void set_magic(Endian* endian, u4 magic) { return endian->set(_magic, magic); } + + u4 major_version(Endian* endian) const { return endian->get(_version) >> 16; } + u4 minor_version(Endian* endian) const { return endian->get(_version) & 0xFFFF; } + void set_version(Endian* endian, u4 major_version, u4 minor_version) { + return endian->set(_version, major_version << 16 | minor_version); + } + + u4 flags(Endian* endian) const { return endian->get(_flags); } + void set_flags(Endian* endian, u4 value) { return endian->set(_flags, value); } + + u4 resource_count(Endian* endian) const { return endian->get(_resource_count); } + void set_resource_count(Endian* endian, u4 count) { return endian->set(_resource_count, count); } + + u4 table_length(Endian* endian) const { return endian->get(_table_length); } + void set_table_length(Endian* endian, u4 count) { return endian->set(_table_length, count); } + + u4 locations_size(Endian* endian) const { return endian->get(_locations_size); } + void set_locations_size(Endian* endian, u4 size) { return endian->set(_locations_size, size); } + + u4 strings_size(Endian* endian) const { return endian->get(_strings_size); } + void set_strings_size(Endian* endian, u4 size) { return endian->set(_strings_size, size); } +}; + +// Max path length limit independent of platform. Windows max path is 1024, +// other platforms use 4096. The JCK fails several tests when 1024 is used. +#define IMAGE_MAX_PATH 4096 + +class ImageFileReader; + +// Manage a table of open image files. This table allows multiple access points +// to share an open image. +class ImageFileReaderTable { +private: + const static u4 _growth = 8; // Growth rate of the table + u4 _count; // Number of entries in the table + u4 _max; // Maximum number of entries allocated + ImageFileReader** _table; // Growable array of entries + +public: + ImageFileReaderTable(); + ~ImageFileReaderTable(); + + // Return the number of entries. + inline u4 count() { return _count; } + + // Return the ith entry from the table. + inline ImageFileReader* get(u4 i) { return _table[i]; } + + // Add a new image entry to the table. + void add(ImageFileReader* image); + + // Remove an image entry from the table. + void remove(ImageFileReader* image); + + // Determine if image entry is in table. + bool contains(ImageFileReader* image); +}; + +// Manage the image file. +// ImageFileReader manages the content of an image file. +// Initially, the header of the image file is read for validation. If valid, +// values in the header are used calculate the size of the image index. The +// index is then memory mapped to allow load on demand and sharing. The +// -XX:+MemoryMapImage flag determines if the entire file is loaded (server use.) +// An image can be used by Hotspot and multiple reference points in the JDK, thus +// it is desirable to share a reader. To accomodate sharing, a share table is +// defined (see ImageFileReaderTable in imageFile.cpp) To track the number of +// uses, ImageFileReader keeps a use count (_use). Use is incremented when +// 'opened' by reference point and decremented when 'closed'. Use of zero +// leads the ImageFileReader to be actually closed and discarded. +class ImageFileReader { +private: + // Manage a number of image files such that an image can be shared across + // multiple uses (ex. loader.) + static ImageFileReaderTable _reader_table; + + char* _name; // Name of image + s4 _use; // Use count + int _fd; // File descriptor + Endian* _endian; // Endian handler + u8 _file_size; // File size in bytes + ImageHeader _header; // Image header + size_t _index_size; // Total size of index + u1* _index_data; // Raw index data + s4* _redirect_table; // Perfect hash redirect table + u4* _offsets_table; // Location offset table + u1* _location_bytes; // Location attributes + u1* _string_bytes; // String table + ImageModuleData *module_data; // The ImageModuleData for this image + + ImageFileReader(const char* name, bool big_endian); + ~ImageFileReader(); + + // Compute number of bytes in image file index. + inline size_t index_size() { + return sizeof(ImageHeader) + + table_length() * sizeof(u4) * 2 + locations_size() + strings_size(); + } + +public: + enum { + // Image file marker. + IMAGE_MAGIC = 0xCAFEDADA, + // Endian inverted Image file marker. + IMAGE_MAGIC_INVERT = 0xDADAFECA, + // Image file major version number. + MAJOR_VERSION = 1, + // Image file minor version number. + MINOR_VERSION = 0 + }; + + // Open an image file, reuse structure if file already open. + static ImageFileReader* open(const char* name, bool big_endian = Endian::is_big_endian()); + + // Close an image file if the file is not in use elsewhere. + static void close(ImageFileReader *reader); + + // Return an id for the specifed ImageFileReader. + static u8 readerToID(ImageFileReader *reader); + + // Validate the image id. + static bool idCheck(u8 id); + + // Return an id for the specifed ImageFileReader. + static ImageFileReader* idToReader(u8 id); + + // Open image file for read access. + bool open(); + + // Close image file. + void close(); + + // Read directly from the file. + bool read_at(u1* data, u8 size, u8 offset) const; + + inline Endian* endian() const { return _endian; } + + // Retrieve name of image file. + inline const char* name() const { + return _name; + } + + // Retrieve size of image file. + inline u8 file_size() const { + return _file_size; + } + + // Return first address of index data. + inline u1* get_index_address() const { + return _index_data; + } + + // Return first address of resource data. + inline u1* get_data_address() const { + return _index_data + _index_size; + } + + // Get the size of the index data. + size_t get_index_size() const { + return _index_size; + } + + inline u4 table_length() const { + return _header.table_length(_endian); + } + + inline u4 locations_size() const { + return _header.locations_size(_endian); + } + + inline u4 strings_size()const { + return _header.strings_size(_endian); + } + + inline u4* offsets_table() const { + return _offsets_table; + } + + // Increment use count. + inline void inc_use() { + _use++; + } + + // Decrement use count. + inline bool dec_use() { + return --_use == 0; + } + + // Return a string table accessor. + inline const ImageStrings get_strings() const { + return ImageStrings(_string_bytes, _header.strings_size(_endian)); + } + + // Return location attribute stream at offset. + inline u1* get_location_offset_data(u4 offset) const { + assert((u4)offset < _header.locations_size(_endian) && + "offset exceeds location attributes size"); + return offset != 0 ? _location_bytes + offset : NULL; + } + + // Return location attribute stream for location i. + inline u1* get_location_data(u4 index) const { + return get_location_offset_data(get_location_offset(index)); + } + + // Return the location offset for index. + inline u4 get_location_offset(u4 index) const { + assert((u4)index < _header.table_length(_endian) && + "index exceeds location count"); + return _endian->get(_offsets_table[index]); + } + + // Find the location attributes associated with the path. Returns true if + // the location is found, false otherwise. + bool find_location(const char* path, ImageLocation& location) const; + + // Find the location index and size associated with the path. + // Returns the location index and size if the location is found, + // ImageFileReader::NOT_FOUND otherwise. + u4 find_location_index(const char* path, u8 *size) const; + + // Assemble the location path. + void location_path(ImageLocation& location, char* path, size_t max) const; + + // Verify that a found location matches the supplied path. + bool verify_location(ImageLocation& location, const char* path) const; + + // Return the resource for the supplied location index. + void get_resource(u4 index, u1* uncompressed_data) const; + + // Return the resource for the supplied path. + void get_resource(ImageLocation& location, u1* uncompressed_data) const; + + // Return the ImageModuleData for this image + ImageModuleData * get_image_module_data(); + +}; +#endif // LIBJIMAGE_IMAGEFILE_HPP diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/inttypes.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/inttypes.hpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef LIBJIMAGE_INTTYPES_HPP +#define LIBJIMAGE_INTTYPES_HPP + +typedef unsigned char u1; +typedef char s1; +typedef unsigned short u2; +typedef short s2; +typedef unsigned int u4; +typedef int s4; +#ifdef LP64 +typedef unsigned long u8; +typedef long s8; +#else +typedef unsigned long long u8; +typedef long long s8; +#endif + +#endif // LIBJIMAGE_INTTYPES_HPP + diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/jimage.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/jimage.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include + +#include "jimage.hpp" + +#include "imageFile.hpp" + +#define BOOT_VERSION "9.0" + +/* + * JImageOpen - Given the supplied full path file name, open an image file. This + * function will also initialize tables and retrieve meta-data necessary to + * satisfy other functions in the API. If the image file has been previously + * open, a new open request will share memory and resources used by the previous + * open. A call to JImageOpen should be balanced by a call to JImageClose, to + * release memory and resources used. If the image file is not found or cannot + * be open, then NULL is returned and error will contain a reason for the + * failure; a positive value for a system error number, negative for a jimage + * specific error (see JImage Error Codes.) + * + * Ex. + * jint error; + * JImageFile* jimage = (*JImageOpen)(JAVA_HOME "lib/modules/bootmodules.jimage", &error); + * if (image == NULL) { + * tty->print_cr("JImage failed to open: %d", error); + * ... + * } + * ... + */ +extern "C" JImageFile* JIMAGE_Open(const char *name, jint* error) { + // TODO - return a meaningful error code + *error = 0; + ImageFileReader* jfile = ImageFileReader::open(name); + return (JImageFile*) jfile; +} + +/* + * JImageClose - Given the supplied open image file (see JImageOpen), release + * memory and resources used by the open file and close the file. If the image + * file is shared by other uses, release and close is deferred until the last use + * is also closed. + * + * Ex. + * (*JImageClose)(image); + */ +extern "C" void JIMAGE_Close(JImageFile* image) { + ImageFileReader::close((ImageFileReader*) image); +} + +/* + * JImagePackageToModule - Given an open image file (see JImageOpen) and the name + * of a package, return the name of module where the package resides. If the + * package does not exist in the image file, the function returns NULL. + * The resulting string does/should not have to be released. All strings are + * utf-8, zero byte terminated. + * + * Ex. + * const char* package = (*JImagePackageToModule)(image, "java/lang"); + * tty->print_cr(package); + * —> java.base + */ +extern "C" const char* JIMAGE_PackageToModule(JImageFile* image, const char* package_name) { + return ((ImageFileReader*) image)->get_image_module_data()->package_to_module(package_name); +} + +/* + * JImageFindResource - Given an open image file (see JImageOpen), a module + * name, a version string and the name of a class/resource, return location + * information describing the resource and its size. If no resource is found, the + * function returns JIMAGE_NOT_FOUND and the value of size is undefined. + * The version number should be "9.0" and is not used in locating the resource. + * The resulting location does/should not have to be released. + * All strings are utf-8, zero byte terminated. + * + * Ex. + * jlong size; + * JImageLocationRef location = (*JImageFindResource)(image, "java.base", "9.0", "java/lang/String.class", &size); + */ +extern "C" JImageLocationRef JIMAGE_FindResource(JImageFile* image, + const char* module_name, const char* version, const char* name, + jlong* size) { + if (strcmp(version, BOOT_VERSION) != 0) { + return (JImageLocationRef) 0; + } + + ImageLocation location; + char fullpath[IMAGE_MAX_PATH]; + + // Concatenate to get full path + strncpy(fullpath, "/", IMAGE_MAX_PATH - 1); + strncat(fullpath, module_name, IMAGE_MAX_PATH - 1); + strncat(fullpath, "/", IMAGE_MAX_PATH - 1); + strncat(fullpath, name, IMAGE_MAX_PATH - 1); + JImageLocationRef loc = + (JImageLocationRef) ((ImageFileReader*) image)->find_location_index(fullpath, (u8*) size); + return loc; +} + +/* + * JImageGetResource - Given an open image file (see JImageOpen), a resource’s + * location information (see JImageFindResource), a buffer of appropriate + * size and the size, retrieve the bytes associated with the + * resource. If the size is less than the resource size then the read is truncated. + * If the size is greater than the resource size then the remainder of the buffer + * is zero filled. The function will return the actual size of the resource. + * + * Ex. + * jlong size; + * JImageLocationRef* location = (*JImageFindResource)(image, "java.base", "9.0", "java/lang/String.class", &size); + * char* buffer = new char[size]; + * (*JImageGetResource)(image, location, buffer, size); + */ +extern "C" jlong JIMAGE_GetResource(JImageFile* image, JImageLocationRef location, + char* buffer, jlong size) { + ((ImageFileReader*) image)->get_resource((u4) location, (u1*) buffer); + return size; +} + +/* + * JImageResourceIterator - Given an open image file (see JImageOpen), a visitor + * function and a visitor argument, iterator through each of the image's resources. + * The visitor function is called with the image file, the module name, the + * package name, the base name, the extension and the visitor argument. The return + * value of the visitor function should be true, unless an early iteration exit is + * required. All strings are utf-8, zero byte terminated.file. + * + * Ex. + * bool ctw_visitor(JImageFile* jimage, const char* module_name, const char* version, const char* package, const char* name, const char* extension, void* arg) { + * if (strcmp(extension, “class”) == 0) { + * char path[JIMAGE_MAX_PATH]; + * Thread* THREAD = Thread::current(); + * jio_snprintf(path, JIMAGE_MAX_PATH - 1, "/%s/%s", package, name); + * ClassLoader::compile_the_world_in(path, (Handle)arg, THREAD); + * return !HAS_PENDING_EXCEPTION; + * } + * return true; + * } + * (*JImageResourceIterator)(image, ctw_visitor, loader); + */ +extern "C" void JIMAGE_ResourceIterator(JImageFile* image, + JImageResourceVisitor_t visitor, void* arg) { + ImageFileReader* imageFile = (ImageFileReader*) image; + u4 nEntries = imageFile->table_length(); + const ImageStrings strings = imageFile->get_strings(); + for (u4 i = 0; i < nEntries; i++) { + ImageLocation location(imageFile->get_location_data(i)); + + u4 moduleOffset = (u4) location.get_attribute(ImageLocation::ATTRIBUTE_MODULE); + if (moduleOffset == 0) { + continue; // skip non-modules + } + const char *module = strings.get(moduleOffset); + if (strcmp(module, "modules") == 0 + || strcmp(module, "packages") == 0) { + continue; // always skip + } + + u4 parentOffset = (u4) location.get_attribute(ImageLocation::ATTRIBUTE_PARENT); + const char *parent = strings.get(parentOffset); + u4 baseOffset = (u4) location.get_attribute(ImageLocation::ATTRIBUTE_BASE); + const char *base = strings.get(baseOffset); + u4 extOffset = (u4) location.get_attribute(ImageLocation::ATTRIBUTE_EXTENSION); + const char *extension = strings.get(extOffset); + + if (!(*visitor)(image, module, "9", parent, base, extension, arg)) { + break; + } + + } +} + diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/jimage.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/jimage.hpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "jni.h" + +// Opaque reference to a JImage file. +class JImageFile; +// Opaque reference to an image file resource location. +typedef jlong JImageLocationRef; + +// Max path length limit independent of platform. Windows max path is 1024, +// other platforms use 4096. The JCK fails several tests when 1024 is used. +#define JIMAGE_MAX_PATH 4096 + +// JImage Error Codes + +// The image file is not prefixed with 0xCAFEDADA +#define JIMAGE_BAD_MAGIC (-1) +// The image file does not have a compatible (translatable) version +#define JIMAGE_BAD_VERSION (-2) +// The image file content is malformed +#define JIMAGE_CORRUPTED (-3) + +/* + * JImageOpen - Given the supplied full path file name, open an image file. This + * function will also initialize tables and retrieve meta-data necessary to + * satisfy other functions in the API. If the image file has been previously + * open, a new open request will share memory and resources used by the previous + * open. A call to JImageOpen should be balanced by a call to JImageClose, to + * release memory and resources used. If the image file is not found or cannot + * be open, then NULL is returned and error will contain a reason for the + * failure; a positive value for a system error number, negative for a jimage + * specific error (see JImage Error Codes.) + * + * Ex. + * jint error; + * JImageFile* jimage = (*JImageOpen)(JAVA_HOME "lib/modules/bootmodules.jimage", &error); + * if (image == NULL) { + * tty->print_cr("JImage failed to open: %d", error); + * ... + * } + * ... + */ + +extern "C" JImageFile* JIMAGE_Open(const char *name, jint* error); + +typedef JImageFile* (*JImageOpen_t)(const char *name, jint* error); + +/* + * JImageClose - Given the supplied open image file (see JImageOpen), release + * memory and resources used by the open file and close the file. If the image + * file is shared by other uses, release and close is deferred until the last use + * is also closed. + * + * Ex. + * (*JImageClose)(image); + */ + +extern "C" void JIMAGE_Close(JImageFile* jimage); + +typedef void (*JImageClose_t)(JImageFile* jimage); + + +/* + * JImagePackageToModule - Given an open image file (see JImageOpen) and the name + * of a package, return the name of module where the package resides. If the + * package does not exist in the image file, the function returns NULL. + * The resulting string does/should not have to be released. All strings are + * utf-8, zero byte terminated. + * + * Ex. + * const char* package = (*JImagePackageToModule)(image, "java/lang"); + * tty->print_cr(package); + * —> java.base + */ + +extern "C" const char * JIMAGE_PackageToModule(JImageFile* jimage, const char* package_name); + +typedef const char* (*JImagePackageToModule_t)(JImageFile* jimage, const char* package_name); + + +/* + * JImageFindResource - Given an open image file (see JImageOpen), a module + * name, a version string and the name of a class/resource, return location + * information describing the resource and its size. If no resource is found, the + * function returns JIMAGE_NOT_FOUND and the value of size is undefined. + * The version number should be "9.0" and is not used in locating the resource. + * The resulting location does/should not have to be released. + * All strings are utf-8, zero byte terminated. + * + * Ex. + * jlong size; + * JImageLocationRef location = (*JImageFindResource)(image, "java.base", "9.0", "java/lang/String.class", &size); + */ +extern "C" JImageLocationRef JIMAGE_FindResource(JImageFile* jimage, + const char* module_name, const char* version, const char* name, + jlong* size); + +typedef JImageLocationRef(*JImageFindResource_t)(JImageFile* jimage, + const char* module_name, const char* version, const char* name, + jlong* size); + + +/* + * JImageGetResource - Given an open image file (see JImageOpen), a resource’s + * location information (see JImageFindResource), a buffer of appropriate + * size and the size, retrieve the bytes associated with the + * resource. If the size is less than the resource size then the read is truncated. + * If the size is greater than the resource size then the remainder of the buffer + * is zero filled. The function will return the actual size of the resource. + * + * Ex. + * jlong size; + * JImageLocationRef location = (*JImageFindResource)(image, "java.base", "9.0", "java/lang/String.class", &size); + * char* buffer = new char[size]; + * (*JImageGetResource)(image, location, buffer, size); + */ +extern "C" jlong JIMAGE_GetResource(JImageFile* jimage, JImageLocationRef location, + char* buffer, jlong size); + +typedef jlong(*JImageGetResource_t)(JImageFile* jimage, JImageLocationRef location, + char* buffer, jlong size); + + +/* + * JImageResourceIterator - Given an open image file (see JImageOpen), a visitor + * function and a visitor argument, iterator through each of the image's resources. + * The visitor function is called with the image file, the module name, the + * package name, the base name, the extension and the visitor argument. The return + * value of the visitor function should be true, unless an early iteration exit is + * required. All strings are utf-8, zero byte terminated.file. + * + * Ex. + * bool ctw_visitor(JImageFile* jimage, const char* module_name, const char* version, const char* package, const char* name, const char* extension, void* arg) { + * if (strcmp(extension, “class”) == 0) { + * char path[JIMAGE_MAX_PATH]; + * Thread* THREAD = Thread::current(); + * jio_snprintf(path, JIMAGE_MAX_PATH - 1, "/%s/%s", package, name); + * ClassLoader::compile_the_world_in(path, (Handle)arg, THREAD); + * return !HAS_PENDING_EXCEPTION; + * } + * return true; + * } + * (*JImageResourceIterator)(image, ctw_visitor, loader); + */ + +typedef bool (*JImageResourceVisitor_t)(JImageFile* jimage, + const char* module_name, const char* version, const char* package, + const char* name, const char* extension, void* arg); + +extern "C" void JIMAGE_ResourceIterator(JImageFile* jimage, + JImageResourceVisitor_t visitor, void *arg); + +typedef void (*JImageResourceIterator_t)(JImageFile* jimage, + JImageResourceVisitor_t visitor, void* arg); diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/share/native/libjimage/osSupport.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libjimage/osSupport.hpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef LIBJIMAGE_OSSUPPORT_HPP +#define LIBJIMAGE_OSSUPPORT_HPP + +#ifdef WIN32 +#include +#else +#include +#endif + +class osSupport { +public: + /** + * Open a regular file read-only. + * Return the file descriptor. + */ + static jint openReadOnly(const char *path); + + /** + * Close a file descriptor. + */ + static jint close(jint fd); + + /** + * Return the size of a regular file. + */ + static jlong size(const char *path); + + /** + * Read nBytes at offset into a buffer. + */ + static jlong read(jint fd, char *buf, jlong nBytes, jlong offset); + + /** + * Map nBytes at offset into memory and return the address. + * The system chooses the address. + */ + static void* map_memory(jint fd, const char *filename, size_t file_offset, size_t bytes); + + /** + * Unmap nBytes of memory at address. + */ + static int unmap_memory(void* addr, size_t bytes); +}; + +/** + * A CriticalSection to protect a small section of code. + */ +class SimpleCriticalSection { + friend class SimpleCriticalSectionLock; +private: + void enter(); + void exit(); +public: + SimpleCriticalSection(); + //~SimpleCriticalSection(); // Cretes a dependency on Solaris on a C++ exit registration + +private: +#ifdef WIN32 + CRITICAL_SECTION critical_section; +#else + pthread_mutex_t mutex; +#endif // WIN32 +}; + +/** + * SimpleCriticalSectionLock instance. + * The constructor locks a SimpleCriticalSection and the + * destructor does the unlock. + */ +class SimpleCriticalSectionLock { +private: + SimpleCriticalSection *lock; +public: + + SimpleCriticalSectionLock(SimpleCriticalSection *cslock) { + this->lock = cslock; + lock->enter(); + } + + ~SimpleCriticalSectionLock() { + lock->exit(); + } +}; + +#endif // LIBJIMAGE_OSSUPPORT_HPP diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/unix/native/libjimage/osSupport_unix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/unix/native/libjimage/osSupport_unix.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "jni.h" +#include "osSupport.hpp" + +/** + * Open a regular file read-only. + * Return the file descriptor. + */ +jint osSupport::openReadOnly(const char *path) { + return ::open(path, 0); +} + +/** + * Close a file descriptor. + */ +jint osSupport::close(jint fd) { + return ::close(fd); +} + +/** + * Return the size of a regular file. + */ +jlong osSupport::size(const char *path) { + struct stat statbuf; + if (stat(path, &statbuf) < 0 || + (statbuf.st_mode & S_IFREG) != S_IFREG) { + return -1; + } + return (jsize) statbuf.st_size; +} + +/** + * Read nBytes at offset into a buffer. + */ +jlong osSupport::read(jint fd, char *buf, jlong nBytes, jlong offset) { + return ::pread(fd, buf, nBytes, offset); +} + +/** + * Map nBytes at offset into memory and return the address. + * The system chooses the address. + */ +void* osSupport::map_memory(int fd, const char *filename, size_t file_offset, size_t bytes) { + void* mapped_address = NULL; + mapped_address = (void*) mmap(NULL, + bytes, PROT_READ, MAP_SHARED, + fd, file_offset); + if (mapped_address == MAP_FAILED) { + return NULL; + } + return mapped_address; +} + +/** + * Unmap nBytes of memory at address. + */ +int osSupport::unmap_memory(void *addr, size_t bytes) { + return munmap((char *) addr, bytes) == 0; +} + +/** + * A CriticalSection to protect a small section of code. + */ +void SimpleCriticalSection::enter() { + pthread_mutex_lock(&mutex); +} + +void SimpleCriticalSection::exit() { + pthread_mutex_unlock(&mutex); + +} + +SimpleCriticalSection::SimpleCriticalSection() { + pthread_mutex_init(&mutex, NULL); +} + +//SimpleCriticalSection::~SimpleCriticalSection() { +// pthread_mutex_destroy(&mutex); +//} + diff -r e00364b38376 -r ac2c73b45253 jdk/src/java.base/windows/native/libjimage/osSupport_windows.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/windows/native/libjimage/osSupport_windows.cpp Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "jni.h" +#include "osSupport.hpp" + +/** + * Open a regular file read-only. + * Return the file descriptor. + */ +jint osSupport::openReadOnly(const char *path) { + return ::open(path, 0, 0); +} + +/** + * Close a file descriptor. + */ +jint osSupport::close(jint fd) { + return ::close(fd); +} + +/** + * Return the size of a regular file. + */ +jlong osSupport::size(const char *path) { + struct stat statbuf; + if (stat(path, &statbuf) < 0 || + (statbuf.st_mode & S_IFREG) != S_IFREG) { + return -1; + } + return (jlong) statbuf.st_size; +} + +/** + * Read nBytes at offset into a buffer. + */ +jlong osSupport::read(jint fd, char *buf, jlong nBytes, jlong offset) { + OVERLAPPED ov; + DWORD nread; + BOOL result; + + ZeroMemory(&ov, sizeof (ov)); + ov.Offset = (DWORD) offset; + ov.OffsetHigh = (DWORD) (offset >> 32); + + HANDLE h = (HANDLE)::_get_osfhandle(fd); + + result = ReadFile(h, (LPVOID) buf, (DWORD) nBytes, &nread, &ov); + + return result ? nread : 0; +} + +/** + * Map nBytes at offset into memory and return the address. + * The system chooses the address. + */ +void* osSupport::map_memory(jint fd, const char *file_name, size_t file_offset, size_t bytes) { + HANDLE hFile; + char* base = NULL; + + // Get a handle to the file + hFile = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != NULL) { + // Create a file mapping handle + HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, + NULL /* file_name */); + if (hMap != NULL) { + // Map the file into the address space at the offset + base = (char*) MapViewOfFileEx(hMap, FILE_MAP_READ, 0, (DWORD) file_offset, + (DWORD) bytes, NULL); + CloseHandle(hMap); // The mapping is no longer needed + } + CloseHandle(hFile); // The file handle is no longer needed + } + return base; +} + +/** + * Unmap nBytes of memory at address. + */ +int osSupport::unmap_memory(void* addr, size_t bytes) { + BOOL result = UnmapViewOfFile(addr); + return result; +} + +/** + * A CriticalSection to protect a small section of code. + */ +void SimpleCriticalSection::enter() { + EnterCriticalSection(&critical_section); +} + +void SimpleCriticalSection::exit() { + LeaveCriticalSection(&critical_section); +} + +SimpleCriticalSection::SimpleCriticalSection() { + InitializeCriticalSection(&critical_section); +} + +//SimpleCriticalSection::~SimpleCriticalSection() { +// DeleteCriticalSection(&critical_section); +//} + diff -r e00364b38376 -r ac2c73b45253 jdk/test/TEST.groups --- a/jdk/test/TEST.groups Mon Aug 31 21:48:00 2015 +0300 +++ b/jdk/test/TEST.groups Fri Sep 04 10:11:43 2015 -0300 @@ -67,6 +67,7 @@ sun/misc \ sun/reflect \ jdk/lambda \ + jdk/internal/jimage \ vm # All of the java.util package @@ -210,13 +211,13 @@ # java launcher specific tests, Note: do not include this group into any groups # that potentially could be included into a jprt test rule, as the complementary -# closed group includes awt SplashScreen and these tests may not run -# satisfacotorily on all platforms and profiles thus this group must always +# closed group includes awt SplashScreen and these tests may not run +# satisfactorily on all platforms and profiles thus this group must always # be a stand-alone group jdk_launcher = \ tools/launcher \ sun/tools - + # # Tool (and tool API) tests are split into core and svc groups # @@ -473,7 +474,7 @@ sun/reflect/CallerSensitive/MissingCallerSensitive.java \ sun/security/util/Resources/NewNamesFormat.java \ vm/verifier/defaultMethods/DefaultMethodRegressionTestsRun.java \ - javax/xml/ws/clientjar/TestWsImport.java + javax/xml/ws/clientjar/TestWsImport.java # JRE adds further tests to compact3 # diff -r e00364b38376 -r ac2c73b45253 jdk/test/jdk/internal/jimage/JImageReadTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/jdk/internal/jimage/JImageReadTest.java Fri Sep 04 10:11:43 2015 -0300 @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @modules java.base/jdk.internal.jimage + * @summary Unit test for libjimage JIMAGE_Open/Read/Close + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; + +import jdk.internal.jimage.BasicImageReader; +import jdk.internal.jimage.ImageNativeSubstrate; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Optional; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; +import org.testng.Assert; +import org.testng.TestNG; + +@Test +public class JImageReadTest { + + static String javaHome = System.getProperty("java.home"); + static String imageFile = javaHome + File.separator + "lib" + + File.separator + "modules" + File.separator + + "bootmodules.jimage"; + + @DataProvider(name="classes") + static Object[][] loadClasses() { + return new Object[][] { + {"java.base", "java/lang/String.class"}, + {"java.base", "java/lang/Object.class"}, + {"java.base", "sun/reflect/generics/tree/TypeArgument.class"}, + {"jdk.jdeps", "com/sun/tools/javap/StackMapWriter$StackMapBuilder.class"}, + {"jdk.hotspot.agent", "sa.properties"}, + {"java.logging", "java/util/logging/Logger.class"}, + {"java.base", "java/NOSUCHCLASS/yyy.class"}, // non-existent + {"NOSUCHMODULE", "java/lang/Class.class"}, // non-existent + }; + } + + + @DataProvider(name="packages") + static Object[][] loadPackages() { + return new Object[][] { + {"java.base", "java/lang"}, + {"java.base", "java/io"}, + {"java.logging", "java/util/logging"}, + }; + } + + /** + * Test a class is correctly accessible from the image in a module. + * + * @param moduleName the module name + * @param className the classname + * @throws Exception is thrown if there is a test error + */ + @Test(dataProvider="classes") + public static void test1_ReadClasses(String moduleName, String className) throws Exception { + final int classMagic = 0xCAFEBABE; + final long NOT_FOUND = 0L; + + if (!(new File(imageFile)).exists()) { + System.out.printf("Test skipped; no jimage file"); + return; + } + + long jimageHandle = ImageNativeSubstrate.JIMAGE_Open(imageFile); + Assert.assertTrue(jimageHandle != 0, "JIMAGE_Open failed: id: " + jimageHandle); + + long[] sizeArray = new long[1]; + long locationHandle = + ImageNativeSubstrate.JIMAGE_FindResource(jimageHandle, + moduleName, "9.0", className, sizeArray); + long size = sizeArray[0]; + System.out.printf("reading: module: %s, path: %s, handle: %16x, " + + "location: %d, size: %d%n", + moduleName, className, jimageHandle, locationHandle, size); + if (moduleName.contains("NOSUCH") || className.contains("NOSUCH")) { + Assert.assertEquals(locationHandle, NOT_FOUND, + "location found for non-existing module: " + + moduleName + + ", or class: " + className); + return; // no more to test for non-existing class + } else { + Assert.assertTrue(locationHandle != NOT_FOUND, "location not found: " + className); + Assert.assertTrue(size > 0, "size of should be > 0: " + className); + } + + // positive: read whole class + ByteBuffer buffer = ByteBuffer.allocate((int)size); + long actual = ImageNativeSubstrate.JIMAGE_GetResource(jimageHandle, + locationHandle, buffer.array(), size); + Assert.assertEquals(actual, size, "bytes read not equal bytes requested"); + + if (className.endsWith(".class")) { + int m = buffer.getInt(); + Assert.assertEquals(m, classMagic, "Classfile has bad magic number"); + } + + // Read less than the size of the artifact + buffer.rewind(); + Arrays.fill(buffer.array(), (byte)0xc0); + long sizeExpected = size - 10; + actual = ImageNativeSubstrate.JIMAGE_GetResource(jimageHandle, + locationHandle, buffer.array(), sizeExpected); + Assert.assertEquals(actual, sizeExpected, "bytes read not equal bytes requested"); + + if (className.endsWith(".class")) { + int m1 = buffer.getInt(); + Assert.assertEquals(m1, classMagic, "Read operation succeeded but has bad magic number"); + } + + ImageNativeSubstrate.JIMAGE_Close(jimageHandle); + } + + /** + * For all the resource names, check the name and approximate count. + * + * @throws IOException thrown if an error occurs + */ + @Test + static void test2_ImageResources() throws IOException { + if (!(new File(imageFile)).exists()) { + System.out.printf("Test skipped; no jimage file"); + return; + } + + long jimageHandle = ImageNativeSubstrate.JIMAGE_Open(imageFile); + Assert.assertTrue(jimageHandle != 0, "JIMAGE_Open failed: id: " + jimageHandle); + + String[] names = new String[4096]; + int max = ImageNativeSubstrate.JIMAGE_Resources(jimageHandle, + names); + + // Repeat with count available + names = new String[max + 1]; + int count = ImageNativeSubstrate.JIMAGE_Resources(jimageHandle, + names); + System.out.printf(" count: %d, a class: %s\n", count, names[0]); + Assert.assertTrue(max > 31000, + "missing entries, should be more than 31000, reported: " + count); + Assert.assertTrue(count == max, + "unexpected count of entries, count: " + count + + ", max: " + max); + for (int i = 0; i < count; i++) { + checkFullName(names[i]); + } + + ImageNativeSubstrate.JIMAGE_Close(jimageHandle); + } + + /** + * Tests that a package exists and correctly mapped to the module. + * + * @param moduleName the module name + * @param packageName the package name + * @throws IOException thrown if an error occurs + */ + @Test(dataProvider="packages") + static void test3_PackageToModule(String moduleName, String packageName) throws IOException { + if (!(new File(imageFile)).exists()) { + System.out.printf("Test skipped; no jimage file"); + return; + } + + long jimageHandle = ImageNativeSubstrate.JIMAGE_Open(imageFile); + Assert.assertTrue(jimageHandle != 0, "JIMAGE_Open failed: id: " + jimageHandle); + + String result = ImageNativeSubstrate.JIMAGE_PackageToModule(jimageHandle, packageName); + System.out.printf(" package: %s, module: %s%n", packageName, result); + Assert.assertEquals(result, moduleName, "wrong module for package: " + packageName); + + ImageNativeSubstrate.JIMAGE_Close(jimageHandle); + } + + + static void checkFullName(String path) { + int next = 0; + String m = null; + String p = null; + String b = null; + String e = null; + if (path.startsWith("/")) { + next = path.indexOf('/', 1); + m = path.substring(1, next); + next = next + 1; + } + int lastSlash = path.lastIndexOf('/'); + if (lastSlash > next) { + // has a parent + p = path.substring(next, lastSlash); + next = lastSlash + 1; + } + int period = path.indexOf('.', next); + if (period > next) { + b = path.substring(next, period); + e = path.substring(period + 1); + } else { + b = path.substring(next); + } + Assert.assertNotNull(m, "module must be non-empty"); + Assert.assertNotNull(b, "base name must be non-empty"); + } + + /** + * Verify that all of the resource names from BasicImageReader + * match those returned from the native JIMAGE_Resources iterator. + * Names that start with /modules, /packages, and bootmodules.jdata + * must appear in the names from JIMAGE_Resource iterator; + * from the BasicImageReader they are ignored. + */ + @Test + static void test4_verifyNames() { + if (!(new File(imageFile)).exists()) { + System.out.printf("Test skipped; no jimage file"); + return; + } + + try { + String[] names = BasicImageReader_Names(); + //writeNames("/tmp/basic-names.txt", names); // debug + + // Read all the names from the native JIMAGE API + String[] nativeNames = JIMAGE_Names(); + //writeNames("/tmp/native-names.txt", nativeNames); // debug + + + int modCount = 0; + int pkgCount = 0; + int otherCount = 0; + for (String n : nativeNames) { + if (n.startsWith("/modules/")) { + modCount++; + } else if (n.startsWith("/packages/")) { + pkgCount++; + } else { + otherCount++; + } + } + System.out.printf("native name count: %d, modCount: %d, pkgCount: %d, otherCount: %d%n", + names.length, modCount, pkgCount, otherCount); + + Assert.assertEquals(modCount, 0, "JIMAGE_ResourceIterator should not return any '/modules' paths"); + Assert.assertEquals(pkgCount, 0, "JIMAGE_ResourceIterator should not return any '/packages' paths"); + + // Sort and merge the two arrays. Every name should appear exactly twice. + Arrays.sort(names); + Arrays.sort(nativeNames); + String[] combined = Arrays.copyOf(names, nativeNames.length + names.length); + System.arraycopy(nativeNames,0, combined, names.length, nativeNames.length); + Arrays.sort(combined); + int missing = 0; + for (int i = 0; i < combined.length; i++) { + String s = combined[i]; + if (isMetaName(s)) { + // Ignore /modules and /packages in BasicImageReader names + continue; + } + + if (i < combined.length - 1 && s.equals(combined[i + 1])) { + i++; // string appears in both java and native + continue; + } + + missing++; + int ndx = Arrays.binarySearch(names, s); + String which = (ndx >= 0) ? "java BasicImageReader" : "native JIMAGE_Resources"; + System.out.printf("Missing Resource: %s found only via %s%n", s, which); + } + Assert.assertEquals(missing, 0, "Resources missing"); + + } catch (IOException ioe) { + Assert.fail("I/O exception", ioe); + } + } + + /** + * Return true if the name is one of the meta-data names + * @param name a name + * @return return true if starts with either /packages or /modules + */ + static boolean isMetaName(String name) { + return name.startsWith("/modules") + || name.startsWith("/packages") + || name.equals("bootmodules.jdata"); + } + + /** + * Return all of the names from BasicImageReader. + * @return the names returned from BasicImageReader + */ + static String[] BasicImageReader_Names() throws IOException { + String[] names = null; + try (BasicImageReader reader = BasicImageReader.open(imageFile)) { + names = reader.getEntryNames(); + } catch (IOException ioe) { + Assert.fail("I/O exception", ioe); + } + return names; + } + + /** + * Returns an array of all of the names returned from JIMAGE_Resources + */ + static String[] JIMAGE_Names() throws IOException { + + long jimageHandle = ImageNativeSubstrate.JIMAGE_Open(imageFile); + Assert.assertTrue(jimageHandle != 0, "JIMAGE_Open failed: id: " + jimageHandle); + + String[] names = new String[50000]; + int max = ImageNativeSubstrate.JIMAGE_Resources(jimageHandle, names); + + if (max > names.length) { + // Not all names fit, repeat with correct size + names = new String[max]; + max = ImageNativeSubstrate.JIMAGE_Resources(jimageHandle, names); + } else { + names = Arrays.copyOf(names, max); + } + + ImageNativeSubstrate.JIMAGE_Close(jimageHandle); + return names; + } + + // Write an array of names to a file for debugging + static void writeNames(String fname, String[] names) throws IOException { + try (FileWriter wr = new FileWriter(new File(fname))) { + for (String s : names) { + wr.write(s); + wr.write("\n"); + } + + } + System.out.printf(" %s: %d names%n", fname, names.length); + } + + // main method to run standalone from jtreg + + @Test(enabled=false) + @Parameters({"x"}) + @SuppressWarnings("raw_types") + public static void main(@Optional String[] args) { + Class[] testclass = { JImageReadTest.class}; + TestNG testng = new TestNG(); + testng.setTestClasses(testclass); + testng.run(); + } + +}