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:
- A linker allows Java code to link against foreign functions, via downcall method handles ; and
- A linker allows foreign functions to call Java method handles, via the generation of upcall stubs .
In addition, a linker provides a way to look up foreign functions in libraries that conform to the ABI. Each linker chooses a set of libraries that are commonly used on the OS and processor combination associated with the ABI. For example, a linker for Linux/x64 might choose two libraries:
libc
and
libm
. The functions in these libraries are exposed via a
symbol lookup .
The nativeLinker()
method provides a linker for the ABI associated with the OS and processor where the Java runtime is currently executing. This linker also provides access, via its default lookup , to the native libraries loaded with the Java runtime.
Linking a foreign function is a process which requires a function descriptor, a set of memory layouts which, together, specify the signature of the foreign function to be linked, and returns, when complete, a downcall method handle, that is, a method handle that can be used to invoke the target foreign function.
The Java method type associated with the returned method handle is derived from the argument and return layouts in the function descriptor. More specifically, given each layout L
in the function descriptor, a corresponding carrier C
is inferred, as described below:
- if
L
is a ValueLayout
PREVIEW with carrier E
then there are two cases:
- if
L
occurs in a parameter position and E
is MemoryAddress.class
, then C = Addressable.class
;
- otherwise,
C = E
;
- or, if
L
is a GroupLayout
PREVIEW, then C
is set to MemorySegment.class
The downcall method handle type, derived as above, might be decorated by additional leading parameters, in the given order if both are present:
- If the downcall method handle is created without specifying a target address , the downcall method handle type features a leading parameter of type
Addressable
PREVIEW, from which the address of the target foreign function can be derived.
- If the function descriptor's return layout is a group layout, the resulting downcall method handle accepts an additional leading parameter of type
SegmentAllocator
PREVIEW, which is used by the linker runtime to allocate the memory region associated with the struct returned by the downcall method handle.
Creating an upcall stub requires a method handle and a function descriptor; in this case, the set of memory layouts in the function descriptor specify the signature of the function pointer associated with the upcall stub.
The type of the provided method handle has to match the Java method type associated with the upcall stub, which is derived from the argument and return layouts in the function descriptor. More specifically, given each layout L
in the function descriptor, a corresponding carrier C
is inferred, as described below:
- If
L
is a ValueLayout
PREVIEW with carrier E
then there are two cases:
- If
L
occurs in a return position and E
is MemoryAddress.class
, then C = Addressable.class
;
- Otherwise,
C = E
;
- Or, if
L
is a GroupLayout
PREVIEW, then C
is set to MemorySegment.class
Upcall stubs are modelled by instances of type
MemorySegment
PREVIEW; upcall stubs can be passed by reference to other downcall method handles (as
MemorySegment
PREVIEW implements the
Addressable
PREVIEW interface) and, when no longer required, they can be
releasedPREVIEW, via their associated
sessionPREVIEW.
Safety considerations
Creating a downcall method handle is intrinsically unsafe. A symbol in a foreign library does not, in general, contain enough signature information (e.g. arity and types of foreign function parameters). As a consequence, the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts), the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation, the linker runtime guarantees the following for any argument that is a memory resource
R
(of type
MemorySegment
PREVIEW or
VaList
PREVIEW):
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 memory address PREVIEW, 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.
Linker
when preview features are enabled.