jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp
author rriggs
Fri, 30 Oct 2015 11:12:20 -0400
changeset 33492 051df634418c
parent 32757 79d34d4b9627
child 33661 ad7c7378a002
permissions -rw-r--r--
8139390: Very long classname in jimage causes SIGSEGV Summary: Correct issues with ImageNativeSubstrate and JImageReadTest Reviewed-by: mchung

/*
 * 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 <string.h>

#include "jni.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;

/////////////////////////////////////////////////////////////////////////////

// Static function for primitive throw since libjimage is not linked with libjava
static void JNICALL ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
    jclass cls = (env)->FindClass(name);

    if (cls != 0) /* Otherwise an exception has already been thrown */
        (env)->ThrowNew(cls, msg);
}

// 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;

    if (moduleName == NULL) {
        ThrowByName(env, "java/lang/NullPointerException", "moduleName");
        return 0;
    }
    if (version == NULL) {
        ThrowByName(env, "java/lang/NullPointerException", "version");
        return 0;
    }
    if (path == NULL) {
        ThrowByName(env, "java/lang/NullPointerException", "path");
        return 0;
    }
    if (output_size == NULL) {
        ThrowByName(env, "java/lang/NullPointerException", "size");
        return 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];
        size_t moduleLen = strlen(module);
        size_t packageLen = strlen(package);
        size_t nameLen = strlen(name);
        size_t extLen = strlen(extension);
        size_t index;

        if (1 + moduleLen + 1 + packageLen + 1 + nameLen + 1 + extLen + 1 > IMAGE_MAX_PATH) {
            ThrowByName(env, "java/lang/InternalError", "concatenated name too long");
            return true;
        }

        index = 0;
        if (moduleLen > 0) {
            fullpath[index++] = '/';
            memcpy(&fullpath[index], module, moduleLen);
            index += moduleLen;
            fullpath[index++] = '/';
        }
        if (packageLen > 0) {
            memcpy(&fullpath[index], package, packageLen);
            index += packageLen;
            fullpath[index++] = '/';
        }
        memcpy(&fullpath[index], name, nameLen);
        index += nameLen;
        if (extLen > 0) {
            fullpath[index++] = '.';
            memcpy(&fullpath[index], extension, extLen);
            index += extLen;
        }
        fullpath[index++] = '\0';

        jobject str = env->NewStringUTF(fullpath);
        if (env->ExceptionCheck()) {
            return true;
        }

        env->SetObjectArrayElement(vdata->array, vdata->size, str);
        if (env->ExceptionCheck()) {
            return 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);
    if (env->ExceptionCheck()) {
        return 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();
}