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
--