adam bien's blog

GraalVM: How To Call C-Function From Java ...with an Annotation 📎

To call a C-function:

#include <stdio.h>

extern "C" int multiply(int a,int b){
    printf("c function called with %d and %d",a,b);
    return a * b;
}

...from Java on GraalVM, you have to declare a static, native method annotated with @CFunction:


import java.io.IOException;
import org.graalvm.nativeimage.c.function.CFunction;
import org.graalvm.nativeimage.c.function.CFunction.Transition;
import org.graalvm.nativeimage.c.function.CLibrary;

@CLibrary("calculator")
public class Calculator {

    @CFunction(transition = Transition.NO_TRANSITION)
    public static native int multiply(int a,int b);

    public static void main(String[] args) throws IOException {

        System.out.println("calling c with parameter");
        var result = multiply(2,21);
        System.out.println("-----------------> result: " + result);
    }
}

you will have to download and install GraalVM first.

To compile the code above, you have to "run" on GraalVM. The command java -version should contain "GraalVM":


openjdk version "17.0.4" 2022-07-19
OpenJDK Runtime Environment GraalVM CE 22.3.0-dev (build 17.0.4+7-jvmci-22.3-b02)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.0-dev (build 17.0.4+7-jvmci-22.3-b02, mixed mode, sharing)

Also the following GraalVM components have to be installed (gu list output):


ComponentId                Component name     
----------------------------------------------
graalvm                    GraalVM Core       
llvm                       LLVM Runtime Core  
llvm-toolchain             LLVM.org toolchain 
native-image               Native Image       

Install any missing components with gu install [ComponentId] e.g gu install llvm

To create the native image:

  1. Compile the c code: $LLVM_TOOLCHAIN/clang -shared -o libcalculator.so calculator.cc
  2. Compile the Java code: javac -cp . -d . *.java
  3. Create native image: native-image -cp target --verbose -H:CLibraryPath=[FULLY QUALIFIED PATH TO: libcalculator.so] Calculator NativeCalculator

Now you can call the calculator with: NativeCalculator.

Output:


calling c with parameter
-----------------> result: 42
c function called with 2 and 21

attention

  1. In case $JAVA_HOME/bin/lli --print-toolchain-path throws an exception, consider using the latest dev build.
  2. The value of the annotation @CLibrary("calculator") contains the name of the *.so file without the "lib" prefix: libcalculator.so -> calculator.so
  3. The flag: -H:CLibraryPath= has to point to the fully qualifed path of the directory containing the libcalculator.so

The entire code and build script is available from: graalvm-hello-java-c