Calling a C Library Function with the Foreign Function and Memory API
The following example calls strlen with the Foreign Function and Memory API:
static long invokeStrlen(String s) throws Throwable {
try (Arena arena = Arena.ofConfined()) {
// Allocate off-heap memory and
// copy the argument, a Java string, into off-heap memory
MemorySegment nativeString = arena.allocateFrom(s);
// Link and call the C function strlen
// Obtain an instance of the native linker
Linker linker = Linker.nativeLinker();
// Locate the address of the C function signature
SymbolLookup stdLib = linker.defaultLookup();
MemorySegment strlen_addr = stdLib.find("strlen").get();
// Create a description of the C function
FunctionDescriptor strlen_sig =
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
// Create a downcall handle for the C function
MethodHandle strlen = linker.downcallHandle(strlen_addr, strlen_sig);
// Call the C function directly from Java
return (long)strlen.invokeExact(nativeString);
}
}
The following is the declaration of the strlen C standard library function:
size_t strlen(const char *s);
It takes one argument, a string, and returns the length of the string. To call this function from a Java application, you would follow these steps:
-
Allocate off-heap memory, which is memory outside the Java runtime, for the strlen function's argument.
-
Store the Java string in the off-heap memory that you allocated.
The
invokeStrlen
example performs the previous step and this step with the following statement:MemorySegment nativeString = arena.allocateFrom(s);
- Build and then call a method handle that points to the strlen function. The topics in this section show you how to do this.
The following sections describe this example in detail:
Obtaining an Instance of the Native Linker
The following statement obtains an instance of the native linker, which provides access to the libraries that adhere to the calling conventions of the platform in which the Java runtime is running. These libraries are referred to as "native" libraries.
Linker linker = Linker.nativeLinker();
Locating the Address of the C Function
To call a native method such as strlen
, you need a downcall
method handle, which is a MethodHandle instance that points to a native function. This
instance requires the native function's address. To obtain this address, you use a
symbol lookup, which enables you to retrieve the address of a symbol (such as
the strlen
function) in one or more libraries.
The following statements obtain the address of the strlen
function:
// Obtain an instance of the C function
Linker linker = Linker.nativeLinker();
// Locate the address of the C function signature
SymbolLookup stdLib = linker.defaultLookup();
MemorySegment strlen_addr = stdLib.find("strlen").get();
Because strlen is part of the C standard library, this example uses the native linker's default lookup by calling Linker.defaultLookup(). A default lookup is a symbol lookup for symbols in a set of commonly used libraries (including the C standard library).
Note:
Call the method SymbolLookup.libraryLookup(String, Arena) to create a symbol lookup from the name of a library. This method loads the specified library and associates it with an arena, which controls the symbol lookup's lifetime. The following example specifieslibc.so.6
, which is the name of the C standard
library for many Linux
systems. SymbolLookup stdLib = SymbolLookup.libraryLookup("libc.so.6", arena);
MemorySegment strlen_addr = stdLib.find("strlen").get();
Tip:
Call SymbolLookup.loaderLookup() to find symbols in libraries that are loaded with System.loadLibrary(String).Describing the C Function Signature
A downcall method handle also requires a description of the native function's signature, which is represented by a FunctionDescriptor instance. A function descriptor describes the layouts of the native function's arguments and its return value, if any.
Each layout in a function descriptor maps to a Java type, which is the type
that should be used when invoking the resulting downcall method handle. Most value
layouts map to a Java primitive type. For example, ValueLayout.JAVA_INT maps to an int
value.
However, ValueLayout.ADDRESS maps to a pointer.
Composite types such as struct
and union
types are
modeled with the GroupLayout interface, which is a supertype of StructLayout and UnionLayout. See Memory Layouts and Structured Access for an example of how to initialize and access a C structure.
The following creates a function descriptor for the strlen function:
// Create a description of the C function signature
FunctionDescriptor strlen_sig =
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
The first argument of the FunctionDescriptor::of method is the layout of the native function's return value. Native primitive types are modeled using value layouts whose size matches that of such types. This means that a function descriptor is platform-specific. For example, size_t has a layout of JAVA_LONG on 64-bit or x64 platforms but a layout of JAVA_INT on 32-bit or x86 platforms.
Tip:
To determine the layout of a native primitive type that the native linker uses for your platform, call the method Linker::canonicalLayouts.The subsequent arguments of FunctionDescriptor::of are the layouts of the native function's arguments. In this example, there's only one subsequent argument, a ValueLayout.ADDRESS. This represents the only argument for strlen, a pointer to a string.
Creating the Downcall Handle for the C Function
The following statement creates a downcall method handle for the strlen function with its address and function descriptor.
// Create a downcall handle for the C function
MethodHandle strlen = linker.downcallHandle(strlen_addr, strlen_sig);
Calling the C Function Directly from Java
The following statement calls the strlen function with a memory segment that contains the function's argument:
// Call the C function directly from Java
return (long)strlen.invokeExact(nativeString);
You need to cast a method handle invocation with the expected return type; in
this case, it's long
.