diff --git a/dd-java-agent/agent-tooling/src/main/java25/datadog/trace/agent/tooling/servicediscovery/MemFDUnixWriterFFM.java b/dd-java-agent/agent-tooling/src/main/java25/datadog/trace/agent/tooling/servicediscovery/MemFDUnixWriterFFM.java index 27c3a8f3c44..602fb1723d8 100644 --- a/dd-java-agent/agent-tooling/src/main/java25/datadog/trace/agent/tooling/servicediscovery/MemFDUnixWriterFFM.java +++ b/dd-java-agent/agent-tooling/src/main/java25/datadog/trace/agent/tooling/servicediscovery/MemFDUnixWriterFFM.java @@ -20,65 +20,87 @@ public class MemFDUnixWriterFFM extends MemFDUnixWriter { private static final long ERRNO_OFFSET = CAPTURE_STATE_LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("errno")); - // Function handles - initialized once - private final MethodHandle syscallMH; - private final MethodHandle writeMH; - private final MethodHandle fcntlMH; + private static class Lazy { + // Function handles - initialized once + static final MethodHandle syscallMH; + static final MethodHandle writeMH; + static final MethodHandle fcntlMH; - private final MemorySegment captureState; + static { + MethodHandle syscall = null; + MethodHandle write = null; + MethodHandle fcntl = null; + try { + final Linker linker = Linker.nativeLinker(); + final SymbolLookup lookup = linker.defaultLookup(); + // long syscall(long number, ...) + // Note: variadic functions require special handling, we'll use a fixed signature + syscall = + linker.downcallHandle( + lookup.find("syscall").orElseThrow(), + FunctionDescriptor.of( + ValueLayout.JAVA_LONG, // return type: long + ValueLayout.JAVA_LONG, // syscall number + ValueLayout.ADDRESS, // const char* name + ValueLayout.JAVA_INT // int flags + ), + Linker.Option.captureCallState("errno")); - public MemFDUnixWriterFFM() { - final Linker linker = Linker.nativeLinker(); - final SymbolLookup LIBC = linker.defaultLookup(); + // ssize_t write(int fd, const void *buf, size_t count) + write = + linker.downcallHandle( + lookup.find("write").orElseThrow(), + FunctionDescriptor.of( + ValueLayout.JAVA_LONG, // return type: ssize_t + ValueLayout.JAVA_INT, // int fd + ValueLayout.ADDRESS, // const void* buf + ValueLayout.JAVA_LONG // size_t count + ), + Linker.Option.captureCallState("errno")); - // Allocate memory for capturing errno (need to be alive until the class instance is collected) - this.captureState = Arena.ofAuto().allocate(CAPTURE_STATE_LAYOUT); + // int fcntl(int fd, int cmd, ... /* arg */) + fcntl = + linker.downcallHandle( + lookup.find("fcntl").orElseThrow(), + FunctionDescriptor.of( + ValueLayout.JAVA_INT, // return type: int + ValueLayout.JAVA_INT, // int fd + ValueLayout.JAVA_INT, // int cmd + ValueLayout.JAVA_INT // int arg + ), + Linker.Option.captureCallState("errno")); + } catch (final Throwable ex) { + log.error("Failed to initialize MemFDUnixWriterFFM", ex); + } finally { + syscallMH = syscall; + writeMH = write; + fcntlMH = fcntl; + } + } - // long syscall(long number, ...) - // Note: variadic functions require special handling, we'll use a fixed signature - syscallMH = - linker.downcallHandle( - LIBC.find("syscall").orElseThrow(), - FunctionDescriptor.of( - ValueLayout.JAVA_LONG, // return type: long - ValueLayout.JAVA_LONG, // syscall number - ValueLayout.ADDRESS, // const char* name - ValueLayout.JAVA_INT // int flags - ), - Linker.Option.captureCallState("errno")); + static boolean isAvailable() { + // just check the first - either all null or all non-null. + return syscallMH != null; + } + } - // ssize_t write(int fd, const void *buf, size_t count) - writeMH = - linker.downcallHandle( - LIBC.find("write").orElseThrow(), - FunctionDescriptor.of( - ValueLayout.JAVA_LONG, // return type: ssize_t - ValueLayout.JAVA_INT, // int fd - ValueLayout.ADDRESS, // const void* buf - ValueLayout.JAVA_LONG // size_t count - ), - Linker.Option.captureCallState("errno")); + private final MemorySegment captureState; - // int fcntl(int fd, int cmd, ... /* arg */) - fcntlMH = - linker.downcallHandle( - LIBC.find("fcntl").orElseThrow(), - FunctionDescriptor.of( - ValueLayout.JAVA_INT, // return type: int - ValueLayout.JAVA_INT, // int fd - ValueLayout.JAVA_INT, // int cmd - ValueLayout.JAVA_INT // int arg - ), - Linker.Option.captureCallState("errno")); + public MemFDUnixWriterFFM() { + // Allocate memory for capturing errno (need to be alive until the class instance is collected) + this.captureState = Arena.ofAuto().allocate(CAPTURE_STATE_LAYOUT); } @Override protected long syscall(long number, String name, int flags) { + if (!Lazy.isAvailable()) { + return -1; + } try (Arena arena = Arena.ofConfined()) { // Allocate native string for file name MemorySegment fileNameSegment = arena.allocateFrom(name); // Call memfd_create via syscall, passing captureState as first arg - return (long) syscallMH.invoke(captureState, (long) number, fileNameSegment, flags); + return (long) Lazy.syscallMH.invoke(captureState, (long) number, fileNameSegment, flags); } catch (Throwable t) { log.error("Unable to make a syscall through FFM", t); return -1; @@ -87,13 +109,16 @@ protected long syscall(long number, String name, int flags) { @Override protected long write(int fd, byte[] payload) { + if (!Lazy.isAvailable()) { + return -1; + } try (Arena arena = Arena.ofConfined()) { // Allocate native memory for payload MemorySegment buffer = arena.allocate(payload.length); MemorySegment.copy(payload, 0, buffer, ValueLayout.JAVA_BYTE, 0, payload.length); // Write payload to memfd, passing captureState as first arg - return (long) writeMH.invoke(captureState, fd, buffer, (long) payload.length); + return (long) Lazy.writeMH.invoke(captureState, fd, buffer, (long) payload.length); } catch (Throwable t) { log.error("Unable to make a write call through FFM", t); return -1; @@ -102,8 +127,11 @@ protected long write(int fd, byte[] payload) { @Override protected int fcntl(int fd, int cmd, int arg) { + if (!Lazy.isAvailable()) { + return -1; + } try { - return (int) fcntlMH.invoke(captureState, fd, cmd, arg); + return (int) Lazy.fcntlMH.invoke(captureState, fd, cmd, arg); } catch (Throwable t) { log.error("Unable to make a fcntl call through FFM", t); return -1; @@ -112,6 +140,9 @@ protected int fcntl(int fd, int cmd, int arg) { @Override protected int getLastError() { + if (!Lazy.isAvailable()) { + return -1; + } try { // Read errno from the captured state memory segment return captureState.get(ValueLayout.JAVA_INT, ERRNO_OFFSET);