On 07/07/2026 10:31, Chandru Kumaresan wrote:
Hi everyone,

On AArch64, the autoload INIT_WRAPPER passes func_info* via x30 ("mov
x0, x30").  This works for the first init stage (std_dll_init), reached
by the trampoline's "blr".  It breaks for a chained second init, because
dll_chain reaches it via "br x1" without updating x30.

ws2_32 is the only DLL with such a chain (std_dll_init -> wsock_init ->
dll_func_load).  wsock_init received garbage in x0, so WSAStartup was
never called; every ws2_32 call failed with WSANOTINITIALISED / EFAULT.

dll_chain also runs twice for ws2_32, pushing a 16-byte frame each time.
wsock_init consumes its arg from x30, not the stack, so the first frame
strands.  dll_func_load then restores caller-save registers from a
shifted offset, corrupting the first call's arguments.

Fix: copy func_info into x30 in dll_chain before the tail-branch, and
give AArch64 a dedicated _wsock_init wrapper that drops the stranded
frame ("add sp, sp, #16") before chaining to dll_func_load.

The loss of generality here makes me slightly unhappy.

(Although, practically, it's extremely likely that we'll ever have to deal with another DLL having special initialization requirements like ws2_32 does)

But.. it seems like maybe this could be written without dll_chain as a tail call, which would also balance the stack, so something like:

push x0 and lr
move lr to x0
bl x1
pop x0 and lr
ret

idk, this code is a real maze and every time I look at it I have to spend an hour drawing diagrams. So I'm perfectly prepared to be told that won't work!

It seems like there must be a generic way to write this for aarch64, but maybe it looks so different from the existing implementation it's not worth the effort of finding it...

---
  winsup/cygwin/autoload.cc | 28 ++++++++++++++++++++++++++--
  1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index 7054511b6..8d0f2a59e 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -302,6 +302,10 @@ dll_func_load:                                           
\n\
    .global    dll_chain                                   \n\
  dll_chain:                                               \n\
    stp        x0, xzr, [sp, #-16]! // x0 = func_info* (= ret.high); push for 
dll_func_load\n\
+  mov        x30, x0              // also pass func_info in x30: a chained 
INIT_WRAPPER\n\
+                                  // (e.g. _wsock_init) reads its arg from 
x30, but is\n\
+                                  // reached here via 'br' which would 
otherwise leave\n\
+                                  // x30 stale.  dll_func_load ignores x30 
(reads [sp]).\n\
    br         x1                   // x1 = dll->init (= ret.low); tail-call 
resolver\n\
  ");
  #else
@@ -481,9 +485,29 @@ std_dll_init (struct func_info *func)

  /* Initialization function for winsock stuff. */

-#if defined(__x86_64__) || defined(__aarch64__)
-/* See above comment preceeding std_dll_init. */
+#if defined(__x86_64__)
+/* See above comment preceding std_dll_init. */
  INIT_WRAPPER (wsock_init)
+#elif defined(__aarch64__)
+__asm__("\n\
+  .text                                                  \n\
+  .p2align 2                                             \n\
+  .seh_proc _wsock_init                                  \n\
+_wsock_init:                                             \n\
+  stp        x29, x30, [sp, #-16]!  // save fp/lr, open our 16-byte frame\n\
+  .seh_save_fplr_x 16                                   \n\
+  .seh_endprologue                                      \n\
+  mov        x0, x30              // x0 = func_info  (the wsock_init() 
argument)\n\
+  bl         wsock_init           // run WSAStartup; returns x0=func_info, 
x1=dll_func_load\n\
+  ldp        x29, xzr, [sp], #16  // restore fp, discard saved lr, close our 
frame\n\
+  add        sp, sp, #16          // drop the stranded dll_chain frame so 
the\n\
+                                  // downstream dll_func_load sees exactly 
one\n\
+                                  // dll_chain frame above the trampoline 
frame\n\
+  adrp       x30, dll_chain       // x30 = &dll_chain ...\n\
+  add        x30, x30, #:lo12:dll_chain  // ... so the 'ret' below tail-chains 
there\n\
+  ret                             // -> dll_chain, which tail-calls x1 
(dll_func_load)\n\
+  .seh_endproc                                          \n\
+");
  #else
  #error unimplemented for this target
  #endif
--

Reply via email to