mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-15 08:34:30 +02:00
8228336: Refactor native library loading implementation
Reviewed-by: alanb, dholmes
This commit is contained in:
parent
36fac8c332
commit
d5d6dc0caa
16 changed files with 817 additions and 617 deletions
|
@ -65,7 +65,6 @@
|
|||
template(java_lang_Cloneable, "java/lang/Cloneable") \
|
||||
template(java_lang_Throwable, "java/lang/Throwable") \
|
||||
template(java_lang_ClassLoader, "java/lang/ClassLoader") \
|
||||
template(java_lang_ClassLoader_NativeLibrary, "java/lang/ClassLoader\x024NativeLibrary") \
|
||||
template(java_lang_ThreadDeath, "java/lang/ThreadDeath") \
|
||||
template(java_lang_Boolean, "java/lang/Boolean") \
|
||||
template(java_lang_Character, "java/lang/Character") \
|
||||
|
@ -130,6 +129,7 @@
|
|||
template(java_util_Iterator, "java/util/Iterator") \
|
||||
template(java_lang_Record, "java/lang/Record") \
|
||||
\
|
||||
template(jdk_internal_loader_NativeLibraries, "jdk/internal/loader/NativeLibraries") \
|
||||
template(jdk_internal_loader_ClassLoaders_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \
|
||||
template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \
|
||||
\
|
||||
|
|
|
@ -397,13 +397,13 @@ JNI_ENTRY(jclass, jni_FindClass(JNIEnv *env, const char *name))
|
|||
// Special handling to make sure JNI_OnLoad and JNI_OnUnload are executed
|
||||
// in the correct class context.
|
||||
if (k->class_loader() == NULL &&
|
||||
k->name() == vmSymbols::java_lang_ClassLoader_NativeLibrary()) {
|
||||
k->name() == vmSymbols::jdk_internal_loader_NativeLibraries()) {
|
||||
JavaValue result(T_OBJECT);
|
||||
JavaCalls::call_static(&result, k,
|
||||
vmSymbols::getFromClass_name(),
|
||||
vmSymbols::void_class_signature(),
|
||||
CHECK_NULL);
|
||||
// When invoked from JNI_OnLoad, NativeLibrary::getFromClass returns
|
||||
// When invoked from JNI_OnLoad, NativeLibraries::getFromClass returns
|
||||
// a non-NULL Class object. When invoked from JNI_OnUnload,
|
||||
// it will return NULL to indicate no context.
|
||||
oop mirror = (oop) result.get_jobject();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 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
|
||||
|
@ -159,7 +159,7 @@ address NativeLookup::lookup_style(const methodHandle& method, char* pure_name,
|
|||
|
||||
// If the loader is null we have a system class, so we attempt a lookup in
|
||||
// the native Java library. This takes care of any bootstrapping problems.
|
||||
// Note: It is critical for bootstrapping that Java_java_lang_ClassLoader_00024NativeLibrary_find
|
||||
// Note: It is critical for bootstrapping that Java_java_lang_ClassLoader_findNative
|
||||
// gets found the first time around - otherwise an infinite loop can occure. This is
|
||||
// another VM/library dependency
|
||||
Handle loader(THREAD, method->method_holder()->class_loader());
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
|
@ -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 --
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 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
|
||||
|
@ -32,7 +32,6 @@
|
|||
#include "jvm.h"
|
||||
#include "check_classname.h"
|
||||
#include "java_lang_ClassLoader.h"
|
||||
#include "java_lang_ClassLoader_NativeLibrary.h"
|
||||
#include <string.h>
|
||||
|
||||
static JNINativeMethod methods[] = {
|
||||
|
@ -254,266 +253,3 @@ Java_java_lang_ClassLoader_findLoadedClass0(JNIEnv *env, jobject loader,
|
|||
return JVM_FindLoadedClass(env, loader, name);
|
||||
}
|
||||
}
|
||||
|
||||
static jfieldID handleID;
|
||||
static jfieldID jniVersionID;
|
||||
static void *procHandle;
|
||||
|
||||
static jboolean initIDs(JNIEnv *env)
|
||||
{
|
||||
if (handleID == 0) {
|
||||
jclass this =
|
||||
(*env)->FindClass(env, "java/lang/ClassLoader$NativeLibrary");
|
||||
if (this == 0)
|
||||
return JNI_FALSE;
|
||||
handleID = (*env)->GetFieldID(env, this, "handle", "J");
|
||||
if (handleID == 0)
|
||||
return JNI_FALSE;
|
||||
jniVersionID = (*env)->GetFieldID(env, this, "jniVersion", "I");
|
||||
if (jniVersionID == 0)
|
||||
return JNI_FALSE;
|
||||
procHandle = getProcessHandle();
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
typedef jint (JNICALL *JNI_OnLoad_t)(JavaVM *, void *);
|
||||
typedef void (JNICALL *JNI_OnUnload_t)(JavaVM *, void *);
|
||||
|
||||
/*
|
||||
* Support for finding JNI_On(Un)Load_<lib_name> if it exists.
|
||||
* If cname == NULL then just find normal JNI_On(Un)Load entry point
|
||||
*/
|
||||
static void *findJniFunction(JNIEnv *env, void *handle,
|
||||
const char *cname, jboolean isLoad) {
|
||||
const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
|
||||
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
||||
const char **syms;
|
||||
int symsLen;
|
||||
void *entryName = NULL;
|
||||
char *jniFunctionName;
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
// Check for JNI_On(Un)Load<_libname> function
|
||||
if (isLoad) {
|
||||
syms = onLoadSymbols;
|
||||
symsLen = sizeof(onLoadSymbols) / sizeof(char *);
|
||||
} else {
|
||||
syms = onUnloadSymbols;
|
||||
symsLen = sizeof(onUnloadSymbols) / sizeof(char *);
|
||||
}
|
||||
for (i = 0; i < symsLen; i++) {
|
||||
// cname + sym + '_' + '\0'
|
||||
if ((len = (cname != NULL ? strlen(cname) : 0) + strlen(syms[i]) + 2) >
|
||||
FILENAME_MAX) {
|
||||
goto done;
|
||||
}
|
||||
jniFunctionName = malloc(len);
|
||||
if (jniFunctionName == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, NULL);
|
||||
goto done;
|
||||
}
|
||||
buildJniFunctionName(syms[i], cname, jniFunctionName);
|
||||
entryName = JVM_FindLibraryEntry(handle, jniFunctionName);
|
||||
free(jniFunctionName);
|
||||
if(entryName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return entryName;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: java_lang_ClassLoader_NativeLibrary
|
||||
* Method: load0
|
||||
* Signature: (Ljava/lang/String;Z)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_java_lang_ClassLoader_00024NativeLibrary_load0
|
||||
(JNIEnv *env, jobject this, jstring name, jboolean isBuiltin)
|
||||
{
|
||||
const char *cname;
|
||||
jint jniVersion;
|
||||
jthrowable cause;
|
||||
void * handle;
|
||||
jboolean loaded = JNI_FALSE;
|
||||
|
||||
if (!initIDs(env))
|
||||
return JNI_FALSE;
|
||||
|
||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||
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);
|
||||
} 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, this, jniVersionID, jniVersion);
|
||||
} else {
|
||||
cause = (*env)->ExceptionOccurred(env);
|
||||
if (cause) {
|
||||
(*env)->ExceptionClear(env);
|
||||
(*env)->SetLongField(env, this, handleID, (jlong)0);
|
||||
(*env)->Throw(env, cause);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
(*env)->SetLongField(env, this, handleID, ptr_to_jlong(handle));
|
||||
loaded = JNI_TRUE;
|
||||
|
||||
done:
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: java_lang_ClassLoader_NativeLibrary
|
||||
* Method: unload
|
||||
* Signature: (Ljava/lang/String;ZJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_ClassLoader_00024NativeLibrary_unload
|
||||
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jlong address)
|
||||
{
|
||||
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
||||
void *handle;
|
||||
JNI_OnUnload_t JNI_OnUnload;
|
||||
const char *cname;
|
||||
|
||||
if (!initIDs(env))
|
||||
return;
|
||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||
if (cname == NULL) {
|
||||
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 (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
}
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: java_lang_ClassLoader_NativeLibrary
|
||||
* Method: findEntry
|
||||
* Signature: (Ljava/lang/String;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ClassLoader_00024NativeLibrary_findEntry
|
||||
(JNIEnv *env, jobject this, jstring name)
|
||||
{
|
||||
jlong handle;
|
||||
const char *cname;
|
||||
jlong res;
|
||||
|
||||
if (!initIDs(env))
|
||||
return jlong_zero;
|
||||
|
||||
handle = (*env)->GetLongField(env, this, handleID);
|
||||
cname = (*env)->GetStringUTFChars(env, name, 0);
|
||||
if (cname == 0)
|
||||
return jlong_zero;
|
||||
res = ptr_to_jlong(JVM_FindLibraryEntry(jlong_to_ptr(handle), cname));
|
||||
(*env)->ReleaseStringUTFChars(env, name, cname);
|
||||
return res;
|
||||
}
|
||||
/*
|
||||
* Class: java_lang_ClassLoader
|
||||
* Method: findBuiltinLib
|
||||
* Signature: (Ljava/lang/String;)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_java_lang_ClassLoader_findBuiltinLib
|
||||
(JNIEnv *env, jclass cls, jstring name)
|
||||
{
|
||||
const char *cname;
|
||||
char *libName;
|
||||
size_t prefixLen = strlen(JNI_LIB_PREFIX);
|
||||
size_t suffixLen = strlen(JNI_LIB_SUFFIX);
|
||||
size_t len;
|
||||
jstring lib;
|
||||
void *ret;
|
||||
const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
|
||||
|
||||
if (name == NULL) {
|
||||
JNU_ThrowInternalError(env, "NULL filename for native library");
|
||||
return NULL;
|
||||
}
|
||||
procHandle = getProcessHandle();
|
||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||
if (cname == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Copy name Skipping PREFIX
|
||||
len = strlen(cname);
|
||||
if (len <= (prefixLen+suffixLen)) {
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
return NULL;
|
||||
}
|
||||
libName = malloc(len + 1); //+1 for null if prefix+suffix == 0
|
||||
if (libName == NULL) {
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
JNU_ThrowOutOfMemoryError(env, NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (len > prefixLen) {
|
||||
strcpy(libName, cname+prefixLen);
|
||||
}
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
|
||||
// Strip SUFFIX
|
||||
libName[strlen(libName)-suffixLen] = '\0';
|
||||
|
||||
// Check for JNI_OnLoad_libname function
|
||||
ret = findJniFunction(env, procHandle, libName, JNI_TRUE);
|
||||
if (ret != NULL) {
|
||||
lib = JNU_NewStringPlatform(env, libName);
|
||||
free(libName);
|
||||
return lib;
|
||||
}
|
||||
free(libName);
|
||||
return NULL;
|
||||
}
|
||||
|
|
303
src/java.base/share/native/libjava/NativeLibraries.c
Normal file
303
src/java.base/share/native/libjava/NativeLibraries.c
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
#include "jlong.h"
|
||||
#include "jvm.h"
|
||||
#include "jdk_internal_loader_NativeLibraries.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef jint (JNICALL *JNI_OnLoad_t)(JavaVM *, void *);
|
||||
typedef void (JNICALL *JNI_OnUnload_t)(JavaVM *, void *);
|
||||
|
||||
static jfieldID handleID;
|
||||
static jfieldID jniVersionID;
|
||||
static void *procHandle;
|
||||
|
||||
|
||||
static jboolean initIDs(JNIEnv *env)
|
||||
{
|
||||
if (handleID == 0) {
|
||||
jclass this =
|
||||
(*env)->FindClass(env, "jdk/internal/loader/NativeLibraries$NativeLibraryImpl");
|
||||
if (this == 0)
|
||||
return JNI_FALSE;
|
||||
handleID = (*env)->GetFieldID(env, this, "handle", "J");
|
||||
if (handleID == 0)
|
||||
return JNI_FALSE;
|
||||
jniVersionID = (*env)->GetFieldID(env, this, "jniVersion", "I");
|
||||
if (jniVersionID == 0)
|
||||
return JNI_FALSE;
|
||||
procHandle = getProcessHandle();
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Support for finding JNI_On(Un)Load_<lib_name> if it exists.
|
||||
* If cname == NULL then just find normal JNI_On(Un)Load entry point
|
||||
*/
|
||||
static void *findJniFunction(JNIEnv *env, void *handle,
|
||||
const char *cname, jboolean isLoad) {
|
||||
const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
|
||||
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
||||
const char **syms;
|
||||
int symsLen;
|
||||
void *entryName = NULL;
|
||||
char *jniFunctionName;
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
// Check for JNI_On(Un)Load<_libname> function
|
||||
if (isLoad) {
|
||||
syms = onLoadSymbols;
|
||||
symsLen = sizeof(onLoadSymbols) / sizeof(char *);
|
||||
} else {
|
||||
syms = onUnloadSymbols;
|
||||
symsLen = sizeof(onUnloadSymbols) / sizeof(char *);
|
||||
}
|
||||
for (i = 0; i < symsLen; i++) {
|
||||
// cname + sym + '_' + '\0'
|
||||
if ((len = (cname != NULL ? strlen(cname) : 0) + strlen(syms[i]) + 2) >
|
||||
FILENAME_MAX) {
|
||||
goto done;
|
||||
}
|
||||
jniFunctionName = malloc(len);
|
||||
if (jniFunctionName == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, NULL);
|
||||
goto done;
|
||||
}
|
||||
buildJniFunctionName(syms[i], cname, jniFunctionName);
|
||||
entryName = JVM_FindLibraryEntry(handle, jniFunctionName);
|
||||
free(jniFunctionName);
|
||||
if(entryName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return entryName;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: jdk_internal_loader_NativeLibraries
|
||||
* Method: load
|
||||
* Signature: (Ljava/lang/String;Z)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_jdk_internal_loader_NativeLibraries_load
|
||||
(JNIEnv *env, jobject this, jobject lib, jstring name, jboolean isBuiltin)
|
||||
{
|
||||
const char *cname;
|
||||
jint jniVersion;
|
||||
jthrowable cause;
|
||||
void * handle;
|
||||
jboolean loaded = JNI_FALSE;
|
||||
|
||||
if (!initIDs(env))
|
||||
return JNI_FALSE;
|
||||
|
||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||
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);
|
||||
} 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 {
|
||||
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;
|
||||
|
||||
done:
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: jdk_internal_loader_NativeLibraries
|
||||
* Method: unload
|
||||
* Signature: (Ljava/lang/String;ZJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_jdk_internal_loader_NativeLibraries_unload
|
||||
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jlong address)
|
||||
{
|
||||
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
||||
void *handle;
|
||||
JNI_OnUnload_t JNI_OnUnload;
|
||||
const char *cname;
|
||||
|
||||
if (!initIDs(env))
|
||||
return;
|
||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||
if (cname == NULL) {
|
||||
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 (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
}
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: jdk_internal_loader_NativeLibraries
|
||||
* Method: findEntry0
|
||||
* Signature: (Ljava/lang/String;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_jdk_internal_loader_NativeLibraries_findEntry0
|
||||
(JNIEnv *env, jobject this, jobject lib, jstring name)
|
||||
{
|
||||
jlong handle;
|
||||
const char *cname;
|
||||
jlong res;
|
||||
|
||||
if (!initIDs(env))
|
||||
return jlong_zero;
|
||||
|
||||
handle = (*env)->GetLongField(env, lib, handleID);
|
||||
cname = (*env)->GetStringUTFChars(env, name, 0);
|
||||
if (cname == 0)
|
||||
return jlong_zero;
|
||||
res = ptr_to_jlong(JVM_FindLibraryEntry(jlong_to_ptr(handle), cname));
|
||||
(*env)->ReleaseStringUTFChars(env, name, cname);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: jdk_internal_loader_NativeLibraries
|
||||
* Method: findBuiltinLib
|
||||
* Signature: (Ljava/lang/String;)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_jdk_internal_loader_NativeLibraries_findBuiltinLib
|
||||
(JNIEnv *env, jclass cls, jstring name)
|
||||
{
|
||||
const char *cname;
|
||||
char *libName;
|
||||
size_t prefixLen = strlen(JNI_LIB_PREFIX);
|
||||
size_t suffixLen = strlen(JNI_LIB_SUFFIX);
|
||||
size_t len;
|
||||
jstring lib;
|
||||
void *ret;
|
||||
const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
|
||||
|
||||
if (name == NULL) {
|
||||
JNU_ThrowInternalError(env, "NULL filename for native library");
|
||||
return NULL;
|
||||
}
|
||||
procHandle = getProcessHandle();
|
||||
cname = JNU_GetStringPlatformChars(env, name, 0);
|
||||
if (cname == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Copy name Skipping PREFIX
|
||||
len = strlen(cname);
|
||||
if (len <= (prefixLen+suffixLen)) {
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
return NULL;
|
||||
}
|
||||
libName = malloc(len + 1); //+1 for null if prefix+suffix == 0
|
||||
if (libName == NULL) {
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
JNU_ThrowOutOfMemoryError(env, NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (len > prefixLen) {
|
||||
strcpy(libName, cname+prefixLen);
|
||||
}
|
||||
JNU_ReleaseStringPlatformChars(env, name, cname);
|
||||
|
||||
// Strip SUFFIX
|
||||
libName[strlen(libName)-suffixLen] = '\0';
|
||||
|
||||
// Check for JNI_OnLoad_libname function
|
||||
ret = findJniFunction(env, procHandle, libName, JNI_TRUE);
|
||||
if (ret != NULL) {
|
||||
lib = JNU_NewStringPlatform(env, libName);
|
||||
free(libName);
|
||||
return lib;
|
||||
}
|
||||
free(libName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
|
@ -23,7 +23,7 @@
|
|||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
* Quoted entries should get unquoted on Windows.
|
||||
* Empty entries should be replaced with dot.
|
||||
* @library /test/lib
|
||||
* @modules java.base/java.lang:open
|
||||
* @modules java.base/jdk.internal.loader:open
|
||||
* @build jdk.test.lib.Platform
|
||||
* @run main LibraryPathProperty
|
||||
*/
|
||||
|
@ -44,7 +44,7 @@ public class LibraryPathProperty {
|
|||
static Method method;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
Class<?> klass = Class.forName("java.lang.ClassLoaderHelper");
|
||||
Class<?> klass = Class.forName("jdk.internal.loader.ClassLoaderHelper");
|
||||
method = klass.getDeclaredMethod("parsePath", String.class);
|
||||
method.setAccessible(true);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue