Skip to content

Commit

Permalink
Linux: use latest jextract
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelbl committed Feb 20, 2024
1 parent 5fbfa68 commit 9f31481
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 20 deletions.
20 changes: 5 additions & 15 deletions java-does-usb/jextract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,11 @@ The resulting code is then committed to the source code repository. Before the c

## General limitations

- The binaries for *jextract* on https://jdk.java.net/jextract/ have not been updated for JDK 21. So it must be built from source. Instructions can be found at [Building & Testing](https://github.com/openjdk/jextract#building--testing).

- According to the jextract mailing list, it would be required to create separate code for Intel x64 and ARM64 architecture. And jextract would need to be run on each architecture separately (no cross-compilation). Fortunately, this doesn't seem to be the case. Linux code generated on Intel x64 also runs on ARM64 without change. The same holds for macOS. However, jextract needs to be run on each operating system separately.

- JDK 20 introduced a new feature for saving the thread-specific error values (`GetLastError()` on Windows, `errno` on Linux). To use it, an additional parameter must be added to function calls. Unfortunately, this is not yet supported by jextract. So a good number of function bindings have to be written manually.

- `typedef` and `struct`:

1. If only the `typedef` is included (`--include-typedef`), an empty Java class is generated.
2. If both *typedef* and the `struct` it refers to are included, the `typedef` class inherits from the `struct` class, which contains all the `struct` members.
3. If the `typedef` refers to an unnamed `struct`, the generated class contains all the `struct` members.

Case 1 looks like a bug.
- Dependencies: to be updated...

- *jextract* is not really transparent about what it does. It often skips elements without providing any information. In particular, it will silently skip a requested element in these cases:

Expand All @@ -37,7 +29,6 @@ The resulting code is then committed to the source code repository. Before the c
- `--include-typedef mytypedef` if `mytypedef` is a `typedef` for a primitive type.



## Linux

To run the script, the header files for *libudev* must be present. In most cases, they aren't install by default (in contrast to the library itself):
Expand All @@ -48,21 +39,20 @@ sudo apt-get install libudev-dev

On Linux, the limitations are:

- `usbdevice_fs.h`: The macro `USBDEVFS_CONTROL` and all similar ones are not generated. They are probably considered function-like macros. *jextract* does not generate code for function-like macros. But `USBDEVFS_CONTROL` evaluates to a constant.
- `usbdevice_fs.h`: The macro `USBDEVFS_CONTROL` and all similar ones are not generated. They are probably considered function-like macros. *jextract* does not generate code for function-like macros. `USBDEVFS_CONTROL` would evaluate to a constant.

- `sd-device.h` (header file for *libsystemd*): *jextract* fails with *"Error: /usr/include/inttypes.h:290:8: error: unknown type name 'intmax_t'"*. The reason is yet unknown. This code is currently not needed as *libudev* is used instead of *libsystemd*. They are related, *libsystemd* is the future solution, but it is missing support for monitoring devices.

- `libudev.h`: After code generation, the class `RuntimeHelper.java` in `.../linux/gen/udev` must be manually modified as the code to access the library does not work for the directory the library is located in. So replace:
- `libudev.h`: After code generation, the class `udev` in `.../linux/gen/udev` must be manually modified. In most Linux installations, there is no `libudev.so` alias to an actual version like `libudev.so.1.7.2`. The only alias is `libudev.so.1`. This is probably a deliberate decision by the authors as they do not plan to provide backward compatibility across major versions. But there does not seem to be a way to make *jextract* generate valid code for this setup. So manually replace:

```
System.loadLibrary("udev");
SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("udev"), LIBRARY_ARENA)
```

with:

```
SymbolLookup loaderLookup = SymbolLookup.libraryLookup("libudev.so", MemorySession.openImplicit());
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("libudev.so.1", LIBRARY_ARENA)
```


Expand Down
2 changes: 1 addition & 1 deletion java-does-usb/jextract/linux/gen_linux.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh

JEXTRACT=../../../../jextract-22/bin/jextract
JEXTRACT=../../../../jextract/build/jextract/bin/jextract

# errno.h
$JEXTRACT --output ../../src/main/java \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ public class epoll_data {
}

private static final GroupLayout $LAYOUT = MemoryLayout.unionLayout(
epoll.C_POINTER.withName("ptr").withByteAlignment(4),
epoll.C_POINTER.withName("ptr"),
epoll.C_INT.withName("fd"),
epoll.C_INT.withName("u32"),
epoll.C_LONG.withName("u64").withByteAlignment(4)
epoll.C_LONG.withName("u64")
).withName("epoll_data");

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public class epoll_event {
}

private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
epoll.C_INT.withByteAlignment(1).withName("events"),
epoll_data.layout().withName("data")
epoll.align(epoll.C_INT, 1).withName("events"),
epoll.align(epoll_data.layout(), 1).withName("data")
).withName("epoll_event");

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

// Manually fix. Otherwise, libudev will not be found if "libudev-dev" is not installed
// static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("udev"), LIBRARY_ARENA)
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup("libudev.so.1", LIBRARY_ARENA)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -46,6 +50,20 @@ static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fd
}
}

static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}

static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());

Expand Down

0 comments on commit 9f31481

Please sign in to comment.