Slide 1

Slide 1 text

PanamaGL & FFM API Exploit native function using Java Martin Pernollet Brice Dutheil

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Agenda - Introduction : why native access is great - PanamaGL : 3D rendering in Java with FFM API & JExtract - Panama : deep dive in FFM API & JExtract

Slide 5

Slide 5 text

Introduction : why native Wider Ecosystem Hardware Access Improved Performance CPU GPU USB

Slide 6

Slide 6 text

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)

Slide 7

Slide 7 text

2024-03-19 22 https://www.oracle.com/a/ocom/img/obic-java-cup.svg https://upload.wikimedia.org/wikipedia/commons/1/18/OpenJDK_logo.svg The people The Java spec version

Slide 8

Slide 8 text

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. “

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

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/

Slide 15

Slide 15 text

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 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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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 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

Slide 18

Slide 18 text

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)

Slide 19

Slide 19 text

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 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

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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 ));

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Binding Automation: JavaCPP Supports rich C++ constructs Generation at build time Configuration medium : Java class Existing binding (javacpp-presets)

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Panama GL A 3D visualization framework for Java

Slide 28

Slide 28 text

3D visualization in Java is an active topic

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

OpenGL Interface Draws Point, Lines, Triangles, Polygons Matrices for 3D/2D projection Cameras, Lights

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

How does JOGL bind to native OpenGL?

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Limitations (technical) Requires a complex CI with real hardware to build and test

Slide 39

Slide 39 text

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!

Slide 40

Slide 40 text

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!

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

FFM & JExtract to the rescue Generated bindings are only made of Java files!

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Generated code is platform specific

Slide 45

Slide 45 text

A pattern for bundling specific bindings (1)

Slide 46

Slide 46 text

GL interface splitted by hardware version

Slide 47

Slide 47 text

GL wrapper

Slide 48

Slide 48 text

Abstract GL

Slide 49

Slide 49 text

FFM Utils ● Arena (formerly MemorySession) ○ Implicit ○ Shared ○ Confined ○ […] ● SegmentAllocator ● MemorySegment ○ For doubles, floats, strings, …

Slide 50

Slide 50 text

JExtractGL : libs need a bundling

Slide 51

Slide 51 text

Quizz!!!! Which are the three OS specific libraries allowing to open an OpenGL context?

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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()

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

OpenGL Teapot Demo with PanamaGL

Slide 60

Slide 60 text

OpenGL Teapot Demo with PanamaGL

Slide 61

Slide 61 text

OpenGL Teapot Demo with PanamaGL

Slide 62

Slide 62 text

OpenGL Teapot Demo with PanamaGL

Slide 63

Slide 63 text

The Factory

Slide 64

Slide 64 text

Let’s run Jzy3D with PanamaGL Rendering backend selector ● JOGL : native bindings ● EmulGL : emulated OpenGL 1 in pure Java (CPU rendering) ● PanamaGL new

Slide 65

Slide 65 text

Jzy3D Demo with PanamaGL

Slide 66

Slide 66 text

Jzy3D Demo with PanamaGL

Slide 67

Slide 67 text

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)

Slide 68

Slide 68 text

Java Visualization People

Slide 69

Slide 69 text

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)

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

Offscreen Renderer - Use FBO to render in a BufferedImage - Schedule single threaded OpenGL

Slide 73

Slide 73 text

Back to FFM The basics

Slide 74

Slide 74 text

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:

Slide 75

Slide 75 text

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 #

Slide 76

Slide 76 text

Foreign Function and Memory Suppose the printf function

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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);

Slide 81

Slide 81 text

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 ) );

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

For the printf example, let’s create a confined arena Foreign Function and Memory try (var arena = Arena.ofConfined()) { }

Slide 89

Slide 89 text

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); }

Slide 90

Slide 90 text

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); }

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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 ) );

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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");

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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");

Slide 100

Slide 100 text

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"));

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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(…)

Slide 105

Slide 105 text

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(…)

Slide 106

Slide 106 text

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 }

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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)

Slide 110

Slide 110 text

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());

Slide 111

Slide 111 text

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));

Slide 112

Slide 112 text

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();

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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" …

Slide 117

Slide 117 text

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" …

Slide 118

Slide 118 text

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 …

Slide 119

Slide 119 text

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 …

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

A note about GraalVM

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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 ⬆