Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PanamaGL & FFM API - Exploit native function using Java

PanamaGL & FFM API - Exploit native function using Java

With JDK22 the Foreign Function and Memory API (JEP-454) becomes available for all after ~9 years of hard work known as the Panama project. Come discover how this API enable exploiting the native functions. Panama project also created the jextract utility which quickly gets indispensable to generate binding with ease when a library API surface is large.

This work was used as a foundation for the next generation tooling with advanced feature to interact with OpenGL : PanamaGL, what were the challenges and what this new tool allows to do.

This presentation was authored by Brice Dutheil and Martin Pernollet (jzy3d)

Brice Dutheil

December 12, 2023
Tweet

More Decks by Brice Dutheil

Other Decks in Technology

Transcript

  1. Bios Martin Pernollet est un ingénieur logiciel spécialisé dans la

    visualisation 3D Java. Il accompagne des éditeurs construisant des solutions de visualisation de données scientifiques. Il maintient activement Jzy3D, un framework de visualisation 3D et 2D et contribue à JOGL et VTK. https://github.com/jzy3d https://gitlab.com/jzy3d
  2. Bios Brice DUTHEIL est un ingénieur logiciel chez Datadog. Il

    a construit son expérience avec du backend type EJB/WAR/EAR, du Spring Boot, puis des pods kubernetes – d’abord en tant que développeur puis ingénieur de production – dans le secteur du transport et de la téléphonie avant de rejoindre Datadog. Il a également été un des gros contributeurs à Mockito. Aujourd'hui il travaille sur le développement de plugin pour les produits JetBrains. https://blog.arkey.fr https://twitter.com/BriceDutheil
  3. Agenda - Introduction : why native access is great -

    PanamaGL : 3D rendering in Java with FFM API & JExtract - Panama : deep dive in FFM API & JExtract
  4. Native access history Jogl (Java OpenGL) Gluegen generate bindings (java

    & C code) Opensourced in 2010 FFM (Foreign Function & Memory API) to bind to native libraries JDK 1.0 1996 Native interface ? JDK 1.1 1997 JNI (Java Native Interface) JDK 1.5 2004 JNA (Java Native Access) 2006 JDK 8 2014 JDK 22 2023-03 JNR (Java Native Runtime) 2010 JDK 7 2011 Method handles JExtract utility (Oracle) to Java generate bindings (source and/or bytecode) SWIG (Simplified Wrapper and Interface Generator) Covers more that C or Java JavaCPP generate bindings (java & C code)
  5. JEP-454 : Foreign Function & Memory API Introduce an API

    by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI. “
  6. JEP-454 : Foreign Function & Memory API Introduce an API

    by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI. “ Also part of the Panama Project Vector API 7th incubator (JEP-460) An API to express vectorized computation on supported CPU.
  7. 9 years in the making JEP-191 Foreign Function Interface JEP-338

    Vector API (Incubator) JEP-370 Foreign Memory Access API (Incubator) JEP-383 Foreign Memory Access API (2nd Incubator) JEP-389 Foreign Linker API (Incubator) JEP-393 Foreign Memory Access API (3rd Incubator) JEP-414 Vector API (2nd Incubator) JEP-412 Foreign Function & Memory API JEP-417 Vector API (3nd Incubator) JEP-419 Foreign Function & Memory API (Second Incubator) JEP-424 Foreign Function & Memory API (Preview) JEP-426 Vector API (Fourth Incubator) JEP-434 Foreign Function & Memory API (Second Preview) JEP-438 Vector API (Fifth Incubator) JEP-442 Foreign Function & Memory API (Third Preview) JEP-448 Vector API (Sixth Incubator) JEP 454 Foreign Function & Memory API JEP 460 Vector API (Seventh Incubator) JDK 22 2014 2023
  8. Why native ? Native functions are needed for various reasons

    • OS: interacting with devices, file systems, network interfaces, … • Third party libraries ◦ Unique feature, unique implementation, performance ▪ e.g. Harfbuzz
  9. Why native ? Native functions are needed for various reasons

    • OS: interacting with devices, file systems, network interfaces, … • Third party libraries ◦ Unique feature, unique implementation, performance ▪ e.g. Harfbuzz, Libsodium, blake3
  10. Why native ? Native functions are needed for various reasons

    • OS: interacting with devices, file systems, network interfaces, … • Third party libraries ◦ Unique feature, unique implementation, performance ▪ e.g. Harfbuzz, Libsodium, blake3, vectorscan, fswatch, tcmalloc, libVLC, CEF, clang, etc.
  11. Why native ? Native functions are needed for various reasons

    • OS: interacting with devices, file systems, network interfaces, … • Third party libraries ◦ Unique feature, unique implementation, performance ▪ e.g. Harfbuzz, Libsodium, blake3, vectorscan, fswatch, tcmalloc, libVLC, CEF, clang, etc. ▪ OpenGL https://www.flickr.com/photos/watz/3558789383/
  12. JNI package q.r.s; class NativeBinding { static { System.load("path/to/libNative.so"); }

    public static native boolean isatty( int fileDescriptor ); } #include "q_r_s_Native.h" #include <unistd.h> JNIEXPORT jboolean JNICALL Java_q_r_s_NativeBinding_isatty( JNIEnv *env, jclass cls, jint fileDescriptor ) { return isatty(fileDescriptor)? JNI_TRUE: JNI_FALSE; } $ javac -d classes -cp src \ -h jni q/r/s/Native.java $ gcc \ -I $JAVA_HOME/include \ -I $JAVA_HOME/include/linux \ -fpic \ -shared \ -o libNative.so \ Native.c 🥇 Available since Java 1.1 🏎 Efficient 🏗 Complex and painful build ☣ Can terminate the JVM 💡 Before JDK10 use javah. See JEP-313
  13. JNI The base of everything To call native code, the

    JVM must perform some tricks • Initialize a valid C stack • Initialize CPU register for the OS and CPU calling convention, and switch them back on return • Use CPU memory barrier instructions (fencing) • Prevent some JIT optimisations like inlining • If critical sections are declared (Get*Critical) prevent the GC to run at the very least on the impacted région • Data exchange (objects, arrays) likely require copy (unless in critical section) ◦ And likely serialization
  14. JNI JNA package q.r.s; class NativeBinding { static { System.load("path/to/libNative.so");

    } public static native boolean isatty( int fileDescriptor ); } #include "q_r_s_Native.h" #include <unistd.h> JNIEXPORT jboolean JNICALL Java_q_r_s_NativeBinding_isatty( JNIEnv *env, jclass cls, jint fileDescriptor ) { return isatty(fileDescriptor)? JNI_TRUE: JNI_FALSE; } $ javac -d classes -cp src \ -h jni q/r/s/Native.java $ gcc \ -I $JAVA_HOME/include \ -I $JAVA_HOME/include/linux \ -fpic \ -shared \ -o Native.so \ Native.c public interface JNA_Library extends Library { JNA_Library INSTANCE = (JNA_Library) Native.loadLibrary( "c", JNA_Library.class ); boolean isatty(int fileDescriptor); } 💪 Concept 1996, released 2006 📦 Third party 🎚 Easy to use, no C build 🚜 Versatile, not fast
  15. Featureful JNA • Supports native types (wstring, structure, union, etc.)

    • Lots of reflection used for ease-of-use • Handles serialization, memory segments • Based upon JNI (invisible) to invoke its own native library • JNA Native lib will perform dispatch via libffi libffi is an assembly handwritten library that sets up the C callsite (c stack, registers)
  16. JNI JNA JNR-FFI package q.r.s; class NativeBinding { static {

    System.load("path/to/libNative.so"); } public static native boolean isatty( int fileDescriptor ); } #include "q_r_s_Native.h" #include <unistd.h> JNIEXPORT jboolean JNICALL Java_q_r_s_NativeBinding_isatty( JNIEnv *env, jclass cls, jint fileDescriptor ) { return isatty(fileDescriptor)? JNI_TRUE: JNI_FALSE; } $ javac -d classes -cp src \ -h jni q/r/s/Native.java $ gcc \ -I $JAVA_HOME/include \ -I $JAVA_HOME/include/linux \ -fpic \ -shared \ -o Native.so \ Native.c public interface JNA_Library extends Library { JNA_Library INSTANCE = (JNA_Library) Native.loadLibrary( "c", JNA_Library.class ); boolean isatty(int fileDescriptor); } public interface IsATTY_JNRFFI { boolean isatty(int fileDescriptor); } import jnr.ffi.LibraryLoader; IsATTY_JNRFFI c = LibraryLoader .create(IsATTY_JNRFFI.class) .load("c"); 📦 Third party 🎚 Easy to use 🏎 Almost as fast as JNI
  17. JNR-FFI Simple and faster than JNA • Supports native types

    (wstring, structure, union, etc.) • Handles classloader • Generates bytecode instead of reflection, way more efficient • Based upon JNI (invisible) to invoke its own native library • Also rely on libffi • Inspiration for Project Panama (JEP-191)
  18. Foreign Function and Memory var linker = Linker.nativeLinker(); var lookup

    = SymbolLookup.loaderLookup().or(linker.defaultLookup());
  19. Foreign Function and Memory var linker = Linker.nativeLinker(); var lookup

    = SymbolLookup.loaderLookup().or(linker.defaultLookup()); var isatty_symbol = lookup.find("isatty").orElseThrow();
  20. Foreign Function and Memory var linker = Linker.nativeLinker(); var lookup

    = SymbolLookup.loaderLookup().or(linker.defaultLookup()); var isatty_symbol = lookup.find("isatty").orElseThrow(); var isatty_MH = linker.downcallHandle(FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT ));
  21. Foreign Function and Memory var linker = Linker.nativeLinker(); var lookup

    = SymbolLookup.loaderLookup().or(linker.defaultLookup()); var isatty_symbol = lookup.find("isatty").orElseThrow(); var isatty_MH = linker.downcallHandle(FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT )); int result = isatty_MH.invokeExact(fileDescriptor); ☕ First party, made by Oracle 🎚 Easy to use 🏎 Faster than JNI Thanks to inlining and less conversions
  22. Binding Automation: JavaCPP Supports rich C++ constructs Generation at build

    time Configuration medium : Java class Existing binding (javacpp-presets)
  23. Binding Automation: jextract https://twitter.com/devoxx/status/1715655857797550378 External CLI tool Supports C/C++ constructs

    • Even complex declarations • No C code generated Maintained by JDK team ⚠ Stable but not finalized
  24. 3D/2D charting frameworks Jzy3D (2009) Native rendering Force : +10.000.000

    points | Limitation : compatibility issues JFreeChart (2001) Java rendering Force : runs everywhere | Limitation : 10.000 points
  25. 3D/2D charting frameworks Jzy3D draws 2D (2021) Native rendering Force

    : +10.000.000 points | Limitation : compatibility issues JFreeChart 3D : Orson Charts (2013) Java rendering Force : runs everywhere | Limitation : 10.000 points
  26. Standards for rendering with GPU 1992 1.0 2004 2.0 Since

    2016 (uncovered) Fixed Rendering Pipeline 2017 4.6 2008 3.0 Programmable Rendering Pipeline GLSL Flexible with Hardware heterogeneity = complex
  27. OpenGL Context Communication with a GPU is established with a

    GL Context GL context also provides informations about the GPU - Supported OpenGL versions - Supported OpenGL extensions - Supported GLSL versions - Hardware capabilities (Displays, etc) Dedicated native libraries for establishing GL context over operating systems - CGL - GLX - WGL GLU : GL Utility creates context over all OS
  28. Higher abstraction Unity etc VTK - A fully native visualization

    framework - Very large framework involving tons of DLL - Using JOGL only for getting GL context through JVM - Makes objects & algorithms available through JNI bindings - Java support dying - M1 support + Maven packaging - TC picture
  29. JOGL History (Java OpenGL) 1992 1995 1997 2003 2006 2010

    1 released by Silicon Graphics JDK 1 released by Sun Microsystem JOGL 1 initialized at Sun by Kenneth Russel jGL initialized by Robin Bing Yu Chen JOGL 1 open sourced by Sun Microsystem 2012 2020 2021 JDK 17 JEP-412 preview by Oracle Led by Mauricio Cimadamore JOGL 2 Freeze PanamaGL Prototypes jGL Freezing JOGL 2 released by Sven Gothel 2023 LWJGL Alternative to JOGL 2007 JDK 21 FFM API released by Oracle M1 Patch Java3D
  30. Limitations (technical) Compatibility issues on multiple combinations of : 2

    to 4 weeks to build & test a single line change! XP, Vista 7, 10, 11 Ubuntu Debian … 10 11 12 NVidia Intel Apple M1 … 8 9 11 14 17 … AWT, JavaFX, Swing, SWT x86-64 i586 arm aarch
  31. Limitations (organizational) Few OpenGL Bindings for Java - JOGL -

    LWJGL Small communities - Single owner - Short time contributors Sharp learning curves - Java / C / OpenGL / MultiOS Disclaimer : JOGL is fantastic!
  32. Limitations (organizational) Few OpenGL Bindings for Java - JOGL -

    LWJGL Small communities - Single owner - Short time contributors Sharp learning curves - Java / C / OpenGL / MultiOS Disclaimer : JOGL is fantastic!
  33. PanamaGL : 3D for JDK 21 • Alternative to existing

    3D rendering libraries (JOGL, LWJGL) • An interesting use case to discover the Foreign Function & Memory API • Started in 2019 : JDK 17, 18, 19, … to be continued Panama-GL
  34. JExtract https://gitlab.com/jzy3d/panama-gl-bindings/-/blob/main/scripts/linux/generate_opengl.sh JEXTRACT=/usr/lib/jvm/jextract-19/bin/jextract # Generate OpenGL Binding $JEXTRACT \ --target-package

    opengl.ubuntu.v20 \ --output ../../panama-gl-bindings-linux/src/generated/java/ \ --source \ --header-class-name glut_h \ -lGL \ -lglut \ -lGLU \ -lGLEW \ -I /usr/include/GL \ /usr/include/GL/glut.h
  35. FFM Utils • Arena (formerly MemorySession) ◦ Implicit ◦ Shared

    ◦ Confined ◦ […] • SegmentAllocator • MemorySegment ◦ For doubles, floats, strings, …
  36. Quizz!!!! Which are the three OS specific libraries allowing to

    open an OpenGL context? WGL, CGL, GLX are OS Specific libraries to create a context with the GPU GLU is a helper to invoke WGL CGL and GLX from any platform
  37. Les classes importantes de PanamaGL GLCanvas GLCanvasAWT GLCanvasSWT GLCanvasSwing GLCanvasJavaFX

    OffscreenRenderer On init() Or repaint() Or resize() Or destroy() 1
  38. Les classes importantes de PanamaGL GLCanvas GLCanvasAWT GLCanvasSWT GLCanvasSwing GLCanvasJavaFX

    OffscreenRenderer On init() Or repaint() Or resize() Or destroy() 1 2 GLContext GLContext_macOS_arm GLContext_macOS_x86 GLContext_linux_x86 GLContext_windows_x86 On init()
  39. Les classes importantes de PanamaGL GLCanvas GLCanvasAWT GLCanvasSWT GLCanvasSwing GLCanvasJavaFX

    OffscreenRenderer On init() Or repaint() Or resize() Or destroy() 1 GLEventListener init() display() reshape() destroy() 2 GLContext GLContext_macOS_arm GLContext_macOS_x86 GLContext_linux_x86 GLContext_windows_x86 GL GL_macOS_arm GL_macOS_x86 GL_linux_x86 GL_windows_x86 On init() 3 4
  40. Les classes importantes de PanamaGL GLCanvas GLCanvasAWT GLCanvasSWT GLCanvasSwing GLCanvasJavaFX

    OffscreenRenderer On init() Or repaint() Or resize() Or destroy() 1 GLEventListener init() display() reshape() destroy() 2 GLContext GLContext_macOS_arm GLContext_macOS_x86 GLContext_linux_x86 GLContext_windows_x86 GL GL_macOS_arm GL_macOS_x86 GL_linux_x86 GL_windows_x86 On init() 3 4 PanamaGLFactory
  41. Les classes importantes de PanamaGL GLCanvas GLCanvasAWT GLCanvasSWT GLCanvasSwing GLCanvasJavaFX

    OffscreenRenderer On init() Or repaint() Or resize() Or destroy() 1 GLEventListener init() display() reshape() destroy() 2 GLContext GLContext_macOS_arm GLContext_macOS_x86 GLContext_linux_x86 GLContext_windows_x86 GL GL_macOS_arm GL_macOS_x86 GL_linux_x86 GL_windows_x86 On init() 3 4 PanamaGLFactory
  42. Les classes importantes de PanamaGL GLCanvas GLCanvasAWT GLCanvasSWT GLCanvasSwing GLCanvasJavaFX

    OffscreenRenderer On init() Or repaint() Or resize() Or destroy() 1 GLEventListener init() display() reshape() destroy() 2 GLContext GLContext_macOS_arm GLContext_macOS_x86 GLContext_linux_x86 GLContext_windows_x86 GL GL_macOS_arm GL_macOS_x86 GL_linux_x86 GL_windows_x86 On init() 3 4 PanamaGLFactory
  43. Let’s run Jzy3D with PanamaGL Rendering backend selector • JOGL

    : native bindings • EmulGL : emulated OpenGL 1 in pure Java (CPU rendering) • PanamaGL new
  44. PanamaGL progress Supported Windowing Toolkits : AWT, Swing, JavaFX (SWT*

    merge request in progress) Supported Operating System : Linux, Windows, macOS (GLU* only, no CGL) Supported OpenGL Versions : 1, 2 (3, 4 coming) TODO : Bump to JDK 21 * SWT : Standard Widget LIbrary (Eclipse) * GLU : GL Utility library (cross platform library for all OS)
  45. PanamaGL Community Emmanuel Puybaret Sweet Home 3D Martin Pernollet Jzy3D

    Hannes Wellman Eclipse Christoph Läubrich Eclipse Mathieu Bastian Gephi Maurizio Cimadamore & Collaborators Oracle Leader of FFM API PanamaGL Hackathon (March 2023)
  46. Wrap up OpenGL & JOGL have a complex history (and

    future) PanamaGL uses a simple pattern to create a multi-platform library Join PanamaGL! Panama simplifies native access and opens the door to larger communities
  47. Question : Why not Vulkan? 1992 1.0 Since 2016 2017

    4.6 Since 2014 Stable everywhere for all GL versions More hardware control = lower level verbose
  48. JVM Restrictions JDK Team starting to enforce integrity and safety

    by default e.g. JDK 21, dynamic agent loading raise a warning, in later JDK disabled by default JNI might be restricted too Native memory must be explicitly enabled via cli or manifest (JDK 22) --enable-native-access=ALL-UNNAMED | {module} Enable-Native-Access: ALL-UNNAMED ℹ JEP draft: Prepare to Restrict The Use of JNI:
  49. Bad code can crash the VM # # A fatal

    error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x0000000104a0e9d8, pid=52896, tid=9987 # # JRE version: OpenJDK Runtime Environment (22.0+26) (build 22-ea+26-2112) # Java VM: OpenJDK 64-Bit Server VM (22-ea+26-2112, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64) # Problematic frame: # V [libjvm.dylib+0x9b69d8] Unsafe_GetByte(JNIEnv_*, _jobject*, _jobject*, long)+0x14c # # No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again # # An error report file with more information is saved as: # /Users/brice.dutheil/opensource/panama-watch/hs_err_pid52896.log # # If you would like to submit a bug report, please visit: # https://bugreport.java.com/bugreport/crash.jsp #
  50. Foreign Function and Memory Suppose the printf function Standard libc

    function are documented in man pages man 3 printf
  51. Foreign Function and Memory Suppose the printf function Standard libc

    function are documented in man pages man 3 printf
  52. Foreign Function and Memory The printf is very similar to

    System.out.printf, 1. A format string 2. Variable arguments
  53. Foreign Function and Memory The printf is very similar to

    System.out.printf System.out.printf("%-10.3f%n", pi); printf("%-10.3f\n", pi);
  54. Lets simplify to only call printf("printed to stdout"), that is

    without format and args Foreign Function and Memory var printf = LINKER.downcallHandle( SYMBOL_LOOKUP.find("printf").orElseThrow(), FunctionDescriptor.of( ValueLayout.JAVA_LONG, ValueLayout.ADDRESS ) );
  55. The process And then …handling memory The JVM The Over

    simplified “chart” ™ A native lib Java heap message
  56. The process And then …handling memory The JVM The Over

    simplified “chart” ™ A native lib Java heap message 1. Allocate new Off-heap segment of memory and copy content message
  57. The process And then …handling memory The JVM The Over

    simplified “chart” ™ A native lib Java heap message 2. Pass the pointer of native segment to function function message
  58. The process And then …handling memory The JVM The Over

    simplified “chart” ™ A native lib Java heap message Now there’s litter in the process memory ? function message
  59. Foreign Function and Memory 4 concepts to grasp • Arenas

    : allow memory lifecycle management, form allocation to disposal • Memory segments : a contiguous region of bytes • Memory layouts : data model in a memory segment • MethodHandle, VarHandle : polymorphic glue to access native function & memory
  60. Foreign Function and Memory • FFM API is designed in

    a way to prevent misuse for memory in particular. • FFM comes with Arenas, they control the lifecycle of native memory segments. ◦ Multiple characteristics are available ▪ Global ⟹ Deallocation ❌ ▪ Automatic ⟹ Deallocation❓ (when GC is passing by) ▪ Confined ⟹ Deallocate at close 🚧 Singled 🧵 ▪ Shared ⟹ Deallocate at close 🚧, Shared by multiple 🧵🧵🧵🧵… ◦ Implementing custom memory management is possible, ▪ Interfaces: Arena ➝ SegmentAllocator
  61. For the printf example, let’s create a confined arena Foreign

    Function and Memory try (var arena = Arena.ofConfined()) { }
  62. For the printf example, then let’s allocate and copy Java

    string content Foreign Function and Memory try (var arena = Arena.ofConfined()) { var memorySegment = arena.allocateFrom(str); }
  63. For the printf example, then make the call Foreign Function

    and Memory try (var arena = Arena.ofConfined()) { var memorySegment = arena.allocateFrom(str); return (long) printf.invoke(memorySegment); }
  64. For the printf example, then make the call Foreign Function

    and Memory try (var arena = Arena.ofConfined()) { var memorySegment = arena.allocateFrom(str); return (long) printf.invoke(memorySegment); } Deallocation when closed
  65. MemorySegment Represents a contiguous “segment” of bytes, on-heap or off-heap

    All segments have a start address in memory, this value is used as the pointer Accessible via MemorySegment::address All segments have a size Accessible via MemorySegment::byteSize A segment with a zero-length is an address
  66. MemorySegment Represents a contiguous “segment” of memory The process The

    JVM The Over simplified “chart” ™ A native lib Java heap 2E502E2E A0FB992E function Segments of memory
  67. Pointers everywhere Pointers are links to everything Even native functions

    are actually memory address Actually an address, i.e. a zero-length MemorySegment var printf = LINKER.downcallHandle( SYMBOL_LOOKUP.find("printf").orElseThrow(), FunctionDescriptor.of( ValueLayout.JAVA_LONG, ValueLayout.ADDRESS ) );
  68. MemorySegment Represents a contiguous “segment” of memory The process The

    JVM The Over simplified “chart” ™ A native lib Java heap 2E502E2E A0FB992E function Segments of memory 0-length
  69. Exchanging structured data Passing strings and raw types is almost

    easy as usual factories are there, e.g. allocateFrom(elementLayout, elementsArray) Fun begin with complex layout, use MemoryLayout factories
  70. Exchanging structured data typedef struct { uint8_t buf_len; uint8_t flags;

    } basic_struct; MemoryLayout.structLayout( JAVA_BYTE.withName("buf_len"), JAVA_BYTE.withName("flags"), ).withName("basic_struct"); uint8_t buf[8]; MemoryLayout.sequenceLayout( 64, JAVA_BYTE ).withName("buf") typedef struct { uint8_t buf[8]; uint8_t buf_len; uint8_t flags; } basic_struct; MemoryLayout.structLayout( bufLayout, JAVA_BYTE.withName("buf_len"), JAVA_BYTE.withName("flags"), ).withName("basic_struct");
  71. Exchanging structured data Passing strings and raw types is almost

    easy as usual factories are there, e.g. allocateFrom(elementLayout, elementsArray) Fun begin with complex layout, use MemoryLayout factories • structLayout, structure like • sequenceLayout, array like • unionLayout, allow different structs over the same storage • paddingLayout, padding require understanding of what means alignment
  72. Exchanging structured data Structured access to elements basic_struct_LAYOUT = MemoryLayout.structLayout(bufLayout,

    JAVA_BYTE.withName("buf_len"), JAVA_BYTE.withName("flags") ).withName("basic_struct");
  73. Exchanging structured data Structured access to elements basic_struct_LAYOUT = MemoryLayout.structLayout(bufLayout,

    JAVA_BYTE.withName("buf_len"), JAVA_BYTE.withName("flags") ).withName("basic_struct"); VarHandle flags = basic_struct_LAYOUT.varHandle( MemoryLayout.PathElement.groupElement("flags"));
  74. Exchanging structured data Structured access to elements basic_struct_LAYOUT = MemoryLayout.structLayout(bufLayout,

    JAVA_BYTE.withName("buf_len"), JAVA_BYTE.withName("flags") ).withName("basic_struct"); VarHandle flags = basic_struct_LAYOUT.varHandle( MemoryLayout.PathElement.groupElement("flags")); flags.get(memorySegment, 0L); flags.set(memorySegment, 0L, (byte) 0x2); storage index value
  75. Exchanging structured data Structured access to elements basic_struct_LAYOUT = MemoryLayout.structLayout(bufLayout,

    JAVA_BYTE.withName("buf_len"), JAVA_BYTE.withName("flags")).withName("basic_struct"); VarHandle flags = basic_struct_LAYOUT.varHandle( MemoryLayout.PathElement.groupElement("flags")); flags.get(memorySegment, 0L); flags.set(memorySegment, 0L, (byte) 0x2); Exception in thread "main" java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,MemorySegment,long,byte)void to (VarHandle,MemorySegment,int)void Notice the enforced type
  76. Handling error in C with errno errno is like a

    global error state in the C standard library, • it’s an integer variable set by the function that was called https://man7.org/linux/man-pages/man3/errno.3.html
  77. Handling error in C with errno errno is like a

    global error state in the C standard library, • it’s an integer variable set by the function that was called https://man7.org/linux/man-pages/man3/errno.3.html ioctl = LINKER.downcallHandle( SYMBOL_LOOKUP.find("ioctl").orElseThrow(), ioctlFunctionDescriptor, ); result = (int) ioctl.invokeExact(…)
  78. Handling error in C with errno errno is like a

    global error state in the C standard library, • it’s an integer variable set by the function that was called https://man7.org/linux/man-pages/man3/errno.3.html ioctl = LINKER.downcallHandle( SYMBOL_LOOKUP.find("ioctl").orElseThrow(), ioctlFunctionDescriptor, Linker.Option.captureCallState("errno") ); var csl = Linker.Option.captureStateLayout(); var errnoHandle = csl.varHandle( PathElement.groupElement("errno") ); result = (int) ioctl.invokeExact(…)
  79. Handling error in C with errno errno is like a

    global error state in the C standard library, • it’s an integer variable set by the function that was called https://man7.org/linux/man-pages/man3/errno.3.html ioctl = LINKER.downcallHandle( SYMBOL_LOOKUP.find("ioctl").orElseThrow(), ioctlFunctionDescriptor, Linker.Option.captureCallState("errno") ); var csl = Linker.Option.captureStateLayout(); var errnoHandle = csl.varHandle( PathElement.groupElement("errno") ); var storage = arena.allocate(csl); result = (int) ioctl.invokeExact(storage, …) switch ((int) errnoHandle.get(storage, 0L)) { 25 -> … // ENOTTY }
  80. MethodHandles As you noticed, this API relies a lot on

    invokedynamic features. • VarHandle • MethodHandle This means we benefit from all the features • binding arguments (similar to currying) • Interception • etc. and it’s errors • WrongMethodTypeException in particular
  81. Bad code example using printf Foreign Function and Memory try

    (var arena = Arena.ofConfined()) { var memorySegment = arena.allocateFrom(str); return (long) printf.invoke(memorySegment.address()); } Wrong
  82. Understanding WrongMethodTypeException If WrongMethodTypeException shows up, read the message Exception

    in thread "main" java.lang.invoke.WrongMethodTypeException: handle's method type (MemorySegment)long but found (long)long at java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:521) at java.base/java.lang.invoke.Invokers.checkExactType(Invokers.java:530) at io.whatever.App.c_printf(App.java:41)
  83. Understanding WrongMethodTypeException handle's method type (MemorySegment)long Indicates what was described

    in the descriptor but found (long)long Indicates what was seen on call site FunctionDescriptor.of( ValueLayout.JAVA_LONG, ValueLayout.ADDRESS ) ℹ ValueLayout.ADDRESS carrier type is MemorySegment (long) printf .invokeExact(segment.address());
  84. Variadic function in the Method handle section ? E.g. int

    printf(const char * restrict format, ...) • It’s not really supported in the JDK • JDK team opted for specialized downcall ◦ new downcall declaration for new sequence of argos • JDK advocate use of jextract for this feature to work. • Necessary to declare the downcallHandle with the index of the first variadic param LINKER.downcallHandle(…, Linker.Option.firstVariadicArg(2));
  85. Interacting with non C language Linker and SymbolLookup works with

    C conventions Foreign code needs to visible as a C function …(usually declared with extern) Swift @_cdecl("authenticate_user_touchid") public func authenticateUserApi() { … } Rust #[no_mangle] pub extern "C" fn abort() { … } Objective-C extern void cameraCapture();
  86. jextract Basic usage if you have a simple library …but…

    $ jextract \ --source \ --target-package org.lib \ --header-class-name Lib \ lib.h org.lib.Lib
  87. jextract Is library on system library path? If yes additional

    definitions are needed $ jextract … \ --library GL \ --library GLU \ … System.loadLibrary("GL"); System.loadLibrary("GLU"); Loaded via standard lookup /usr/lib /usr/lib64 /lib /lib64 ├── libGL.so └── libGLU.so /usr/local/lib /usr/local/lib64 generates
  88. jextract tips Is library on system library path ? If

    yes additional definitions are needed $ jextract … \ --library GL \ --library GLU \ … System.loadLibrary("GL"); System.loadLibrary("GLU"); If no, then don’t pass --library flag, and load the library yourself before using bindings. additional definitions are needed System.load("path/to/libblake3.so"); // Then use generated bindings
  89. jextract tips additional definitions are needed $ jextract \ --source

    \ --target-package org.lib \ --include-dir path1/to/include \ --include-dir path2/to/include \ lib.h E.g. Specific library include path : /usr/local/Cellar/ffmpeg@4/4.4.4/include Or system includes macOs: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ …or: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include Linux: /usr/include/ lib.h #include "path1/to/include/a.h" #include "path2/to/include/b.h" …
  90. jextract tips If multiple header are to be included, juste

    create your own header file Declare each include additional definitions are needed $ jextract …\ my-multiple.h my-multiple.h #include "path/to/libOneThing.h" #include "path/to/libOtherThing.h" …
  91. jextract tips How to be specific about which symbol ?

    Dump all symbols first $ jextract … \ --dump-includes my-dump.txt \ blake3.h my-dump.txt … #### Extracted from: …/blake3.h --include-struct blake3_chunk_state # header: …/c/blake3.h --include-struct blake3_hasher # header: …/blake3.h … --include-struct --include-function --include-constant …
  92. jextract tips How to be specific about which symbol ?

    Dump all symbols first $ jextract … \ --dump-includes my-dump.txt \ blake3.h my-dump.txt … #### Extracted from: …/blake3.h --include-struct blake3_chunk_state # header: …/c/blake3.h --include-struct blake3_hasher # header: …/blake3.h … Selected “symbols” $ jextract … \ --include-struct blake3_chunk_state \ --include-struct blake3_hasher \ blake3.h Select which symbol should have a binding --include-struct --include-function --include-constant …
  93. jextract tips Extract the configuration as an argument file (same

    as java command) With an argfile $ jextract … \ @my-config.txt --source --output path/to/gen-sources/jextract-blake3/java --target-package blake3 --header-class-name blake3_h --include-struct blake3_chunk_state # header: …/c/blake3.h --include-struct blake3_hasher # header: …/blake3.h … blake3.h
  94. Graalvm native-image support Currently baking, e.g. in JDK 21 preview

    API • Missing features: variadic functions, shared arenas, upcalls, etc. • Requires some special configuration at build time https://www.graalvm.org/latest/reference-manual/native-image/dynamic-features/foreign-interface/ class MyFFMFeature implements Feature { public void duringSetup(DuringSetupAccess access) { RuntimeForeignAccess.registerForDowncall( FunctionDescriptor.ofVoid() ); RuntimeForeignAccess.registerForDowncall( FunctionDescriptor.ofVoid(), Linker.Option.isTrivial() ); … } } --features=com.example.MyFFMFeature native-image.properties
  95. Wrap-up Accessing simple function is nice and efficient, • involves

    a few lines of code Essential classes : Linker, SymbolLookup, Arena, MemorySegment, MemoryLayout • Javadoc is rich, sometime verbose Lower level than JNA or JNR, • e.g. no mapping of records to struct and vice-versa Binding generation quickly gets a requisite • to get constants, variadic support, alignment/padding support, … Work is continuing to improve usability and support missing features • e.g. unsigned numbers, very long numbers (e.g. 512 bit long integers) • jextract tool is actively worked on by the Panama team of the JDK
  96. JEP 454: Foreign Function & Memory API Javadoc JDK 22

    java.base/java.lang.foreign Oracle Devguide for JDK 21: Foreign Function and Memory API JExtract : https://github.com/openjdk/jextract • From C to Java Code using Panama by Johannes Bechberger • The Panama Dojo: Black Belt Programming with Java 21 & the FFM API by Per Minborg https://youtu.be/t8c1Q2wJOoM • JVMLS: Project Panama - Foreign Function & Memory API by Maurizio Cimadamore https://youtu.be/kUFysMkMS00 PanamaGL : https://gitlab.com/jzy3d/panama-gl Slides link ⬆