Passing unaligned structs as function arguments

Jorn Vernee jorn.vernee at oracle.com
Tue May 6 13:37:51 UTC 2025


Hey Manuel,

The current linker implementations do indeed not support packed structs. 
Packed structs, as well as structs with bit fields, are not specified 
very well by most ABIs, so the decision was made to conservatively 
reject them on all platforms. Most libraries avoid using by-value packed 
structs for this reason as well. This is covered by the documentation at 
the end of the 'Describing C signatures' section [1], where it states:

     However, a native linker can still reject well-formed function 
descriptors, according to platform-specific rules. For example, some 
native linkers may reject /packed/ struct layouts -- struct layouts 
whose member layouts feature relaxed alignment constraints, to avoid the 
insertion of additional padding.

In this case, the alignment of the fields does not have any impact on 
the layout of the struct (there is no padding either way). So, dropping 
the alignment, like you did, should result on the function being 
callable. I suspect the alignment is set to 1 for these fields to allow 
the struct to be placed at an address that is 1-byte-aligned.

Jorn

[1]: 
https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/foreign/Linker.html#describing-c-sigs

On 1-5-2025 20:07, Manuel Bleichenbacher wrote:
> Hi Panama team,
>
> This code calling a Windows API fails:
>
>         try (var arena = Arena.ofConfined()) {
>             var transfer = _WINUSB_SETUP_PACKET.allocate(arena);
>             WinUsb.WinUsb_ControlTransfer(NULL, transfer, NULL, 0, 
> NULL, NULL);
>         }
>
> It throws an exception:
>
>     Exception in thread "main" java.lang.ExceptionInInitializerError
>         at org.example.gen.WinUsb.WinUsb_ControlTransfer(WinUsb.java:121)
>         at org.example.App.main(App.java:53)
>     Caused by: java.lang.IllegalArgumentException: Unsupported layout: 
> 1%s2
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.checkSupported(AbstractLinker.java:293)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayoutRecursive(AbstractLinker.java:186)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.checkStructMember(AbstractLinker.java:181)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayoutRecursive(AbstractLinker.java:196)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayout(AbstractLinker.java:175)
>         at java.base/java.lang.Iterable.forEach(Iterable.java:75)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayouts(AbstractLinker.java:167)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.downcallHandle0(AbstractLinker.java:97)
>         at 
> java.base/jdk.internal.foreign.abi.AbstractLinker.downcallHandle(AbstractLinker.java:84)
>         at 
> org.example.gen.WinUsb$WinUsb_ControlTransfer.<clinit>(WinUsb.java:82)
>         ... 2 more
>
> Both WinUsb_ControlTransfer() and _WINUSB_SETUP_PACKET have been 
> generated with jextract and are correct as far as I can tell.
>
> Two aspects are special in this call:
>
>  1.  _WINUSB_SETUP_PACKET is a packed struct with an alignment of 1 byte.
>  2. _WINUSB_SETUP_PACKET is passed by value.
>
> Is it a restriction of FFM that unaligned structs cannot be passed by 
> value (it works if the natural alignment of 2 bytes is used)? Or what 
> is the issue here?
>
> For reference, here is the relevant jextract generated code:
>
> WINUSB_SETUP_PACKET 
> (https://learn.microsoft.com/en-us/windows/win32/api/winusb/ns-winusb-winusb_setup_packet):
>
>     private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
>         WinUsb.C_CHAR.withName("RequestType"),
>         WinUsb.C_CHAR.withName("Request"),
>         WinUsb.align(WinUsb.C_SHORT, 1).withName("Value"),
>         WinUsb.align(WinUsb.C_SHORT, 1).withName("Index"),
>         WinUsb.align(WinUsb.C_SHORT, 1).withName("Length")
>     ).withName("_WINUSB_SETUP_PACKET");
>
> WinUsb_ControlTransfer 
> (https://learn.microsoft.com/en-us/windows/win32/api/winusb/nf-winusb-winusb_controltransfer):
>
>     public static final FunctionDescriptor DESC = FunctionDescriptor.of(
>         WinUsb.C_INT,
>         WinUsb.C_POINTER,
>         _WINUSB_SETUP_PACKET.layout(),
>         WinUsb.C_POINTER,
>         WinUsb.C_LONG,
>         WinUsb.C_POINTER,
>         WinUsb.C_POINTER
>     );
>
> Regards,
> Manuel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20250506/fbe3889d/attachment.htm>


More information about the panama-dev mailing list