/* * Copyright (c) 2020, 2023, 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 java.lang.foreign; import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.CapturableState; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.javac.PreviewFeature; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import java.lang.invoke.MethodHandle; import java.nio.ByteOrder; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A linker provides access to foreign functions from Java code, and access to Java code from foreign functions. *
* Foreign functions typically reside in libraries that can be loaded on-demand. Each library conforms to * a specific ABI (Application Binary Interface). An ABI is a set of calling conventions and data types associated with * the compiler, OS, and processor where the library was built. For example, a C compiler on Linux/x64 usually * builds libraries that conform to the SystemV ABI. *
* A linker has detailed knowledge of the calling conventions and data types used by a specific ABI. * For any library which conforms to that ABI, the linker can mediate between Java code running * in the JVM and foreign functions in the library. In particular: *
* Scalar C types such as {@code bool}, {@code int} are modelled as {@linkplain ValueLayout value layouts} * of a suitable carrier. The mapping between a scalar type and its corresponding layout is dependent on the ABI * implemented by the native linker. For instance, the C type {@code long} maps to the layout constant * {@link ValueLayout#JAVA_LONG} on Linux/x64, but maps to the layout constant {@link ValueLayout#JAVA_INT} on * Windows/x64. Similarly, the C type {@code size_t} maps to the layout constant {@link ValueLayout#JAVA_LONG} * on 64-bit platforms, but maps to the layout constant {@link ValueLayout#JAVA_INT} on 32-bit platforms. *
* Composite types are modelled as {@linkplain GroupLayout group layouts}. More specifically, a C {@code struct} type * maps to a {@linkplain StructLayout struct layout}, whereas a C {@code union} type maps to a {@link UnionLayout union * layout}. When defining a struct or union layout, clients must pay attention to the size and alignment constraint * of the corresponding composite type definition in C. For instance, padding between two struct fields * must be modelled explicitly, by adding an adequately sized {@linkplain PaddingLayout padding layout} member * to the resulting struct layout. *
* Finally, pointer types such as {@code int**} and {@code int(*)(size_t*, size_t*)} are modelled as * {@linkplain AddressLayout address layouts}. When the spatial bounds of the pointer type are known statically, * the address layout can be associated with a {@linkplain AddressLayout#targetLayout() target layout}. For instance, * a pointer that is known to point to a C {@code int[2]} array can be modelled as an address layout whose * target layout is a sequence layout whose element count is 2, and whose element type is {@link ValueLayout#JAVA_INT}. *
* The following table shows some examples of how C types are modelled in Linux/x64: * *
** * *
* * * *C type *Layout *Java type *{@code bool} *{@link ValueLayout#JAVA_BOOLEAN} *{@code boolean} *{@code char} *{@link ValueLayout#JAVA_BYTE} *{@code byte} *{@code short} *{@link ValueLayout#JAVA_SHORT} *{@code short} *{@code int} *{@link ValueLayout#JAVA_INT} *{@code int} *{@code long} *{@link ValueLayout#JAVA_LONG} *{@code long} *{@code long long} *{@link ValueLayout#JAVA_LONG} *{@code long} *{@code float} *{@link ValueLayout#JAVA_FLOAT} *{@code float} *{@code double} *{@link ValueLayout#JAVA_DOUBLE} *{@code double} {@code size_t} *{@link ValueLayout#JAVA_LONG} *{@code long} *{@code char*}, {@code int**}, {@code struct Point*} *{@link ValueLayout#ADDRESS} *{@link MemorySegment} *{@code int (*ptr)[10]} ** * ValueLayout.ADDRESS.withTargetLayout( * MemoryLayout.sequenceLayout(10, * ValueLayout.JAVA_INT) * ); **{@link MemorySegment} ** struct Point { int x; long y; };* ** MemoryLayout.structLayout( * ValueLayout.JAVA_INT.withName("x"), * MemoryLayout.paddingLayout(32), * ValueLayout.JAVA_LONG.withName("y") * ); **{@link MemorySegment} ** * union Choice { float a; int b; }* ** MemoryLayout.unionLayout( * ValueLayout.JAVA_FLOAT.withName("a"), * ValueLayout.JAVA_INT.withName("b") * ); **{@link MemorySegment} *
* All the native linker implementations limit the function descriptors that they support to those that contain * only so-called canonical layouts. A canonical layout has the following characteristics: *
* To invoke the {@code qsort} downcall handle obtained above, we need a function pointer to be passed as the last * parameter. That is, we need to create a function pointer out of an existing method handle. First, let's write a * Java method that can compare two int elements passed as pointers (i.e. as {@linkplain MemorySegment memory segments}): * * {@snippet lang = java: * class Qsort { * static int qsortCompare(MemorySegment elem1, MemorySegmet elem2) { * return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0)); * } * } * } * * Now let's create a method handle for the comparator method defined above: * * {@snippet lang = java: * FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT, * ADDRESS.withTargetLayout(JAVA_INT), * ADDRESS.withTargetLayout(JAVA_INT)); * MethodHandle comparHandle = MethodHandles.lookup() * .findStatic(Qsort.class, "qsortCompare", * comparDesc.toMethodType()); * } * * First, we create a function descriptor for the function pointer type. Since we know that the parameters passed to * the comparator method will be pointers to elements of a C {@code int[]} array, we can specify {@link ValueLayout#JAVA_INT} * as the target layout for the address layouts of both parameters. This will allow the comparator method to access * the contents of the array elements to be compared. We then {@linkplain FunctionDescriptor#toMethodType() turn} * that function descriptor into a suitable {@linkplain java.lang.invoke.MethodType method type} which we then use to look up * the comparator method handle. We can now create an upcall stub which points to that method, and pass it, as a function * pointer, to the {@code qsort} downcall handle, as follows: * * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { * MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena); * MemorySegment array = session.allocateArray(0, 9, 3, 4, 6, 5, 1, 8, 2, 7); * qsort.invokeExact(array, 10L, 4L, comparFunc); * int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] * } * } * * This code creates an off-heap array, copies the contents of a Java array into it, and then passes the array to the * {@code qsort} method handle along with the comparator function we obtained from the native linker. After the invocation, the contents * of the off-heap array will be sorted according to our comparator function, written in Java. We then extract a * new Java array from the segment, which contains the sorted elements. * *
* First, we need to create the downcall method handles for {@code malloc} and {@code free}, as follows: * * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); * * MethodHandle malloc = linker.downcallHandle( * linker.defaultLookup().find("malloc").get(), * FunctionDescriptor.of(ADDRESS, JAVA_LONG) * ); * * MethodHandle free = linker.downcallHandle( * linker.defaultLookup().find("free").get(), * FunctionDescriptor.ofVoid(ADDRESS) * ); * } * * When interacting with a native functions returning a pointer (such as {@code malloc}), the Java runtime has no insight * into the size or the lifetime of the returned pointer. Consider the following code: * * {@snippet lang = java: * MemorySegment segment = (MemorySegment)malloc.invokeExact(100); * } * * The size of the segment returned by the {@code malloc} downcall method handle is * zero. Moreover, the scope of the * returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must, * unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to * attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory * backing the segment can be managed automatically, as for any other native segment created directly from Java code. * Both these operations are accomplished using the restricted {@link MemorySegment#reinterpret(long, Arena, Consumer)} * method, as follows: * * {@snippet lang = java: * MemorySegment allocateMemory(long byteSize, Arena arena) { * MemorySegment segment = (MemorySegment)malloc.invokeExact(byteSize); // size = 0, scope = always alive * return segment.reinterpret(byteSize, arena, s -> free.invokeExact(s)); // size = byteSize, scope = arena.scope() * } * } * * The {@code allocateMemory} method defined above accepts two parameters: a size and an arena. The method calls the * {@code malloc} downcall method handle, and unsafely reinterprets the returned segment, by giving it a new size * (the size passed to the {@code allocateMemory} method) and a new scope (the scope of the provided arena). * The method also specifies a cleanup action to be executed when the provided arena is closed. Unsurprisingly, * the cleanup action passes the segment to the {@code free} downcall method handle, to deallocate the underlying * region of memory. We can use the {@code allocateMemory} method as follows: * * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { * MemorySegment segment = allocateMemory(100, arena); * } // 'free' called here * } * * Note how the segment obtained from {@code allocateMemory} acts as any other segment managed by the confined arena. More * specifically, the obtained segment has the desired size, can only be accessed by a single thread (the thread which created * the confined arena), and its lifetime is tied to the surrounding try-with-resources block. * *
* A well-known variadic function is the {@code printf} function, defined in the C standard library: * * {@snippet lang = c: * int printf(const char *format, ...); * } * * This function takes a format string, and a number of additional arguments (the number of such arguments is * dictated by the format string). Consider the following variadic call: * * {@snippet lang = c: * printf("%d plus %d equals %d", 2, 2, 4); * } * * To perform an equivalent call using a downcall method handle we must create a function descriptor which * describes the specialized signature of the C function we want to call. This descriptor must include layouts for any * additional variadic argument we intend to provide. In this case, the specialized signature of the C * function is {@code (char*, int, int, int)} as the format string accepts three integer parameters. Then, we need to use * a linker option to specify the position of the first variadic layout in the provided function descriptor (starting from 0). * In this case, since the first parameter is the format string (a non-variadic argument), the first variadic index * needs to be set to 1, as follows: * * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); * MethodHandle printf = linker.downcallHandle( * linker.defaultLookup().lookup("printf").get(), * FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT), * Linker.Option.firstVariadicArg(1) // first int is variadic * ); * } * * We can then call the specialized downcall handle as usual: * * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { * int res = (int)printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" * } * } * *
* When creating upcall stubs the linker runtime validates the type of the target method handle against the provided * function descriptor and report an error if any mismatch is detected. As for downcalls, JVM crashes might occur, * if the foreign code casts the function pointer associated with an upcall stub to a type * that is incompatible with the provided function descriptor. Moreover, if the target method * handle associated with an upcall stub returns a {@linkplain MemorySegment memory segment}, clients must ensure * that this address cannot become invalid after the upcall completes. This can lead to unspecified behavior, * and even JVM crashes, since an upcall is typically executed in the context of a downcall method handle invocation. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface Linker permits AbstractLinker { /** * Returns a linker for the ABI associated with the underlying native platform. The underlying native platform * is the combination of OS and processor where the Java runtime is currently executing. *
* This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on * restricted methods, and use safe and supported functionalities, where possible. * * @apiNote It is not currently possible to obtain a linker for a different combination of OS and processor. * @implNote The libraries exposed by the {@linkplain #defaultLookup() default lookup} associated with the returned * linker are the native libraries loaded in the process where the Java runtime is currently executing. For example, * on Linux, these libraries typically include {@code libc}, {@code libm} and {@code libdl}. * * @return a linker for the ABI associated with the underlying native platform. * @throws UnsupportedOperationException if the underlying native platform is not supported. * @throws IllegalCallerException If the caller is in a module that does not have native access enabled. */ @CallerSensitive static Linker nativeLinker() { Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "nativeLinker"); return SharedUtils.getSystemLinker(); } /** * Creates a method handle which is used to call a foreign function with the given signature and address. *
* Calling this method is equivalent to the following code: * {@snippet lang=java : * linker.downcallHandle(function).bindTo(symbol); * } * * @param symbol the address of the target function. * @param function the function descriptor of the target function. * @param options any linker options. * @return a downcall method handle. The method handle type is inferred * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. * or if the symbol is {@link MemorySegment#NULL} * @throws IllegalArgumentException if an invalid combination of linker options is given. */ default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) { SharedUtils.checkSymbol(symbol); return downcallHandle(function, options).bindTo(symbol); } /** * Creates a method handle which is used to call a foreign function with the given signature. *
* The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is * {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor, * but features an additional leading parameter of type {@link MemorySegment}, from which the address of the target * foreign function is derived. Moreover, if the function descriptor's return layout is a group layout, the resulting * downcall method handle accepts an additional leading parameter of type {@link SegmentAllocator}, which is used by * the linker runtime to allocate the memory region associated with the struct returned by the downcall method handle. *
* Upon invoking a downcall method handle, the linker runtime will guarantee the following for any argument * {@code A} of type {@link MemorySegment} whose corresponding layout is an {@linkplain AddressLayout address layout}: *
* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout}, * invoking the returned method handle will return a native segment associated with * a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}. * However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout()} {@code T}, * then the size of the returned segment is set to {@code T.byteSize()}. *
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} * representing the target address of the foreign function is the {@link MemorySegment#NULL} address. * The returned method handle will additionally throw {@link NullPointerException} if any argument passed to it is {@code null}. * * @param function the function descriptor of the target function. * @param options any linker options. * @return a downcall method handle. The method handle type is inferred * from the provided function descriptor. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. * @throws IllegalArgumentException if an invalid combination of linker options is given. */ MethodHandle downcallHandle(FunctionDescriptor function, Option... options); /** * Creates a stub which can be passed to other foreign functions as a function pointer, associated with the given * arena. Calling such a function pointer from foreign code will result in the execution of the provided * method handle. *
* The returned memory segment's address points to the newly allocated upcall stub, and is associated with * the provided arena. As such, the lifetime of the returned upcall stub segment is controlled by the * provided arena. For instance, if the provided arena is a confined arena, the returned * upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}. *
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout} * is a native segment associated with a fresh scope that is always alive. * Under normal conditions, the size of this segment argument is {@code 0}. * However, if the address layout has a {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the * segment argument is set to {@code T.byteSize()}. *
* The target method handle should not throw any exceptions. If the target method handle does throw an exception, * the VM will exit with a non-zero exit code. To avoid the VM aborting due to an uncaught exception, clients * could wrap all code in the target method handle in a try/catch block that catches any {@link Throwable}, for * instance by using the {@link java.lang.invoke.MethodHandles#catchException(MethodHandle, Class, MethodHandle)} * method handle combinator, and handle exceptions as desired in the corresponding catch block. * * @param target the target method handle. * @param function the upcall stub function descriptor. * @param arena the arena associated with the returned upcall stub segment. * @param options any linker options. * @return a zero-length segment whose address is the address of the upcall stub. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. * @throws IllegalArgumentException if it is determined that the target method handle can throw an exception, or if the target method handle * has a type that does not match the upcall stub inferred type. * @throws IllegalStateException if {@code arena.scope().isAlive() == false} * @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a * thread {@code T}, other than the arena's owner thread. */ MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options); /** * Returns a symbol lookup for symbols in a set of commonly used libraries. *
* Each {@link Linker} is responsible for choosing libraries that are widely recognized as useful on the OS * and processor combination supported by the {@link Linker}. Accordingly, the precise set of symbols exposed by the * symbol lookup is unspecified; it varies from one {@link Linker} to another. * @implNote It is strongly recommended that the result of {@link #defaultLookup} exposes a set of symbols that is stable over time. * Clients of {@link #defaultLookup()} are likely to fail if a symbol that was previously exposed by the symbol lookup is no longer exposed. *
If an implementer provides {@link Linker} implementations for multiple OS and processor combinations, then it is strongly * recommended that the result of {@link #defaultLookup()} exposes, as much as possible, a consistent set of symbols * across all the OS and processor combinations. * @return a symbol lookup for symbols in a set of commonly used libraries. */ SymbolLookup defaultLookup(); /** * A linker option is used to indicate additional linking requirements to the linker, * besides what is described by a function descriptor. * @since 20 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) sealed interface Option permits LinkerOptions.LinkerOptionImpl { /** * {@return a linker option used to denote the index of the first variadic argument layout in a * foreign function call} * @param index the index of the first variadic argument in a downcall handle linkage request. */ static Option firstVariadicArg(int index) { return new LinkerOptions.FirstVariadicArg(index); } /** * {@return A linker option used to save portions of the execution state immediately after * calling a foreign function associated with a downcall method handle, * before it can be overwritten by the Java runtime, or read through conventional means} *
* Execution state is captured by a downcall method handle on invocation, by writing it * to a native segment provided by the user to the downcall method handle. * For this purpose, a downcall method handle linked with this * option will feature an additional {@link MemorySegment} parameter directly * following the target address, and optional {@link SegmentAllocator} parameters. * This parameter, called the 'capture state segment', represents the native segment into which * the captured state is written. *
* The capture state segment should have the layout returned by {@linkplain #captureStateLayout}. * This layout is a struct layout which has a named field for each captured value. *
* Captured state can be retrieved from the capture state segment by constructing var handles * from the {@linkplain #captureStateLayout capture state layout}. *
* The following example demonstrates the use of this linker option:
* {@snippet lang = "java":
* MemorySegment targetAddress = ...
* Linker.Option ccs = Linker.Option.captureCallState("errno");
* MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs);
*
* StructLayout capturedStateLayout = Linker.Option.capturedStateLayout();
* VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno"));
* try (Arena arena = Arena.ofConfined()) {
* MemorySegment capturedState = arena.allocate(capturedStateLayout);
* handle.invoke(capturedState);
* int errno = errnoHandle.get(capturedState);
* // use errno
* }
* }
*
* @param capturedState the names of the values to save.
* @throws IllegalArgumentException if at least one of the provided {@code capturedState} names
* is unsupported on the current platform.
* @see #captureStateLayout()
*/
static Option captureCallState(String... capturedState) {
Set
* The capture state layout is platform dependent but is guaranteed to be
* a {@linkplain StructLayout struct layout} containing only {@linkplain ValueLayout value layouts}
* and possibly {@linkplain PaddingLayout padding layouts}.
* As an example, on Windows, the returned layout might contain three value layouts named:
*
* A trivial function is a function that has an extremely short running time
* in all cases (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub).
*
* Using this linker option is a hint which some implementations may use to apply
* optimizations that are only valid for trivial functions.
*
* Using this linker option when linking non trivial functions is likely to have adverse effects,
* such as loss of performance, or JVM crashes.
*/
static Option isTrivial() {
return LinkerOptions.IsTrivial.INSTANCE;
}
}
}
*
* The following snipet shows how to obtain the names of the supported captured value layouts:
* {@snippet lang = java:
* String capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream()
* .map(MemoryLayout::name)
* .flatMap(Optional::stream)
* .map(Objects::toString)
* .collect(Collectors.joining(", "));
* }
*
* @see #captureCallState(String...)
*/
static StructLayout captureStateLayout() {
return CapturableState.LAYOUT;
}
/**
* {@return A linker option used to mark a foreign function as trivial}
*