8240975: Extend NativeLibraries to support explicit unloading

Reviewed-by: alanb, mcimadamore
This commit is contained in:
Mandy Chung 2020-03-23 09:05:39 -07:00
parent b66c680885
commit 75a8b7fa83
9 changed files with 493 additions and 92 deletions
src/java.base/share
test/jdk/jdk/internal/loader/NativeLibraries

@ -2374,7 +2374,7 @@ public abstract class ClassLoader {
return null;
}
private final NativeLibraries libraries = new NativeLibraries(this);
private final NativeLibraries libraries = NativeLibraries.jniNativeLibraries(this);
// Invoked in the java.lang.Runtime class to implement load and loadLibrary.
static NativeLibrary loadLibrary(Class<?> fromClass, File file) {

@ -73,7 +73,7 @@ public class BootLoader {
// native libraries loaded by the boot class loader
private static final NativeLibraries NATIVE_LIBS
= new NativeLibraries(null);
= NativeLibraries.jniNativeLibraries(null);
/**
* Returns the unnamed module for the boot loader.

@ -24,6 +24,7 @@
*/
package jdk.internal.loader;
import jdk.internal.misc.VM;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.util.StaticProperty;
@ -34,6 +35,7 @@ import java.security.PrivilegedAction;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Objects;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -52,24 +54,73 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class NativeLibraries {
private final Map<String, NativeLibrary> libraries = new ConcurrentHashMap<>();
private final Map<String, NativeLibraryImpl> libraries = new ConcurrentHashMap<>();
private final ClassLoader loader;
private final Class<?> caller; // may be null. If not null, this is used as
// fromClass as a fast-path. See loadLibrary(String name).
// caller, if non-null, is the fromClass parameter for NativeLibraries::loadLibrary
// unless specified
private final Class<?> caller; // may be null
private final boolean searchJavaLibraryPath;
// loading JNI native libraries
private final boolean isJNI;
public NativeLibraries(ClassLoader loader) {
/**
* Creates a NativeLibraries instance for loading JNI native libraries
* via for System::loadLibrary use.
*
* 1. Support of auto-unloading. The loaded native libraries are unloaded
* when the class loader is reclaimed.
* 2. Support of linking of native method. See JNI spec.
* 3. Restriction on a native library that can only be loaded by one class loader.
* Each class loader manages its own set of native libraries.
* The same JNI native library cannot be loaded into more than one class loader.
*
* This static factory method is intended only for System::loadLibrary use.
*
* @see <a href="${docroot}/specs/jni/invocation.html##library-and-version-management">
* JNI Specification: Library and Version Management</a>
*/
public static NativeLibraries jniNativeLibraries(ClassLoader loader) {
return new NativeLibraries(loader);
}
/**
* Creates a raw NativeLibraries instance that has the following properties:
* 1. Native libraries loaded in this raw NativeLibraries instance are
* not JNI native libraries. Hence JNI_OnLoad and JNI_OnUnload will
* be ignored. No support for linking of native method.
* 2. Native libraries not auto-unloaded. They may be explicitly unloaded
* via NativeLibraries::unload.
* 3. No relationship with class loaders.
*
* This static factory method is restricted for JDK trusted class use.
*/
public static NativeLibraries rawNativeLibraries(Class<?> trustedCaller,
boolean searchJavaLibraryPath) {
return new NativeLibraries(trustedCaller, searchJavaLibraryPath);
}
private NativeLibraries(ClassLoader loader) {
// for null loader, default the caller to this class and
// do not search java.library.path
this(loader, loader != null ? null : NativeLibraries.class, loader != null ? true : false);
}
public NativeLibraries(ClassLoader loader, Class<?> caller, boolean searchJavaLibraryPath) {
if (caller != null && caller.getClassLoader() != loader) {
throw new IllegalArgumentException(caller.getName() + " must be defined by " + loader);
}
this.loader = loader;
this.caller = loader != null ? null : NativeLibraries.class;
this.searchJavaLibraryPath = loader != null ? true : false;
this.isJNI = true;
}
/*
* Constructs a NativeLibraries instance of no relationship with class loaders
* and disabled auto unloading.
*/
private NativeLibraries(Class<?> caller, boolean searchJavaLibraryPath) {
Objects.requireNonNull(caller);
if (!VM.isSystemDomainLoader(caller.getClassLoader())) {
throw new IllegalArgumentException("must be JDK trusted class");
}
this.loader = caller.getClassLoader();
this.caller = caller;
this.searchJavaLibraryPath = searchJavaLibraryPath;
this.isJNI = false;
}
/*
@ -169,11 +220,26 @@ public final class NativeLibraries {
}
}
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin);
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI);
// load the native library
nativeLibraryContext.push(lib);
try {
if (!lib.open()) return null;
if (!lib.open()) {
return null; // fail to open the native library
}
// auto unloading is only supported for JNI native libraries
// loaded by custom class loaders that can be unloaded.
// built-in class loaders are never unloaded.
boolean autoUnload = isJNI && !VM.isSystemDomainLoader(loader)
&& loader != ClassLoaders.appClassLoader();
if (autoUnload) {
// register the loaded native library for auto unloading
// when the class loader is reclaimed, all native libraries
// loaded that class loader will be unloaded.
// The entries in the libraries map are not removed since
// the entire map will be reclaimed altogether.
CleanerFactory.cleaner().register(loader, lib.unloader());
}
} finally {
nativeLibraryContext.pop();
}
@ -218,6 +284,26 @@ public final class NativeLibraries {
return lib;
}
/**
* Unloads the given native library
*
* @param lib native library
*/
public void unload(NativeLibrary lib) {
if (isJNI) {
throw new UnsupportedOperationException("explicit unloading cannot be used with auto unloading");
}
Objects.requireNonNull(lib);
synchronized (loadedLibraryNames) {
NativeLibraryImpl nl = libraries.remove(lib.name());
if (nl != lib) {
throw new IllegalArgumentException(lib.name() + " not loaded by this NativeLibraries instance");
}
// unload the native library and also remove from the global name registry
nl.unloader().run();
}
}
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
for (String path : paths) {
File libfile = new File(path, System.mapLibraryName(name));
@ -255,16 +341,21 @@ public final class NativeLibraries {
final String name;
// Indicates if the native library is linked into the VM
final boolean isBuiltin;
// Indicate if this is JNI native library
final boolean isJNI;
// opaque handle to native library, used in native code.
long handle;
// the version of JNI environment the native library requires.
int jniVersion;
NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin) {
NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin, boolean isJNI) {
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
this.fromClass = fromClass;
this.name = name;
this.isBuiltin = isBuiltin;
this.isJNI = isJNI;
}
@Override
@ -277,26 +368,19 @@ public final class NativeLibraries {
return findEntry0(this, name);
}
Runnable unloader() {
return new Unloader(name, handle, isBuiltin, isJNI);
}
/*
* Loads the native library and registers for cleanup when its
* associated class loader is unloaded
* Loads the named native library
*/
boolean open() {
if (handle != 0) {
throw new InternalError("Native library " + name + " has been loaded");
}
if (!load(this, name, isBuiltin)) return false;
// register the class loader for cleanup when unloaded
// builtin class loaders are never unloaded
ClassLoader loader = fromClass != null ? fromClass.getClassLoader() : null;
if (loader != null &&
loader != ClassLoaders.platformClassLoader() &&
loader != ClassLoaders.appClassLoader()) {
CleanerFactory.cleaner().register(loader, new Unloader(name, handle, isBuiltin));
}
return true;
return load(this, name, isBuiltin, isJNI);
}
}
@ -308,13 +392,15 @@ public final class NativeLibraries {
// This represents the context when a native library is unloaded
// and getFromClass() will return null,
static final NativeLibraryImpl UNLOADER =
new NativeLibraryImpl(null, "dummy", false);
new NativeLibraryImpl(null, "dummy", false, false);
final String name;
final long handle;
final boolean isBuiltin;
final boolean isJNI;
Unloader(String name, long handle, boolean isBuiltin) {
Unloader(String name, long handle, boolean isBuiltin, boolean isJNI) {
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
if (handle == 0) {
throw new IllegalArgumentException(
"Invalid handle for native library " + name);
@ -323,18 +409,21 @@ public final class NativeLibraries {
this.name = name;
this.handle = handle;
this.isBuiltin = isBuiltin;
this.isJNI = isJNI;
}
@Override
public void run() {
synchronized (NativeLibraries.loadedLibraryNames) {
synchronized (loadedLibraryNames) {
/* remove the native library name */
NativeLibraries.loadedLibraryNames.remove(name);
NativeLibraries.nativeLibraryContext.push(UNLOADER);
if (!loadedLibraryNames.remove(name)) {
throw new IllegalStateException(name + " has already been unloaded");
}
nativeLibraryContext.push(UNLOADER);
try {
unload(name, isBuiltin, handle);
unload(name, isBuiltin, isJNI, handle);
} finally {
NativeLibraries.nativeLibraryContext.pop();
nativeLibraryContext.pop();
}
}
}
@ -371,8 +460,8 @@ public final class NativeLibraries {
// JNI FindClass expects the caller class if invoked from JNI_OnLoad
// and JNI_OnUnload is NativeLibrary class
private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin);
private static native void unload(String name, boolean isBuiltin, long handle);
private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin, boolean isJNI);
private static native void unload(String name, boolean isBuiltin, boolean isJNI, long handle);
private static native String findBuiltinLib(String name);
private static native long findEntry0(NativeLibraryImpl lib, String name);
}

@ -31,15 +31,18 @@ package jdk.internal.loader;
public interface NativeLibrary {
String name();
/*
/**
* Finds the address of the entry of the given name. Returns 0
* if not found.
*
* @param name the name of the symbol to be found
*/
long find(String name);
/*
/**
* Finds the address of the entry of the given name.
*
* @param name the name of the symbol to be found
* @throws NoSuchMethodException if the named entry is not found.
*/
default long lookup(String name) throws NoSuchMethodException {

@ -109,11 +109,11 @@ static void *findJniFunction(JNIEnv *env, void *handle,
/*
* Class: jdk_internal_loader_NativeLibraries
* Method: load
* Signature: (Ljava/lang/String;Z)Z
* Signature: (Ljava/lang/String;ZZ)Z
*/
JNIEXPORT jboolean JNICALL
Java_jdk_internal_loader_NativeLibraries_load
(JNIEnv *env, jobject this, jobject lib, jstring name, jboolean isBuiltin)
(JNIEnv *env, jobject this, jobject lib, jstring name, jboolean isBuiltin, jboolean isJNI)
{
const char *cname;
jint jniVersion;
@ -128,50 +128,52 @@ Java_jdk_internal_loader_NativeLibraries_load
if (cname == 0)
return JNI_FALSE;
handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname);
if (handle) {
JNI_OnLoad_t JNI_OnLoad;
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
isBuiltin ? cname : NULL,
JNI_TRUE);
if (JNI_OnLoad) {
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
jniVersion = (*JNI_OnLoad)(jvm, NULL);
if (isJNI) {
if (handle) {
JNI_OnLoad_t JNI_OnLoad;
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
isBuiltin ? cname : NULL,
JNI_TRUE);
if (JNI_OnLoad) {
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
jniVersion = (*JNI_OnLoad)(jvm, NULL);
} else {
jniVersion = 0x00010001;
}
cause = (*env)->ExceptionOccurred(env);
if (cause) {
(*env)->ExceptionClear(env);
(*env)->Throw(env, cause);
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
}
goto done;
}
if (!JVM_IsSupportedJNIVersion(jniVersion) ||
(isBuiltin && jniVersion < JNI_VERSION_1_8)) {
char msg[256];
jio_snprintf(msg, sizeof(msg),
"unsupported JNI version 0x%08X required by %s",
jniVersion, cname);
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
}
goto done;
}
(*env)->SetIntField(env, lib, jniVersionID, jniVersion);
} else {
jniVersion = 0x00010001;
}
cause = (*env)->ExceptionOccurred(env);
if (cause) {
(*env)->ExceptionClear(env);
(*env)->Throw(env, cause);
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
cause = (*env)->ExceptionOccurred(env);
if (cause) {
(*env)->ExceptionClear(env);
(*env)->SetLongField(env, lib, handleID, (jlong)0);
(*env)->Throw(env, cause);
}
goto done;
}
if (!JVM_IsSupportedJNIVersion(jniVersion) ||
(isBuiltin && jniVersion < JNI_VERSION_1_8)) {
char msg[256];
jio_snprintf(msg, sizeof(msg),
"unsupported JNI version 0x%08X required by %s",
jniVersion, cname);
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
}
goto done;
}
(*env)->SetIntField(env, lib, jniVersionID, jniVersion);
} else {
cause = (*env)->ExceptionOccurred(env);
if (cause) {
(*env)->ExceptionClear(env);
(*env)->SetLongField(env, lib, handleID, (jlong)0);
(*env)->Throw(env, cause);
}
goto done;
}
(*env)->SetLongField(env, lib, handleID, ptr_to_jlong(handle));
loaded = JNI_TRUE;
@ -184,11 +186,11 @@ Java_jdk_internal_loader_NativeLibraries_load
/*
* Class: jdk_internal_loader_NativeLibraries
* Method: unload
* Signature: (Ljava/lang/String;ZJ)V
* Signature: (Ljava/lang/String;ZZJ)V
*/
JNIEXPORT void JNICALL
Java_jdk_internal_loader_NativeLibraries_unload
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jlong address)
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jboolean isJNI, jlong address)
{
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
void *handle;
@ -202,13 +204,15 @@ Java_jdk_internal_loader_NativeLibraries_unload
return;
}
handle = jlong_to_ptr(address);
JNI_OnUnload = (JNI_OnUnload_t )findJniFunction(env, handle,
isBuiltin ? cname : NULL,
JNI_FALSE);
if (JNI_OnUnload) {
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
(*JNI_OnUnload)(jvm, NULL);
if (isJNI) {
JNI_OnUnload = (JNI_OnUnload_t )findJniFunction(env, handle,
isBuiltin ? cname : NULL,
JNI_FALSE);
if (JNI_OnUnload) {
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
(*JNI_OnUnload)(jvm, NULL);
}
}
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
@ -299,5 +303,3 @@ Java_jdk_internal_loader_NativeLibraries_findBuiltinLib
free(libName);
return NULL;
}

@ -0,0 +1,70 @@
/*
* Copyright (c) 2020, 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.
*/
/*
* @test
* @bug 8240975
* @modules java.base/jdk.internal.loader
* @build java.base/* p.Test Main
* @run main/othervm/native -Xcheck:jni Main
* @summary Test loading and unloading of native libraries
*/
import jdk.internal.loader.*;
import jdk.internal.loader.NativeLibrariesTest;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String... args) throws Exception {
setup();
NativeLibrariesTest test = new NativeLibrariesTest();
test.runTest();
try {
System.loadLibrary(NativeLibrariesTest.LIB_NAME);
} catch (UnsatisfiedLinkError e) { e.printStackTrace(); }
// unload the native library and then System::loadLibrary should succeed
test.unload();
System.loadLibrary(NativeLibrariesTest.LIB_NAME);
// expect NativeLibraries to fail since the library has been loaded by System::loadLibrary
try {
test.load(false);
} catch (UnsatisfiedLinkError e) { e.printStackTrace(); }
}
/*
* move p/Test.class out from classpath to the scratch directory
*/
static void setup() throws IOException {
String dir = System.getProperty("test.classes", ".");
Path p = Files.createDirectories(Paths.get("classes").resolve("p"));
Files.move(Paths.get(dir, "p", "Test.class"), p.resolve("Test.class"));
}
}

@ -0,0 +1,135 @@
/*
* Copyright (c) 2020, 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.
*/
package jdk.internal.loader;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
public class NativeLibrariesTest implements Runnable {
public static final String LIB_NAME = "nativeLibrariesTest";
// increments when JNI_OnLoad and JNI_OnUnload is invoked.
// This is only for JNI native library
private static int loadedCount = 0;
private static int unloadedCount = 0;
/*
* Called by JNI_OnLoad when the native library is unloaded
*/
static void nativeLibraryLoaded() {
loadedCount++;
}
/*
* Called by JNI_OnUnload when the native library is unloaded
*/
static void nativeLibraryUnloaded() {
unloadedCount++;
}
private final NativeLibraries nativeLibraries;
public NativeLibrariesTest() {
this.nativeLibraries = NativeLibraries.rawNativeLibraries(NativeLibraries.class, true);
}
/*
* Invoke by p.Test to load the same native library from different class loader
*/
public void run() {
load(true); // expect loading of native library succeed
}
public void runTest() throws Exception {
NativeLibrary nl1 = nativeLibraries.loadLibrary(LIB_NAME);
NativeLibrary nl2 = nativeLibraries.loadLibrary(LIB_NAME);
assertTrue(nl1 != null && nl2 != null, "fail to load library");
assertTrue(nl1 == nl2, nl1 + " != " + nl2);
assertTrue(loadedCount == 0, "Native library loaded. Expected: JNI_OnUnload not invoked");
assertTrue(unloadedCount == 0, "native library never unloaded");
// load successfully even from another loader
loadWithCustomLoader();
// unload the native library
nativeLibraries.unload(nl1);
assertTrue(unloadedCount == 0, "Native library unloaded. Expected: JNI_OnUnload not invoked");
// reload the native library and expect new NativeLibrary instance
NativeLibrary nl3 = nativeLibraries.loadLibrary(LIB_NAME);
assertTrue(nl1 != nl3, nl1 + " == " + nl3);
assertTrue(loadedCount == 0, "Native library loaded. Expected: JNI_OnUnload not invoked");
// load successfully even from another loader
loadWithCustomLoader();
}
public void unload() {
NativeLibrary nl = nativeLibraries.loadLibrary(LIB_NAME);
// unload the native library
nativeLibraries.unload(nl);
assertTrue(unloadedCount == 0, "Native library unloaded. Expected: JNI_OnUnload not invoked");
}
public void load(boolean succeed) {
NativeLibrary nl = nativeLibraries.loadLibrary(LIB_NAME);
if (succeed) {
assertTrue(nl != null, "fail to load library");
} else {
assertTrue(nl == null, "load library should fail");
}
}
/*
* Loads p.Test class with a new class loader and invokes the run() method.
* p.Test::run invokes NativeLibrariesTest::run
*/
private void loadWithCustomLoader() throws Exception {
TestLoader loader = new TestLoader();
Class<?> c = Class.forName("p.Test", true, loader);
Constructor<?> ctr = c.getConstructor(Runnable.class);
Runnable r = (Runnable) ctr.newInstance(this);
r.run();
}
static class TestLoader extends URLClassLoader {
static URL[] toURLs() {
try {
return new URL[] { Paths.get("classes").toUri().toURL() };
} catch (MalformedURLException e) {
throw new Error(e);
}
}
TestLoader() {
super("testloader", toURLs(), ClassLoader.getSystemClassLoader());
}
}
static void assertTrue(boolean value, String msg) {
if (!value) {
throw new AssertionError(msg);
}
}
}

@ -0,0 +1,65 @@
/*
* Copyright (c) 2020, 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 <stdio.h>
#include <stdlib.h>
#include "jni.h"
#include "jni_util.h"
static jclass test_class;
static jint current_jni_version = JNI_VERSION_10;
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
jclass cl;
jmethodID mid;
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
cl = (*env)->FindClass(env, "jdk/internal/loader/NativeLibrariesTest");
test_class = (*env)->NewGlobalRef(env, cl);
mid = (*env)->GetStaticMethodID(env, test_class, "nativeLibraryLoaded", "()V");
(*env)->CallStaticVoidMethod(env, test_class, mid);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->FatalError(env, "Exception thrown");
}
return current_jni_version;
}
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env;
jmethodID mid;
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
mid = (*env)->GetStaticMethodID(env, test_class, "nativeLibraryUnloaded", "()V");
(*env)->CallStaticVoidMethod(env, test_class, mid);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->FatalError(env, "Exception thrown");
}
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020, 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.
*/
package p;
public class Test implements Runnable {
private final Runnable r;
public Test(Runnable r) {
this.r = r;
}
/**
* Tests if the native library is loaded.
*/
public void run() {
r.run();
}
}