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:

  1. Allocate off-heap memory, which is memory outside the Java runtime, for the strlen function's argument.

  2. 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);
  3. 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 specifies libc.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.