General Page
IBM Semeru jextract user guide
The IBM Semeru jextract tool simplifies Java integration with native C libraries, by automatically generating Java bindings from C header files.
The generated bindings use Java's modern Foreign Function and Memory (FFM) API, eliminating the need for handwritten JNI code and making native integration safer and easier to maintain. The FFM APIs are a fully supported feature in Java 25.
Built on the community jextract project and powered by IBM Semeru Runtimes, IBM Semeru jextract extends support to enterprise platforms such as IBM Z and Power. This tool enables Java developers to access native libraries across these architectures by using a consistent, modern, and standards-based approach.
Note: The IBM Semeru jextract tool that is described here is different from the jextract dump extractor included in IBM Java 8 and IBM Semeru Runtimes 17 and earlier releases. The latter is renamed as jpackcore in Semeru Runtimes 21 and later releases.
Key benefits
- Automatic code generation - No manual JNI code required.
- Type safety - Compile-time type checking for native calls.
- Easy maintenance - Regenerate bindings when libraries update.
How it works
Jextract parses C headers by using Clang and generates Java code that uses the FFM API to interact with native libraries. For more information, see jextract guide.
Prerequisites
Ensure that the following prerequisites are met before using jextract.
IBM Semeru version 25 or higher
- Download from the IBM Developer website.
Native library with header files
- The C library that you want to create bindings for, including header files (.h) and the compiled library file (.so on Linux, .dylib on macOS, .dll on Windows).
Basic Java knowledge
- Java programming fundamentals, understanding of C data types and function signatures, and familiarity with memory management concepts.
Installation
Follow these steps to install the jextract tool.
- Download IBM Semeru jextract from IBM Semeru Runtimes downloads page.
- Extract to a directory (for example, /opt/ibm/jextract).
Add the directory to the PATH variable. For example,
On Linux/macOS/AIX/z/OS
export PATH=/opt/ibm/jextract/bin:$PATH.
On Windows
set PATH=C:\path\to\jextract\bin;%PATH%
Verify the installation using the following command:
jextract --version
Expected output is as follows:
jextract 25+1-semeru-7551d52
JDK version 25.0.3+8-LTS
LibClang version clang version 13.0.1 (https://github.com/llvm/llvm-project.git 75e33f71c2dae584b13a7d1186ae0a038ba98838)Note: If you are using macOS Catalina or later you may need to remove the quarantine attribute from the bits before you can use the jextract binaries. To do this, run the following.
$ sudo xattr -r -d com.apple.quarantine path/to/jextract/folder/Quick Start Tutorial
This tutorial demonstrates creating a simple C library and generating Java bindings.
Step 1 - Create a simple C library
Create hello.h:
#ifndef HELLO_H
#define HELLO_H
void greet(const char* name);
int add(int a, int b);
#endifCreate hello.c
#include <stdio.h>
#include "hello.h"
void greet(const char* name) {
printf("Hello, %s!\n", name);
}
int add(int a, int b) {
return a + b;
}Step 2 - Compile the library
On Linux/macOS:
gcc -shared -fPIC -o libhello.so hello.cOn AIX:
xlc -q64 -qmkshrobj -o libhello.a hello.cOn z/OS:
ibm-clang -shared -fPIC -m64 -fvisibility=default -o libhello.so hello.cOn Windows:
You will need to manually describe attributes of DLL through .def file: hello.def
LIBRARY hello
EXPORTS
greet
addCompile:
clang -shared hello.c -o hello.dll -Wl,/DEF:hello.def # if using a .def file
clang -shared hello.c -o hello.dll # if using __declspec(dllexport)Step 3: Generate Java bindings
jextract \
--output src \
--target-package com.example.hello \
--library hello \
hello.hCommand Breakdown is as follows:
- --output src - Output directory for generated files
- --target-package com.example.hello - Java package name
- --library hello - Native library name to load
- hello.h - Header file to process
After running jextract, you will find the following directory structure.
src/
└── com/
└── example/
└── hello/
├── hello_h.java # Main header class with your functions
└── hello_h$shared.java # Shared utilities and constantshello_h$shared.java contains the shared utilities and constants while main header class (hello_h.java) contains bindings for your C functions and also takes care of resolving the library name for specific platform.
// Generated by jextract
package com.example.hello;
...
public class hello_h extends hello_h$shared {
...
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("hello"), LIBRARY_ARENA)
.or(SymbolLookup.loaderLookup())
.or(Linker.nativeLinker().defaultLookup());
...
private static class greet {
...
}
...
public static void greet(MemorySegment name) {
...
}
private static class add {
...
}
...
public static int add(int a, int b) {
...
}
}Step 4: Use the generated bindings
Import the generated main header class into your application to call native library functions, as shown in the following example.
Create HelloWorld.java:
import com.example.hello.hello_h;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.charset.Charset;
import java.util.Arrays;
public class HelloWorld {
final static Charset nativeCharset = Charset.forName(System.getProperty("native.encoding"));
static MemorySegment allocateSegmentForString(Arena arena, String str) {
byte[] nativeBytes = str.getBytes(nativeCharset);
return arena.allocateFrom(ValueLayout.JAVA_BYTE, Arrays.copyOf(nativeBytes, nativeBytes.length + 1));
}
public static void main(String[] args) {
try (Arena arena = Arena.ofConfined()) {
// Call greet function
MemorySegment name = allocateSegmentForString(arena, "World");
hello_h.greet(name);
// Call add function
int result = hello_h.add(5, 3);
System.out.println("5 + 3 = " + result);
}
}
}Step 5: Compile and run
Set the library path, compile, and run by using the following commands.
# Set path
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH #Linux
export DYLD_LIBRARY_PATH=.:$DYLD_LIBRARY_PATH #macOS
set PATH=.;%PATH% #Windows
export LIBPATH=.:$LIBPATH #z/OS and AIX
#Compile
javac -d bin src/com/example/hello/*.java HelloWorld.java
#Run the application
java -cp bin HelloWorldThe expected output is as follows.
Hello, World!
5 + 3 = 8Sorting arrays with C standard library (qsort)
(Passing java methods as callbacks)
This example demonstrates how to use the C standard library qsort function by passing Java lambdas as a callback function to native C code.
Generate Java bindings
jextract --output src -t org.stdlib "<stdlib.h>"Usage
import static org.stdlib.stdlib_h.*;
import org.stdlib.__compar_fn_t;
import java.lang.foreign.*;
try (var arena = Arena.ofConfined()) {
// Create array of integers
MemorySegment array = arena.allocate(C_INT, 5);
array.setAtIndex(C_INT, 0, 5);
array.setAtIndex(C_INT, 1, 2);
array.setAtIndex(C_INT, 2, 8);
array.setAtIndex(C_INT, 3, 1);
array.setAtIndex(C_INT, 4, 9);
// Create comparator callback
var comparator = __compar_fn_t.allocate((a, b) -> {
int aVal = a.get(C_INT, 0);
int bVal = b.get(C_INT, 0);
return Integer.compare(aVal, bVal);
}, arena);
// Sort the array
qsort(array, 5, C_INT.byteSize(), comparator);
// Print sorted array
for (int i = 0; i < 5; i++) {
System.out.println(array.getAtIndex(C_INT, i));
}
}Note: The comparator callback interface name is platform-dependent. To find the correct class name for your platform, inspect the header file and check generated files in the output directory.
For more examples, see jextract samples.
Best practices
Library path configurations
Set the library path before you run the application.
export LD_LIBRARY_PATH=/abs/path/to/libmylib.so:$LD_LIBRARY_PATH # Linux
export DYLD_LIBRARY_PATH=/abs/path/to/libmylib.so:$DYLD_LIBRARY_PATH # macOS
set PATH=\abs\path\to\libmylib.so;%PATH% # Windows
export LIBPATH=/abs/path/to/libmylib.so:$LIBPATH # z/OS
export LIBPATH=/abs/path/to/libmylis.a:$LIBPATH # AIXOptionally, provide an absolute path while you generate the bindings.
jextract --library /absolute/path/to/libmylib.so mylib.h # Linux, macOS and z/OS
jextract --library /absolute/path/to/libmylib.a mylib.h # AIXPlatform considerations
- FFM bindings generated on one platform may not be portable to another because of C ABI differences, including variations in the representation of specific types and structures. To avoid incompatibilities, it is recommended to run jextract directly on the target system when generating bindings.
- Test the generated bindings on all target platforms before deployment.
For more information, see jextract guide.
Troubleshooting
Library not found
Error: java.lang.UnsatisfiedLinkError: Unable to load library 'mylib'
Solutions:
- Verify that the library is installed.
- Set the library path with the directory that contains the library.
- Provide the absolute path of the library when you generate Java bindings by using jextract.
Header file not found
If you face the error - fatal error: 'mylib.h' file not found, then try one of the following solutions.
- Add the "include" directory - jextract -I /usr/include mylib.h
- Use the absolute path - jextract /usr/local/include/mylib.h
Platform-specific issues
Problem: Bindings work on one platform but fail on another.
Solutions:
- Generate separate bindings per platform.
- Check for platform-specific types.
- Test on all target platforms.
For more advanced troubleshooting options, see jextract guide.
Command reference
Basic usage
jextract --output src --target-package com.example --library mylib mylib.hWith include directories
jextract -I /usr/include -I /usr/local/include mylib.hMultiple headers
jextract --header-class-name MyLib header1.h header2.hWith preprocessor definitions
jextract -D DEBUG=1 -D VERSION=2 mylib.hCommon options
- --output - Output directory for generated files
- --target-package - Java package name for generated classes
- --library - Native library name to load
- -I - Add include directory
- -D - Define a preprocessor macro
- --header-class-name - Name for the main header class
- --include-typedef - Include specific typedef
- --include-function - Include specific function
Known issues on z/OS
Dependency detection when using selective include
When you use the selective include options such as --include-typedef or --include-function, if the typedef contains anonymous structs that have dependencies on excluded types, then jextract on z/OS might not detect or warn about the missing dependencies.
For example,
struct A { };
typedef struct { struct A a; } t_str;When extracting only t_str by using --include-typedef t_str, the dependency on struct A might not be detected.
The impact is as follows.
- No warning about missing dependencies during extraction.
- Potential compilation failures or runtime errors in generated Java code.
- Affects code that relies on selective filtering to extract specific types.
The possible workarounds are listed here.
- Include all dependent types explicitly when using selective include options.
- Avoid usage of the selective include options for complex type hierarchies.
- Verify that the generated Java code compiles correctly after extraction.
Documentation comments for nested anonymous structs
Generated Javadoc comments for nested anonymous structs and typedef structs might contain incomplete or nondescriptive text.
For example,
// Nested anonymous struct
struct NestedAnon {
struct {
int l;
long long h;
} u;
};
// Typedef'd struct
typedef struct Point {
int x;
int y;
} Point_t;The impact is as follows.
- Generated Javadoc comments are less descriptive and harder to understand.
- Refer to the original C header files for complete type information.
- Does not affect functionality. The generated Java code compiles and runs correctly.
- Only the documentation quality in generated bindings is affected.
Possible workarounds are listed here.
- Manually review and update Javadoc comments in the generated code if needed.
- Keep the original C header files accessible for reference.
- Consider adding custom documentation to wrapper classes.
Resources
Documentation
- Jextract Technical Guide - Comprehensive technical documentation.
- FFM API (JEP 454) - Foreign Function and Memory API specification.
- Project Panama - Native interop project.
Downloads
- Pre-built binary files
- JDK downloads - IBM Semeru Runtimes.
Code Examples
- jextract samples - Working examples for various libraries
- GitHub Repository - Source code and issues
Was this topic helpful?
Document Information
Modified date:
13 May 2026
UID
ibm17272590