<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p>Hey Manuel,</p>
<p>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:<br>
<br>
However, a native linker can still reject well-formed function
descriptors, according to platform-specific rules. For example,
some native linkers may reject <em>packed</em> struct layouts --
struct layouts whose member layouts feature relaxed alignment
constraints, to avoid the insertion of additional padding. </p>
<p>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.<br>
</p>
<p>Jorn</p>
<p>[1]:
<a class="moz-txt-link-freetext" href="https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/foreign/Linker.html#describing-c-sigs">https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/foreign/Linker.html#describing-c-sigs</a><br>
</p>
<div class="moz-cite-prefix">On 1-5-2025 20:07, Manuel
Bleichenbacher wrote:<br>
</div>
<blockquote type="cite" cite="mid:CAA7F5jLXXmtSvGavkcEc-bSGN=2-Bguaf_0OxZ1J0mx4_YbFSQ@mail.gmail.com">
<div dir="ltr">Hi Panama team,
<div><br>
</div>
<div>This code calling a Windows API fails:</div>
<div><br>
</div>
<div><font face="monospace"> try (var arena =
Arena.ofConfined()) {<br>
var transfer =
_WINUSB_SETUP_PACKET.allocate(arena);<br>
WinUsb.WinUsb_ControlTransfer(NULL, transfer,
NULL, 0, NULL, NULL);<br>
}<br>
</font></div>
<div><br>
</div>
<div>It throws an exception:</div>
<div><br>
</div>
<div><font face="monospace"> Exception in thread "main"
java.lang.ExceptionInInitializerError<br>
at
org.example.gen.WinUsb.WinUsb_ControlTransfer(WinUsb.java:121)<br>
at org.example.App.main(App.java:53)<br>
Caused by: java.lang.IllegalArgumentException:
Unsupported layout: 1%s2<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.checkSupported(AbstractLinker.java:293)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayoutRecursive(AbstractLinker.java:186)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.checkStructMember(AbstractLinker.java:181)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayoutRecursive(AbstractLinker.java:196)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayout(AbstractLinker.java:175)<br>
at
java.base/java.lang.Iterable.forEach(Iterable.java:75)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.checkLayouts(AbstractLinker.java:167)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.downcallHandle0(AbstractLinker.java:97)<br>
at
java.base/jdk.internal.foreign.abi.AbstractLinker.downcallHandle(AbstractLinker.java:84)<br>
at
org.example.gen.WinUsb$WinUsb_ControlTransfer.<clinit>(WinUsb.java:82)<br>
... 2 more<br>
</font></div>
<div><br>
</div>
<div>Both WinUsb_ControlTransfer() and _WINUSB_SETUP_PACKET have
been generated with jextract and are correct as far as I can
tell.</div>
<div><br>
</div>
<div>Two aspects are special in this call:</div>
<div>
<ol>
<li> _WINUSB_SETUP_PACKET is a packed struct with an
alignment of 1 byte.</li>
<li>_WINUSB_SETUP_PACKET is passed by value.</li>
</ol>
<div>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?</div>
</div>
<div><br>
</div>
<div>For reference, here is the relevant jextract generated
code:</div>
<div><br>
</div>
<div>WINUSB_SETUP_PACKET (<a href="https://learn.microsoft.com/en-us/windows/win32/api/winusb/ns-winusb-winusb_setup_packet" moz-do-not-send="true" class="moz-txt-link-freetext">https://learn.microsoft.com/en-us/windows/win32/api/winusb/ns-winusb-winusb_setup_packet</a>):</div>
<div><br>
</div>
<div><font face="monospace"> private static final GroupLayout
$LAYOUT = MemoryLayout.structLayout(<br>
WinUsb.C_CHAR.withName("RequestType"),<br>
WinUsb.C_CHAR.withName("Request"),<br>
WinUsb.align(WinUsb.C_SHORT, 1).withName("Value"),<br>
WinUsb.align(WinUsb.C_SHORT, 1).withName("Index"),<br>
WinUsb.align(WinUsb.C_SHORT, 1).withName("Length")<br>
).withName("_WINUSB_SETUP_PACKET");<br>
</font></div>
<div><br>
</div>
<div>WinUsb_ControlTransfer (<a href="https://learn.microsoft.com/en-us/windows/win32/api/winusb/nf-winusb-winusb_controltransfer" moz-do-not-send="true" class="moz-txt-link-freetext">https://learn.microsoft.com/en-us/windows/win32/api/winusb/nf-winusb-winusb_controltransfer</a>):</div>
<div><br>
</div>
<div><font face="monospace"> public static final
FunctionDescriptor DESC = FunctionDescriptor.of(<br>
WinUsb.C_INT,<br>
WinUsb.C_POINTER,<br>
_WINUSB_SETUP_PACKET.layout(),<br>
WinUsb.C_POINTER,<br>
WinUsb.C_LONG,<br>
WinUsb.C_POINTER,<br>
WinUsb.C_POINTER<br>
);<br>
</font></div>
<div><br>
</div>
<div>Regards,</div>
<div>Manuel</div>
<div><br>
</div>
</div>
</blockquote>
</body>
</html>