<div dir="ltr">Hi Jorn<div><br></div><div>Thanks for the answer and reference to the documentation, which I couldn't find when I first encountered the problem.</div><div><br></div><div>The WINUSB_SETUP_PACKET struct isn't Windows specific. It is actually a basic, standardized message sent to USB devices. All USB message structs have an alignment of 1 as it cannot be controlled at what address they end up in a receive buffer and as the USB standard tries to save every byte. It wouldn't be strictly necessary in this case as Windows is always on the sender side.</div><div><br></div><div>Regards</div><div>Manuel</div><div><br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Am Di., 6. Mai 2025 um 15:46 Uhr schrieb Jorn Vernee <<a href="mailto:jorn.vernee@oracle.com">jorn.vernee@oracle.com</a>>:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><u></u>

  
  <div>
    <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 href="https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/foreign/Linker.html#describing-c-sigs" target="_blank">https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/foreign/Linker.html#describing-c-sigs</a><br>
    </p>
    <div>On 1-5-2025 20:07, Manuel
      Bleichenbacher wrote:<br>
    </div>
    <blockquote type="cite">
      
      <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" target="_blank">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" target="_blank">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>
  </div>

</blockquote></div>