8228336: Refactor native library loading implementation

Reviewed-by: alanb, dholmes
This commit is contained in:
Mandy Chung 2020-03-12 11:54:11 -07:00
parent 36fac8c332
commit d5d6dc0caa
16 changed files with 817 additions and 617 deletions

View file

@ -39,14 +39,10 @@ import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
@ -59,13 +55,14 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.perf.PerfCounter;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.ClassLoaders;
import jdk.internal.loader.NativeLibrary;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.perf.PerfCounter;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.util.StaticProperty;
@ -2377,328 +2374,64 @@ public abstract class ClassLoader {
return null;
}
/**
* The inner class NativeLibrary denotes a loaded native library instance.
* Every classloader contains a vector of loaded native libraries in the
* private field {@code nativeLibraries}. The native libraries loaded
* into the system are entered into the {@code systemNativeLibraries}
* vector.
*
* <p> Every native library requires a particular version of JNI. This is
* denoted by the private {@code jniVersion} field. This field is set by
* the VM when it loads the library, and used by the VM to pass the correct
* version of JNI to the native methods. </p>
*
* @see ClassLoader
* @since 1.2
*/
static class NativeLibrary {
// the class from which the library is loaded, also indicates
// the loader this native library belongs.
final Class<?> fromClass;
// the canonicalized name of the native library.
// or static library name
final String name;
// Indicates if the native library is linked into the VM
final boolean isBuiltin;
// opaque handle to native library, used in native code.
long handle;
// the version of JNI environment the native library requires.
int jniVersion;
native boolean load0(String name, boolean isBuiltin);
native long findEntry(String name);
NativeLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
this.name = name;
this.fromClass = fromClass;
this.isBuiltin = isBuiltin;
}
/*
* Loads the native library and registers for cleanup when its
* associated class loader is unloaded
*/
boolean load() {
if (handle != 0) {
throw new InternalError("Native library " + name + " has been loaded");
}
if (!load0(name, isBuiltin)) return false;
// register the class loader for cleanup when unloaded
// builtin class loaders are never unloaded
ClassLoader loader = fromClass.getClassLoader();
if (loader != null &&
loader != getBuiltinPlatformClassLoader() &&
loader != getBuiltinAppClassLoader()) {
CleanerFactory.cleaner().register(loader,
new Unloader(name, handle, isBuiltin));
}
return true;
}
static boolean loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
ClassLoader loader =
fromClass == null ? null : fromClass.getClassLoader();
synchronized (loadedLibraryNames) {
Map<String, NativeLibrary> libs =
loader != null ? loader.nativeLibraries() : systemNativeLibraries();
if (libs.containsKey(name)) {
return true;
}
if (loadedLibraryNames.contains(name)) {
throw new UnsatisfiedLinkError("Native Library " + name +
" already loaded in another classloader");
}
/*
* When a library is being loaded, JNI_OnLoad function can cause
* another loadLibrary invocation that should succeed.
*
* We use a static stack to hold the list of libraries we are
* loading because this can happen only when called by the
* same thread because this block is synchronous.
*
* If there is a pending load operation for the library, we
* immediately return success; otherwise, we raise
* UnsatisfiedLinkError.
*/
for (NativeLibrary lib : nativeLibraryContext) {
if (name.equals(lib.name)) {
if (loader == lib.fromClass.getClassLoader()) {
return true;
} else {
throw new UnsatisfiedLinkError("Native Library " +
name + " is being loaded in another classloader");
}
}
}
NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
// load the native library
nativeLibraryContext.push(lib);
try {
if (!lib.load()) return false;
} finally {
nativeLibraryContext.pop();
}
// register the loaded native library
loadedLibraryNames.add(name);
libs.put(name, lib);
}
return true;
}
// Invoked in the VM to determine the context class in JNI_OnLoad
// and JNI_OnUnload
static Class<?> getFromClass() {
return nativeLibraryContext.peek().fromClass;
}
// native libraries being loaded
static Deque<NativeLibrary> nativeLibraryContext = new ArrayDeque<>(8);
/*
* The run() method will be invoked when this class loader becomes
* phantom reachable to unload the native library.
*/
static class Unloader implements Runnable {
// This represents the context when a native library is unloaded
// and getFromClass() will return null,
static final NativeLibrary UNLOADER =
new NativeLibrary(null, "dummy", false);
final String name;
final long handle;
final boolean isBuiltin;
Unloader(String name, long handle, boolean isBuiltin) {
if (handle == 0) {
throw new IllegalArgumentException(
"Invalid handle for native library " + name);
}
this.name = name;
this.handle = handle;
this.isBuiltin = isBuiltin;
}
@Override
public void run() {
synchronized (loadedLibraryNames) {
/* remove the native library name */
loadedLibraryNames.remove(name);
nativeLibraryContext.push(UNLOADER);
try {
unload(name, isBuiltin, handle);
} finally {
nativeLibraryContext.pop();
}
}
}
}
// JNI FindClass expects the caller class if invoked from JNI_OnLoad
// and JNI_OnUnload is NativeLibrary class
static native void unload(String name, boolean isBuiltin, long handle);
}
/**
* Holds system and user library paths derived from the
* {@code java.library.path} and {@code sun.boot.library.path} system
* properties. The system properties are eagerly read at bootstrap, then
* lazily parsed on first use to avoid initialization ordering issues.
*/
private static class LibraryPaths {
static final String[] USER =
ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
static final String[] SYS =
ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
}
private final NativeLibraries libraries = new NativeLibraries(this);
// Invoked in the java.lang.Runtime class to implement load and loadLibrary.
static void loadLibrary(Class<?> fromClass, String name,
boolean isAbsolute) {
ClassLoader loader =
(fromClass == null) ? null : fromClass.getClassLoader();
static NativeLibrary loadLibrary(Class<?> fromClass, File file) {
ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
NativeLibraries libs = loader != null ? loader.libraries : BootLoader.getNativeLibraries();
NativeLibrary nl = libs.loadLibrary(fromClass, file);
if (nl != null) {
return nl;
}
throw new UnsatisfiedLinkError("Can't load library: " + file);
}
static NativeLibrary loadLibrary(Class<?> fromClass, String name) {
ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
if (loader == null) {
NativeLibrary nl = BootLoader.getNativeLibraries().loadLibrary(fromClass, name);
if (nl != null) {
return nl;
}
throw new UnsatisfiedLinkError("no " + name +
" in system library path: " + StaticProperty.sunBootLibraryPath());
}
if (isAbsolute) {
if (loadLibrary0(fromClass, new File(name))) {
return;
}
throw new UnsatisfiedLinkError("Can't load library: " + name);
}
if (loader != null) {
String libfilename = loader.findLibrary(name);
if (libfilename != null) {
File libfile = new File(libfilename);
if (!libfile.isAbsolute()) {
throw new UnsatisfiedLinkError(
NativeLibraries libs = loader.libraries;
// First load from the file returned from ClassLoader::findLibrary, if found.
String libfilename = loader.findLibrary(name);
if (libfilename != null) {
File libfile = new File(libfilename);
if (!libfile.isAbsolute()) {
throw new UnsatisfiedLinkError(
"ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
}
if (loadLibrary0(fromClass, libfile)) {
return;
}
throw new UnsatisfiedLinkError("Can't load " + libfilename);
}
NativeLibrary nl = libs.loadLibrary(fromClass, libfile);
if (nl != null) {
return nl;
}
throw new UnsatisfiedLinkError("Can't load " + libfilename);
}
for (String sysPath : LibraryPaths.SYS) {
File libfile = new File(sysPath, System.mapLibraryName(name));
if (loadLibrary0(fromClass, libfile)) {
return;
}
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
if (libfile != null && loadLibrary0(fromClass, libfile)) {
return;
}
}
if (loader != null) {
for (String userPath : LibraryPaths.USER) {
File libfile = new File(userPath, System.mapLibraryName(name));
if (loadLibrary0(fromClass, libfile)) {
return;
}
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
if (libfile != null && loadLibrary0(fromClass, libfile)) {
return;
}
}
// Then load from system library path and java library path
NativeLibrary nl = libs.loadLibrary(fromClass, name);
if (nl != null) {
return nl;
}
// Oops, it failed
throw new UnsatisfiedLinkError("no " + name +
" in java.library.path: " + Arrays.toString(LibraryPaths.USER));
}
private static native String findBuiltinLib(String name);
private static boolean loadLibrary0(Class<?> fromClass, final File file) {
// Check to see if we're attempting to access a static library
String name = findBuiltinLib(file.getName());
boolean isBuiltin = (name != null);
if (!isBuiltin) {
name = AccessController.doPrivileged(
new PrivilegedAction<>() {
public String run() {
try {
return file.exists() ? file.getCanonicalPath() : null;
} catch (IOException e) {
return null;
}
}
});
if (name == null) {
return false;
}
}
return NativeLibrary.loadLibrary(fromClass, name, isBuiltin);
" in java.library.path: " + StaticProperty.javaLibraryPath());
}
/*
* Invoked in the VM class linking code.
*/
private static long findNative(ClassLoader loader, String entryName) {
Map<String, NativeLibrary> libs =
loader != null ? loader.nativeLibraries() : systemNativeLibraries();
if (libs.isEmpty())
return 0;
// the native libraries map may be updated in another thread
// when a native library is being loaded. No symbol will be
// searched from it yet.
for (NativeLibrary lib : libs.values()) {
long entry = lib.findEntry(entryName);
if (entry != 0) return entry;
if (loader == null) {
return BootLoader.getNativeLibraries().find(entryName);
} else {
return loader.libraries.find(entryName);
}
return 0;
}
// All native library names we've loaded.
// This also serves as the lock to obtain nativeLibraries
// and write to nativeLibraryContext.
private static final Set<String> loadedLibraryNames = new HashSet<>();
// Native libraries belonging to system classes.
private static volatile Map<String, NativeLibrary> systemNativeLibraries;
// Native libraries associated with the class loader.
private volatile Map<String, NativeLibrary> nativeLibraries;
/*
* Returns the native libraries map associated with bootstrap class loader
* This method will create the map at the first time when called.
*/
private static Map<String, NativeLibrary> systemNativeLibraries() {
Map<String, NativeLibrary> libs = systemNativeLibraries;
if (libs == null) {
synchronized (loadedLibraryNames) {
libs = systemNativeLibraries;
if (libs == null) {
libs = systemNativeLibraries = new ConcurrentHashMap<>();
}
}
}
return libs;
}
/*
* Returns the native libraries map associated with this class loader
* This method will create the map at the first time when called.
*/
private Map<String, NativeLibrary> nativeLibraries() {
Map<String, NativeLibrary> libs = nativeLibraries;
if (libs == null) {
synchronized (loadedLibraryNames) {
libs = nativeLibraries;
if (libs == null) {
libs = nativeLibraries = new ConcurrentHashMap<>();
}
}
}
return libs;
}
// -- Assertion management --

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -36,6 +36,7 @@ import java.util.Optional;
import java.util.StringTokenizer;
import jdk.internal.access.SharedSecrets;
import jdk.internal.loader.NativeLibrary;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@ -738,11 +739,12 @@ public class Runtime {
if (security != null) {
security.checkLink(filename);
}
if (!(new File(filename).isAbsolute())) {
File file = new File(filename);
if (!file.isAbsolute()) {
throw new UnsatisfiedLinkError(
"Expecting an absolute path of the library: " + filename);
}
ClassLoader.loadLibrary(fromClass, filename, true);
ClassLoader.loadLibrary(fromClass, file);
}
/**
@ -804,7 +806,7 @@ public class Runtime {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
ClassLoader.loadLibrary(fromClass, libname, false);
ClassLoader.loadLibrary(fromClass, libname);
}
/**

View file

@ -2276,11 +2276,6 @@ public final class System {
public void setCause(Throwable t, Throwable cause) {
t.setCause(cause);
}
public void loadLibrary(Class<?> caller, String library) {
assert library.indexOf(java.io.File.separatorChar) < 0;
ClassLoader.loadLibrary(caller, library, false);
}
});
}
}

View file

@ -34,7 +34,6 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.security.AccessControlContext;
import java.security.ProtectionDomain;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -312,12 +311,4 @@ public interface JavaLangAccess {
* @param cause set t's cause to new value
*/
void setCause(Throwable t, Throwable cause);
/**
* Privileged System.loadLibrary
*
* @param caller on behalf of which the library is being loaded
* @param library name of the library to load
*/
void loadLibrary(Class<?> caller, String library);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -71,6 +71,10 @@ public class BootLoader {
private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
= new ConcurrentHashMap<>();
// native libraries loaded by the boot class loader
private static final NativeLibraries NATIVE_LIBS
= new NativeLibraries(null);
/**
* Returns the unnamed module for the boot loader.
*/
@ -92,6 +96,13 @@ public class BootLoader {
return CLASS_LOADER_VALUE_MAP;
}
/**
* Returns NativeLibraries for the boot class loader.
*/
public static NativeLibraries getNativeLibraries() {
return NATIVE_LIBS;
}
/**
* Returns {@code true} if there is a class path associated with the
* BootLoader.
@ -129,19 +140,18 @@ public class BootLoader {
}
/**
* Loads a library from the system path.
* Loads a native library from the system library path.
*/
public static void loadLibrary(String library) {
public static void loadLibrary(String name) {
if (System.getSecurityManager() == null) {
SharedSecrets.getJavaLangAccess().loadLibrary(BootLoader.class, library);
BootLoader.getNativeLibraries().loadLibrary(name);
} else {
AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public Void run() {
SharedSecrets.getJavaLangAccess().loadLibrary(BootLoader.class, library);
return null;
}
});
AccessController.doPrivileged(new java.security.PrivilegedAction<>() {
public Void run() {
BootLoader.getNativeLibraries().loadLibrary(name);
return null;
}
});
}
}

View file

@ -0,0 +1,378 @@
/*
* 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. 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.
*/
package jdk.internal.loader;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.util.StaticProperty;
import java.io.File;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Native libraries are loaded via {@link System#loadLibrary(String)},
* {@link System#load(String)}, {@link Runtime#loadLibrary(String)} and
* {@link Runtime#load(String)}. They are caller-sensitive.
*
* Each class loader has a NativeLibraries instance to register all of its
* loaded native libraries. System::loadLibrary (and other APIs) only
* allows a native library to be loaded by one class loader, i.e. one
* NativeLibraries instance. Any attempt to load a native library that
* has already been loaded by a class loader with another class loader
* will fail.
*/
public final class NativeLibraries {
private final Map<String, NativeLibrary> 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).
private final boolean searchJavaLibraryPath;
public 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 = caller;
this.searchJavaLibraryPath = searchJavaLibraryPath;
}
/*
* Find the address of the given symbol name from the native libraries
* loaded in this NativeLibraries instance.
*/
public long find(String name) {
if (libraries.isEmpty())
return 0;
// the native libraries map may be updated in another thread
// when a native library is being loaded. No symbol will be
// searched from it yet.
for (NativeLibrary lib : libraries.values()) {
long entry = lib.find(name);
if (entry != 0) return entry;
}
return 0;
}
/*
* Load a native library from the given file. Returns null if file does not exist.
*
* @param fromClass the caller class calling System::loadLibrary
* @param file the path of the native library
* @throws UnsatisfiedLinkError if any error in loading the native library
*/
public NativeLibrary loadLibrary(Class<?> fromClass, File file) {
// Check to see if we're attempting to access a static library
String name = findBuiltinLib(file.getName());
boolean isBuiltin = (name != null);
if (!isBuiltin) {
name = AccessController.doPrivileged(new PrivilegedAction<>() {
public String run() {
try {
return file.exists() ? file.getCanonicalPath() : null;
} catch (IOException e) {
return null;
}
}
});
if (name == null) {
return null;
}
}
return loadLibrary(fromClass, name, isBuiltin);
}
/**
* Returns a NativeLibrary of the given name.
*
* @param fromClass the caller class calling System::loadLibrary
* @param name library name
* @param isBuiltin built-in library
* @throws UnsatisfiedLinkError if the native library has already been loaded
* and registered in another NativeLibraries
*/
private NativeLibrary loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
if (this.loader != loader) {
throw new InternalError(fromClass.getName() + " not allowed to load library");
}
synchronized (loadedLibraryNames) {
// find if this library has already been loaded and registered in this NativeLibraries
NativeLibrary cached = libraries.get(name);
if (cached != null) {
return cached;
}
// cannot be loaded by other class loaders
if (loadedLibraryNames.contains(name)) {
throw new UnsatisfiedLinkError("Native Library " + name +
" already loaded in another classloader");
}
/*
* When a library is being loaded, JNI_OnLoad function can cause
* another loadLibrary invocation that should succeed.
*
* We use a static stack to hold the list of libraries we are
* loading because this can happen only when called by the
* same thread because this block is synchronous.
*
* If there is a pending load operation for the library, we
* immediately return success; otherwise, we raise
* UnsatisfiedLinkError.
*/
for (NativeLibraryImpl lib : nativeLibraryContext) {
if (name.equals(lib.name())) {
if (loader == lib.fromClass.getClassLoader()) {
return lib;
} else {
throw new UnsatisfiedLinkError("Native Library " +
name + " is being loaded in another classloader");
}
}
}
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin);
// load the native library
nativeLibraryContext.push(lib);
try {
if (!lib.open()) return null;
} finally {
nativeLibraryContext.pop();
}
// register the loaded native library
loadedLibraryNames.add(name);
libraries.put(name, lib);
return lib;
}
}
/**
* Loads a native library from the system library path and java library path.
*
* @param name library name
*
* @throws UnsatisfiedLinkError if the native library has already been loaded
* and registered in another NativeLibraries
*/
public NativeLibrary loadLibrary(String name) {
assert name.indexOf(File.separatorChar) < 0;
assert caller != null;
return loadLibrary(caller, name);
}
/**
* Loads a native library from the system library path and java library path.
*
* @param name library name
* @param fromClass the caller class calling System::loadLibrary
*
* @throws UnsatisfiedLinkError if the native library has already been loaded
* and registered in another NativeLibraries
*/
public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
assert name.indexOf(File.separatorChar) < 0;
NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
if (lib == null && searchJavaLibraryPath) {
lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
}
return lib;
}
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
for (String path : paths) {
File libfile = new File(path, System.mapLibraryName(name));
NativeLibrary nl = loadLibrary(fromClass, libfile);
if (nl != null) {
return nl;
}
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
if (libfile != null) {
nl = loadLibrary(fromClass, libfile);
if (nl != null) {
return nl;
}
}
}
return null;
}
/**
* NativeLibraryImpl denotes a loaded native library instance.
* Each NativeLibraries contains a map of loaded native libraries in the
* private field {@code libraries}.
*
* Every native library requires a particular version of JNI. This is
* denoted by the private {@code jniVersion} field. This field is set by
* the VM when it loads the library, and used by the VM to pass the correct
* version of JNI to the native methods.
*/
static class NativeLibraryImpl implements NativeLibrary {
// the class from which the library is loaded, also indicates
// the loader this native library belongs.
final Class<?> fromClass;
// the canonicalized name of the native library.
// or static library name
final String name;
// Indicates if the native library is linked into the VM
final boolean isBuiltin;
// 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) {
this.fromClass = fromClass;
this.name = name;
this.isBuiltin = isBuiltin;
}
@Override
public String name() {
return name;
}
@Override
public long find(String name) {
return findEntry0(this, name);
}
/*
* Loads the native library and registers for cleanup when its
* associated class loader is unloaded
*/
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;
}
}
/*
* The run() method will be invoked when this class loader becomes
* phantom reachable to unload the native library.
*/
static class Unloader implements Runnable {
// This represents the context when a native library is unloaded
// and getFromClass() will return null,
static final NativeLibraryImpl UNLOADER =
new NativeLibraryImpl(null, "dummy", false);
final String name;
final long handle;
final boolean isBuiltin;
Unloader(String name, long handle, boolean isBuiltin) {
if (handle == 0) {
throw new IllegalArgumentException(
"Invalid handle for native library " + name);
}
this.name = name;
this.handle = handle;
this.isBuiltin = isBuiltin;
}
@Override
public void run() {
synchronized (NativeLibraries.loadedLibraryNames) {
/* remove the native library name */
NativeLibraries.loadedLibraryNames.remove(name);
NativeLibraries.nativeLibraryContext.push(UNLOADER);
try {
unload(name, isBuiltin, handle);
} finally {
NativeLibraries.nativeLibraryContext.pop();
}
}
}
}
/*
* Holds system and user library paths derived from the
* {@code java.library.path} and {@code sun.boot.library.path} system
* properties. The system properties are eagerly read at bootstrap, then
* lazily parsed on first use to avoid initialization ordering issues.
*/
static class LibraryPaths {
// The paths searched for libraries
static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
}
// All native libraries we've loaded.
// This also serves as the lock to obtain nativeLibraries
// and write to nativeLibraryContext.
private static final Set<String> loadedLibraryNames = new HashSet<>();
// native libraries being loaded
private static Deque<NativeLibraryImpl> nativeLibraryContext = new ArrayDeque<>(8);
// Invoked in the VM to determine the context class in JNI_OnLoad
// and JNI_OnUnload
private static Class<?> getFromClass() {
if (nativeLibraryContext.isEmpty()) { // only default library
return Object.class;
}
return nativeLibraryContext.peek().fromClass;
}
// 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 String findBuiltinLib(String name);
private static native long findEntry0(NativeLibraryImpl lib, String name);
}

View file

@ -0,0 +1,52 @@
/*
* 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. 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.
*/
package jdk.internal.loader;
/**
* NativeLibrary represents a loaded native library instance.
*/
public interface NativeLibrary {
String name();
/*
* Finds the address of the entry of the given name. Returns 0
* if not found.
*/
long find(String name);
/*
* Finds the address of the entry of the given name.
*
* @throws NoSuchMethodException if the named entry is not found.
*/
default long lookup(String name) throws NoSuchMethodException {
long addr = find(name);
if (0 == addr) {
throw new NoSuchMethodException("Cannot find symbol " + name + " in library " + name());
}
return addr;
}
}