Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down