mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8295044: Implementation of Foreign Function and Memory API (Second Preview)
Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Per Minborg <pminborg@openjdk.org> Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org> Reviewed-by: jvernee, pminborg, psandoz, alanb, sundar
This commit is contained in:
parent
bd381886e0
commit
73baadceb6
252 changed files with 9221 additions and 7889 deletions
|
@ -42,35 +42,35 @@ import java.util.Optional;
|
|||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* A <em>symbol lookup</em> is an object that may be used to retrieve the address of a symbol in one or more libraries.
|
||||
* A <em>symbol lookup</em> retrieves the address of a symbol in one or more libraries.
|
||||
* A symbol is a named entity, such as a function or a global variable.
|
||||
* <p>
|
||||
* A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#lookup(String)}
|
||||
* A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#find(String)}
|
||||
* method takes the name of a symbol and returns the address of the symbol in that library.
|
||||
* <p>
|
||||
* The address of a symbol is modelled as a zero-length {@linkplain MemorySegment memory segment}. The segment can be used in different ways:
|
||||
* <ul>
|
||||
* <li>It can be passed to a {@link Linker} to create a downcall method handle, which can then be used to call the foreign function at the segment's base address.</li>
|
||||
* <li>It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle}, as an argument to the underlying foreign function.</li>
|
||||
* <li>It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, Addressable) stored} inside another memory segment.</li>
|
||||
* <li>It can be used to dereference memory associated with a global variable (this might require
|
||||
* {@link MemorySegment#ofAddress(MemoryAddress, long, MemorySession) resizing} the segment first).</li>
|
||||
* <li>It can be passed to a {@link Linker} to create a downcall method handle, which can then be used to call the foreign function at the segment's address.</li>
|
||||
* <li>It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}, as an argument to the underlying foreign function.</li>
|
||||
* <li>It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, MemorySegment) stored} inside another memory segment.</li>
|
||||
* <li>It can be used to access the region of memory backing a global variable (this might require
|
||||
* {@link MemorySegment#ofAddress(long, long, SegmentScope) resizing} the segment first).</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2 id="obtaining">Obtaining a symbol lookup</h2>
|
||||
*
|
||||
* The factory methods {@link #libraryLookup(String, MemorySession)} and {@link #libraryLookup(Path, MemorySession)}
|
||||
* The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)}
|
||||
* create a symbol lookup for a library known to the operating system. The library is specified by either its name or a path.
|
||||
* The library is loaded if not already loaded. The symbol lookup, which is known as a <em>library lookup</em>, is associated
|
||||
* with a {@linkplain MemorySession memory session}; when the session is {@linkplain MemorySession#close() closed}, the library is unloaded:
|
||||
* with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded:
|
||||
*
|
||||
* {@snippet lang=java :
|
||||
* try (MemorySession session = MemorySession.openConfined()) {
|
||||
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so"); // libGL.so loaded here
|
||||
* MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow();
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here
|
||||
* MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
|
||||
* ...
|
||||
* } // libGL.so unloaded here
|
||||
* }
|
||||
*}
|
||||
* <p>
|
||||
* If a library was previously loaded through JNI, i.e., by {@link System#load(String)}
|
||||
* or {@link System#loadLibrary(String)}, then the library was also associated with a particular class loader. The factory
|
||||
|
@ -80,7 +80,7 @@ import java.util.function.BiFunction;
|
|||
* System.loadLibrary("GL"); // libGL.so loaded here
|
||||
* ...
|
||||
* SymbolLookup libGL = SymbolLookup.loaderLookup();
|
||||
* MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow();
|
||||
* MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
|
||||
* }
|
||||
*
|
||||
* This symbol lookup, which is known as a <em>loader lookup</em>, is dynamic with respect to the libraries associated
|
||||
|
@ -91,18 +91,18 @@ import java.util.function.BiFunction;
|
|||
* by {@link System#load(String)} or {@link System#loadLibrary(String)}. A loader lookup does not expose symbols in libraries
|
||||
* that were loaded in the course of creating a library lookup:
|
||||
*
|
||||
* {@snippet lang=java :
|
||||
* libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true
|
||||
* loaderLookup().lookup("glGetString").isPresent(); // false
|
||||
* }
|
||||
* {@snippet lang = java:
|
||||
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
|
||||
* loaderLookup().find("glGetString").isPresent(); // false
|
||||
*}
|
||||
*
|
||||
* Note also that a library lookup for library {@code L} exposes symbols in {@code L} even if {@code L} was previously loaded
|
||||
* through JNI (the association with a class loader is immaterial to the library lookup):
|
||||
*
|
||||
* {@snippet lang=java :
|
||||
* {@snippet lang = java:
|
||||
* System.loadLibrary("GL"); // libGL.so loaded here
|
||||
* libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true
|
||||
* }
|
||||
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
|
||||
*}
|
||||
*
|
||||
* <p>
|
||||
* Finally, each {@link Linker} provides a symbol lookup for libraries that are commonly used on the OS and processor
|
||||
|
@ -110,11 +110,11 @@ import java.util.function.BiFunction;
|
|||
* helps clients to quickly find addresses of well-known symbols. For example, a {@link Linker} for Linux/x64 might choose to
|
||||
* expose symbols in {@code libc} through the default lookup:
|
||||
*
|
||||
* {@snippet lang=java :
|
||||
* {@snippet lang = java:
|
||||
* Linker nativeLinker = Linker.nativeLinker();
|
||||
* SymbolLookup stdlib = nativeLinker.defaultLookup();
|
||||
* MemorySegment malloc = stdlib.lookup("malloc").orElseThrow();
|
||||
* }
|
||||
* MemorySegment malloc = stdlib.find("malloc").orElseThrow();
|
||||
*}
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
@FunctionalInterface
|
||||
|
@ -123,9 +123,9 @@ public interface SymbolLookup {
|
|||
/**
|
||||
* Returns the address of the symbol with the given name.
|
||||
* @param name the symbol name.
|
||||
* @return a zero-length memory segment whose base address indicates the address of the symbol, if found.
|
||||
* @return a zero-length memory segment whose address indicates the address of the symbol, if found.
|
||||
*/
|
||||
Optional<MemorySegment> lookup(String name);
|
||||
Optional<MemorySegment> find(String name);
|
||||
|
||||
/**
|
||||
* Returns a symbol lookup for symbols in the libraries associated with the caller's class loader.
|
||||
|
@ -139,8 +139,8 @@ public interface SymbolLookup {
|
|||
* <p>
|
||||
* Libraries associated with a class loader are unloaded when the class loader becomes
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
|
||||
* returned by this method is backed by a {@linkplain MemorySession#asNonCloseable() non-closeable}, shared memory
|
||||
* session which keeps the caller's class loader reachable. Therefore, libraries associated with the caller's class
|
||||
* returned by this method is backed by a scope that is always alive and which keeps the caller's
|
||||
* class loader reachable. Therefore, libraries associated with the caller's class
|
||||
* loader are kept loaded (and their symbols available) as long as a loader lookup for that class loader is reachable.
|
||||
* <p>
|
||||
* In cases where this method is called from a context where there is no caller frame on the stack
|
||||
|
@ -158,24 +158,24 @@ public interface SymbolLookup {
|
|||
ClassLoader loader = caller != null ?
|
||||
caller.getClassLoader() :
|
||||
ClassLoader.getSystemClassLoader();
|
||||
MemorySession loaderSession = (loader == null || loader instanceof BuiltinClassLoader) ?
|
||||
MemorySession.global() : // builtin loaders never go away
|
||||
SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ?
|
||||
SegmentScope.global() : // builtin loaders never go away
|
||||
MemorySessionImpl.heapSession(loader);
|
||||
return name -> {
|
||||
Objects.requireNonNull(name);
|
||||
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
|
||||
// note: ClassLoader::findNative supports a null loader
|
||||
MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name));
|
||||
return addr == MemoryAddress.NULL ?
|
||||
long addr = javaLangAccess.findNative(loader, name);
|
||||
return addr == 0L ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0L, loaderSession));
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library.
|
||||
* The library will be unloaded when the provided memory session is {@linkplain MemorySession#close() closed},
|
||||
* if no other library lookup is still using it.
|
||||
* The library will be unloaded when the provided scope becomes
|
||||
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
|
||||
* @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS,
|
||||
* the library name is resolved according to the specification of the {@code dlopen} function for that OS.
|
||||
* In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function.
|
||||
|
@ -186,7 +186,7 @@ public interface SymbolLookup {
|
|||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @param name the name of the library in which symbols should be looked up.
|
||||
* @param session the memory session which controls the library lifecycle.
|
||||
* @param scope the scope associated with symbols obtained from the returned lookup.
|
||||
* @return a new symbol lookup suitable to find symbols in a library with the given name.
|
||||
* @throws IllegalArgumentException if {@code name} does not identify a valid library.
|
||||
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
|
||||
|
@ -194,15 +194,15 @@ public interface SymbolLookup {
|
|||
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup libraryLookup(String name, MemorySession session) {
|
||||
static SymbolLookup libraryLookup(String name, SegmentScope scope) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
|
||||
return libraryLookup(name, RawNativeLibraries::load, session);
|
||||
return libraryLookup(name, RawNativeLibraries::load, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols
|
||||
* in that library. The library will be unloaded when the provided memory session is {@linkplain MemorySession#close() closed},
|
||||
* if no other library lookup is still using it.
|
||||
* in that library. The library will be unloaded when the provided scope becomes
|
||||
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
|
@ -212,7 +212,7 @@ public interface SymbolLookup {
|
|||
* @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are
|
||||
* implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions.
|
||||
* @param path the path of the library in which symbols should be looked up.
|
||||
* @param session the memory session which controls the library lifecycle.
|
||||
* @param scope the scope associated with symbols obtained from the returned lookup.
|
||||
* @return a new symbol lookup suitable to find symbols in a library with the given path.
|
||||
* @throws IllegalArgumentException if {@code path} does not point to a valid library.
|
||||
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
|
||||
|
@ -220,22 +220,22 @@ public interface SymbolLookup {
|
|||
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup libraryLookup(Path path, MemorySession session) {
|
||||
static SymbolLookup libraryLookup(Path path, SegmentScope scope) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
|
||||
return libraryLookup(path, RawNativeLibraries::load, session);
|
||||
return libraryLookup(path, RawNativeLibraries::load, scope);
|
||||
}
|
||||
|
||||
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, MemorySession session) {
|
||||
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, SegmentScope libScope) {
|
||||
Objects.requireNonNull(libDesc);
|
||||
Objects.requireNonNull(session);
|
||||
Objects.requireNonNull(libScope);
|
||||
// attempt to load native library from path or name
|
||||
RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup());
|
||||
NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc);
|
||||
if (library == null) {
|
||||
throw new IllegalArgumentException("Cannot open library: " + libDesc);
|
||||
}
|
||||
// register hook to unload library when session is closed
|
||||
MemorySessionImpl.toSessionImpl(session).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
// register hook to unload library when 'libScope' becomes not alive
|
||||
((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
public void cleanup() {
|
||||
nativeLibraries.unload(library);
|
||||
|
@ -243,10 +243,10 @@ public interface SymbolLookup {
|
|||
});
|
||||
return name -> {
|
||||
Objects.requireNonNull(name);
|
||||
MemoryAddress addr = MemoryAddress.ofLong(library.find(name));
|
||||
return addr == MemoryAddress.NULL
|
||||
? Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0L, session));
|
||||
long addr = library.find(name);
|
||||
return addr == 0L ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0, libScope));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue