blob: fa4c1390c1ab2914249a57d533d9967955780011 [file]
/*
* Copyright (c) 2022, 2024, 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.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 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 Set<RawNativeLibraryImpl> libraries = ConcurrentHashMap.newKeySet();
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.
*
* The library is opened with the platform-specific library loading
* mechanism. If this method is called with the same path multiple times,
* the library is opened the same number of times. To close the library
* of the given path, {@code #unload} must be called on all the
* {@code NativeLibrary} instances that load it.
*
* @param path the path of the native library
*/
public NativeLibrary load(Path path) {
String name;
try {
name = path.toRealPath().toString();
} catch (IOException e) {
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"));
* }
*
* The library is opened with the platform-specific library loading
* mechanism. If this method is called with the same pathname multiple times,
* the library is opened the same number of times. To close the library
* of the given path, {@code #unload} must be called on all the
* {@code NativeLibrary} instances that load it.
*
* @param pathname the pathname of the native library
* @see System#mapLibraryName(String)
*/
public NativeLibrary load(String pathname) {
RawNativeLibraryImpl lib = new RawNativeLibraryImpl(pathname);
if (!lib.open()) {
return null;
}
libraries.add(lib);
return lib;
}
/*
* Unloads the given native library. Each {@code NativeLibrary}
* instance can be unloaded only once.
*
* The native library may remain opened after this method is called.
* Refer to the platform-specific library loading mechanism, for example,
* dlopen/dlclose on Unix or LoadLibrary/FreeLibrary on Windows.
*
* @throws IllegalArgumentException if the given library is not
* loaded by this RawNativeLibraries or has already been unloaded
*/
public void unload(NativeLibrary lib) {
Objects.requireNonNull(lib);
if (!libraries.remove(lib)) {
throw new IllegalArgumentException("can't unload " + lib.name() + " loaded from " + lib);
}
RawNativeLibraryImpl nl = (RawNativeLibraryImpl)lib;
nl.close();
}
static class RawNativeLibraryImpl extends NativeLibrary {
// the name of the raw native library.
final String name;
// opaque handle to raw native library, used in native code.
long handle;
RawNativeLibraryImpl(String name) {
this.name = name;
}
@Override
public String name() {
return name;
}
@Override
public long find(String name) {
return findEntry0(handle, name);
}
/*
* Loads the named native library.
*/
boolean open() {
if (handle != 0) {
throw new InternalError("Native library " + name + " has been loaded");
}
return load0(this, name);
}
/*
* Close this native library.
*/
void close() {
unload0(name, handle);
}
}
private static native boolean load0(RawNativeLibraryImpl impl, String name);
private static native void unload0(String name, long handle);
}