mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8281335: Allow a library already loaded via System::loadLibrary to be loaded as a raw library
Reviewed-by: sundar, mcimadamore
This commit is contained in:
parent
81645521c8
commit
980d187891
7 changed files with 215 additions and 98 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2022, 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.
|
||||
*
|
||||
|
@ -2385,7 +2385,7 @@ public abstract class ClassLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
private final NativeLibraries libraries = NativeLibraries.jniNativeLibraries(this);
|
||||
private final NativeLibraries libraries = NativeLibraries.newInstance(this);
|
||||
|
||||
// Invoked in the java.lang.Runtime class to implement load and loadLibrary.
|
||||
static NativeLibrary loadLibrary(Class<?> fromClass, File file) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, 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
|
||||
|
@ -74,7 +74,7 @@ public class BootLoader {
|
|||
|
||||
// native libraries loaded by the boot class loader
|
||||
private static final NativeLibraries NATIVE_LIBS
|
||||
= NativeLibraries.jniNativeLibraries(null);
|
||||
= NativeLibraries.newInstance(null);
|
||||
|
||||
/**
|
||||
* Returns the unnamed module for the boot loader.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, 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
|
||||
|
@ -36,7 +36,6 @@ import java.util.ArrayDeque;
|
|||
import java.util.Deque;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -62,8 +61,6 @@ public final class NativeLibraries {
|
|||
// unless specified
|
||||
private final Class<?> caller; // may be null
|
||||
private final boolean searchJavaLibraryPath;
|
||||
// loading JNI native libraries
|
||||
private final boolean isJNI;
|
||||
|
||||
/**
|
||||
* Creates a NativeLibraries instance for loading JNI native libraries
|
||||
|
@ -81,48 +78,14 @@ public final class NativeLibraries {
|
|||
* @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);
|
||||
public static NativeLibraries newInstance(ClassLoader loader) {
|
||||
return new NativeLibraries(loader, loader != null ? null : NativeLibraries.class, loader != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
private NativeLibraries(ClassLoader loader, Class<?> caller, boolean searchJavaLibraryPath) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -227,7 +190,7 @@ public final class NativeLibraries {
|
|||
}
|
||||
}
|
||||
|
||||
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI);
|
||||
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, true);
|
||||
// load the native library
|
||||
NativeLibraryContext.push(lib);
|
||||
try {
|
||||
|
@ -237,8 +200,7 @@ public final class NativeLibraries {
|
|||
// 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();
|
||||
boolean autoUnload = !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
|
||||
|
@ -269,8 +231,6 @@ public final class NativeLibraries {
|
|||
*/
|
||||
public NativeLibrary loadLibrary(String name) {
|
||||
assert name.indexOf(File.separatorChar) < 0;
|
||||
assert caller != null;
|
||||
|
||||
return loadLibrary(caller, name);
|
||||
}
|
||||
|
||||
|
@ -293,29 +253,6 @@ 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);
|
||||
acquireNativeLibraryLock(lib.name());
|
||||
try {
|
||||
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();
|
||||
} finally {
|
||||
releaseNativeLibraryLock(lib.name());
|
||||
}
|
||||
}
|
||||
|
||||
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
|
||||
for (String path : paths) {
|
||||
File libfile = new File(path, System.mapLibraryName(name));
|
||||
|
@ -380,7 +317,11 @@ public final class NativeLibraries {
|
|||
return findEntry0(this, name);
|
||||
}
|
||||
|
||||
Runnable unloader() {
|
||||
/*
|
||||
* Unloader::run method is invoked to unload the native library
|
||||
* when this class loader becomes phantom reachable.
|
||||
*/
|
||||
private Runnable unloader() {
|
||||
return new Unloader(name, handle, isBuiltin, isJNI);
|
||||
}
|
||||
|
||||
|
@ -394,6 +335,13 @@ public final class NativeLibraries {
|
|||
|
||||
return load(this, name, isBuiltin, isJNI, loadLibraryOnlyIfPresent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close this native library.
|
||||
*/
|
||||
void close() {
|
||||
unload(name, isBuiltin, isJNI, handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -429,7 +377,7 @@ public final class NativeLibraries {
|
|||
acquireNativeLibraryLock(name);
|
||||
try {
|
||||
/* remove the native library name */
|
||||
if (!loadedLibraryNames.remove(name)) {
|
||||
if (isJNI && !loadedLibraryNames.remove(name)) {
|
||||
throw new IllegalStateException(name + " has already been unloaded");
|
||||
}
|
||||
NativeLibraryContext.push(UNLOADER);
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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.misc.VM;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static jdk.internal.loader.NativeLibraries.*;
|
||||
|
||||
|
||||
/**
|
||||
* RawNativeLibraries has the following properties:
|
||||
* 1. Native libraries loaded in this RawNativeLibraries 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.
|
||||
*/
|
||||
public final class RawNativeLibraries {
|
||||
final Map<String, NativeLibraryImpl> libraries = new ConcurrentHashMap<>();
|
||||
final Class<?> caller;
|
||||
|
||||
private RawNativeLibraries(MethodHandles.Lookup trustedCaller) {
|
||||
this.caller = trustedCaller.lookupClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a RawNativeLibraries instance that has no relationship with
|
||||
* any class loaders and disabled auto unloading.
|
||||
*
|
||||
* This static factory method is restricted for JDK trusted class use.
|
||||
*/
|
||||
public static RawNativeLibraries newInstance(MethodHandles.Lookup trustedCaller) {
|
||||
if (!trustedCaller.hasFullPrivilegeAccess() ||
|
||||
!VM.isSystemDomainLoader(trustedCaller.lookupClass().getClassLoader())) {
|
||||
throw new InternalError(trustedCaller + " does not have access to raw native library loading");
|
||||
}
|
||||
return new RawNativeLibraries(trustedCaller);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a native library from the given path. Returns null if the given
|
||||
* library is determined to be non-loadable, which is system-dependent.
|
||||
*
|
||||
* @param path the path of the native library
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public NativeLibrary load(Path path) {
|
||||
String name = AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public String run() {
|
||||
try {
|
||||
return path.toRealPath().toString();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
return load(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a native library of the given pathname, which is platform-specific.
|
||||
* Returns null if it fails to load the given pathname.
|
||||
*
|
||||
* If the given pathname does not contain a name-separator character,
|
||||
* for example on Unix a slash character, the library search strategy
|
||||
* is system-dependent for example on Unix, see dlopen.
|
||||
*
|
||||
* @apiNote
|
||||
* The {@code pathname} argument is platform-specific.
|
||||
* {@link System#mapLibraryName} can be used to convert a name to
|
||||
* a platform-specific pathname:
|
||||
* {@snippet
|
||||
* RawNativeLibraries libs = RawNativeLibraries.newInstance(MethodHandles.lookup());
|
||||
* NativeLibrary lib = libs.load(System.mapLibraryName("blas"));
|
||||
* }
|
||||
*
|
||||
* @param pathname the pathname of the native library
|
||||
* @see System#mapLibraryName(String)
|
||||
*/
|
||||
public NativeLibrary load(String pathname) {
|
||||
return libraries.computeIfAbsent(pathname, this::get);
|
||||
}
|
||||
|
||||
private NativeLibraryImpl get(String pathname) {
|
||||
NativeLibraryImpl lib = new NativeLibraryImpl(caller, pathname, false, false);
|
||||
if (!lib.open()) {
|
||||
return null; // fail to open the native library
|
||||
}
|
||||
return lib;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unloads the given native library.
|
||||
*/
|
||||
public void unload(NativeLibrary lib) {
|
||||
Objects.requireNonNull(lib);
|
||||
if (!libraries.remove(lib.name(), lib)) {
|
||||
throw new IllegalArgumentException(lib.name() + " not loaded by this RawNativeLibraries instance");
|
||||
}
|
||||
NativeLibraryImpl nl = (NativeLibraryImpl)lib;
|
||||
nl.close();
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue