[PATCH v3] Cygwin: autoload: fix ws2_32 chained init on AArch64
Chandru Kumaresan <[email protected]> Fri, 26 Jun 2026 09:03:03 +0000
| Newsgroups | gmane.os.cygwin.patches |
|---|---|
| Message-ID | <PN0P287MB0295EC86F97A259F0C99818F92EB2@PN0P287MB0295.INDP287.PROD.OUTLOOK.COM> |
Hi Jon,
> I've applied this patch.
Thanks!!
>Just so I'm doing my due diligence, I'd like you confirm that you are
>submitting this under an open source license as per [1], and you and
>your colleagues at multicoreware are authorized to do so.
We have been contributing to Cygwin for about a year, all modifying
existing files rather than creating new ones. We will confirm and get
back to you shortly.
>* Looking at the git history, the comment on the problem the
>no_resolve_on_fork flag is working around has disappeared, but the
>functionality is still there. Is it still needed?
The no_resolve_on_fork removal addresses your question about whether
that flag is still needed -- it was always 0 at every call site, so
the patch drops it entirely.
>* Since it's just data, it seems to me that the initializations of the
>various instances of struct dll_info could actually all be written in C.
The LoadLibrary elision question and the "write dll_info inits in C"
note are out of scope for this patch; happy to look at those separately.
Inline patch
---
winsup/cygwin/autoload.cc | 84 +++++++++++++++++++++++++--------------
1 file changed, 54 insertions(+), 30 deletions(-)
diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index 7054511b6..c425191b9 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -85,13 +85,13 @@ bool NO_COPY wsock_started;
The macro WORD64 stands in for .quad/.xword, and .balign (which means
"align to N bytes" on all targets, unlike .align) is used for
alignment. */
-#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __asm__ ("\n\
+#define LoadDLLprime(dllname, init_also) __asm__ ("\n\
.ifndef " #dllname "_primed \n\
.section .data_cygwin_nocopy,\"w\" \n\
.balign 8 \n\
." #dllname "_info: \n\
" WORD64 " _std_dll_init \n\
- " WORD64 " " #no_resolve_on_fork " \n\
+ " WORD64 " 0 \n\
.long -1 \n\
.balign 8 \n\
" WORD64 " " #init_also " \n\
@@ -108,12 +108,12 @@ bool NO_COPY wsock_started;
#define LoadDLLfuncEx(name, dllname, notimp) \
LoadDLLfuncEx2(name, dllname, notimp, 0)
#define LoadDLLfuncEx2(name, dllname, notimp, err) \
- LoadDLLfuncEx3(name, dllname, notimp, err, 0)
+ LoadDLLfuncEx3(name, dllname, notimp, err)
/* Main DLL setup stuff. */
#if defined(__x86_64__)
-#define LoadDLLfuncEx3(name, dllname, notimp, err, no_resolve_on_fork) \
- LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
+#define LoadDLLfuncEx3(name, dllname, notimp, err) \
+ LoadDLLprime (dllname, dll_func_load) \
__asm__ (" \n\
.section ." #dllname "_autoload_text,\"wx\" \n\
.global " #name " \n\
@@ -138,8 +138,8 @@ _win32_" #name ": \n\
.text \n\
");
#elif defined(__aarch64__)
-#define LoadDLLfuncEx3(name, dllname, notimp, err, no_resolve_on_fork) \
- LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
+#define LoadDLLfuncEx3(name, dllname, notimp, err) \
+ LoadDLLprime (dllname, dll_func_load) \
__asm__ ( "\n\
.section ." #dllname "_autoload_text,\"wx\" \n\
.global " #name " \n\
@@ -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
@@ -438,7 +442,7 @@ std_dll_init (struct func_info *func)
yield ();
}
while (InterlockedIncrement (&dll->here));
- else if ((uintptr_t) dll->handle <= 1)
+ else if (!dll->handle)
{
fenv_t fpuenv;
fegetenv (&fpuenv);
@@ -461,7 +465,7 @@ std_dll_init (struct func_info *func)
if (i < RETRY_COUNT)
yield ();
}
- if ((uintptr_t) dll->handle <= 1)
+ if (!dll->handle)
{
if ((func->decoration & 1))
dll->handle = INVALID_HANDLE_VALUE;
@@ -481,9 +485,29 @@ std_dll_init (struct func_info *func)
/* Initialization function for winsock stuff. */
-#if defined(__x86_64__) || defined(__aarch64__)
+#if defined(__x86_64__)
/* See above comment preceeding 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 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 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 so the 'ret' below tail-chains there\n\
+ add x30, x30, #:lo12:dll_chain // -> dll_chain, which tail-calls x1 (dll_func_load)\n\
+ ret \n\
+ .seh_endproc \n\
+");
#else
#error unimplemented for this target
#endif
@@ -534,7 +558,7 @@ wsock_init (struct func_info *func)
return ret.ll;
}
-LoadDLLprime (ws2_32, _wsock_init, 0)
+LoadDLLprime (ws2_32, _wsock_init)
LoadDLLfunc (CheckTokenMembership, advapi32)
LoadDLLfunc (CreateProcessAsUserW, advapi32)
@@ -711,25 +735,25 @@ LoadDLLfuncEx2 (CreateProfile, userenv, 1, 1)
LoadDLLfunc (DestroyEnvironmentBlock, userenv)
LoadDLLfunc (LoadUserProfileW, userenv)
-LoadDLLfuncEx3 (waveInAddBuffer, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInClose, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInGetNumDevs, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInOpen, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInPrepareHeader, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInReset, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInStart, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveInUnprepareHeader, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutClose, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutGetNumDevs, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutGetVolume, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutOpen, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutPrepareHeader, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutReset, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutSetVolume, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutUnprepareHeader, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutWrite, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutMessage, winmm, 1, 0, 1)
-LoadDLLfuncEx3 (waveOutGetDevCapsA, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInAddBuffer, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInClose, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInGetNumDevs, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInOpen, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInPrepareHeader, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInReset, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInStart, winmm, 1, 0)
+LoadDLLfuncEx3 (waveInUnprepareHeader, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutClose, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutGetNumDevs, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutGetVolume, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutOpen, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutPrepareHeader, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutReset, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutSetVolume, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutUnprepareHeader, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutWrite, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutMessage, winmm, 1, 0)
+LoadDLLfuncEx3 (waveOutGetDevCapsA, winmm, 1, 0)
LoadDLLfunc (accept, ws2_32)
LoadDLLfunc (bind, ws2_32)
--
2.49.0.windows.1
Cygwin-autoload-fix-ws2_32-chained-init-on-AArch64.patch
(application/octet-stream, 9.1 KB)
From ecad9c50ab7b59ee553ed8c851f5a98256a6bdea Mon Sep 17 00:00:00 2001 From: chandru-mcw <[email protected]> Date: Fri, 26 Jun 2026 11:21:02 +0530 Subject: [PATCH v3] Cygwin: autoload: fix ws2_32 chained init on AArch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Also remove the no_resolve_on_fork parameter from LoadDLLprime / LoadDLLfuncEx3 (was always 0) and fix the std_dll_init handle sentinel from "(uintptr_t)handle <= 1" to "!handle". Signed-off-by: Radek BartoĊ <[email protected]> Signed-off-by: Thirumalai Nagalingam <[email protected]> Signed-off-by: Chandru Kumaresan <[email protected]> --- winsup/cygwin/autoload.cc | 84 +++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 7054511b6..c425191b9 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -85,13 +85,13 @@ bool NO_COPY wsock_started; The macro WORD64 stands in for .quad/.xword, and .balign (which means "align to N bytes" on all targets, unlike .align) is used for alignment. */ -#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __asm__ ("\n\ +#define LoadDLLprime(dllname, init_also) __asm__ ("\n\ .ifndef " #dllname "_primed \n\ .section .data_cygwin_nocopy,\"w\" \n\ .balign 8 \n\ ." #dllname "_info: \n\ " WORD64 " _std_dll_init \n\ - " WORD64 " " #no_resolve_on_fork " \n\ + " WORD64 " 0 \n\ .long -1 \n\ .balign 8 \n\ " WORD64 " " #init_also " \n\ @@ -108,12 +108,12 @@ bool NO_COPY wsock_started; #define LoadDLLfuncEx(name, dllname, notimp) \ LoadDLLfuncEx2(name, dllname, notimp, 0) #define LoadDLLfuncEx2(name, dllname, notimp, err) \ - LoadDLLfuncEx3(name, dllname, notimp, err, 0) + LoadDLLfuncEx3(name, dllname, notimp, err) /* Main DLL setup stuff. */ #if defined(__x86_64__) -#define LoadDLLfuncEx3(name, dllname, notimp, err, no_resolve_on_fork) \ - LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \ +#define LoadDLLfuncEx3(name, dllname, notimp, err) \ + LoadDLLprime (dllname, dll_func_load) \ __asm__ (" \n\ .section ." #dllname "_autoload_text,\"wx\" \n\ .global " #name " \n\ @@ -138,8 +138,8 @@ _win32_" #name ": \n\ .text \n\ "); #elif defined(__aarch64__) -#define LoadDLLfuncEx3(name, dllname, notimp, err, no_resolve_on_fork) \ - LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \ +#define LoadDLLfuncEx3(name, dllname, notimp, err) \ + LoadDLLprime (dllname, dll_func_load) \ __asm__ ( "\n\ .section ." #dllname "_autoload_text,\"wx\" \n\ .global " #name " \n\ @@ -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 @@ -438,7 +442,7 @@ std_dll_init (struct func_info *func) yield (); } while (InterlockedIncrement (&dll->here)); - else if ((uintptr_t) dll->handle <= 1) + else if (!dll->handle) { fenv_t fpuenv; fegetenv (&fpuenv); @@ -461,7 +465,7 @@ std_dll_init (struct func_info *func) if (i < RETRY_COUNT) yield (); } - if ((uintptr_t) dll->handle <= 1) + if (!dll->handle) { if ((func->decoration & 1)) dll->handle = INVALID_HANDLE_VALUE; @@ -481,9 +485,29 @@ std_dll_init (struct func_info *func) /* Initialization function for winsock stuff. */ -#if defined(__x86_64__) || defined(__aarch64__) +#if defined(__x86_64__) /* See above comment preceeding 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 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 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 so the 'ret' below tail-chains there\n\ + add x30, x30, #:lo12:dll_chain // -> dll_chain, which tail-calls x1 (dll_func_load)\n\ + ret \n\ + .seh_endproc \n\ +"); #else #error unimplemented for this target #endif @@ -534,7 +558,7 @@ wsock_init (struct func_info *func) return ret.ll; } -LoadDLLprime (ws2_32, _wsock_init, 0) +LoadDLLprime (ws2_32, _wsock_init) LoadDLLfunc (CheckTokenMembership, advapi32) LoadDLLfunc (CreateProcessAsUserW, advapi32) @@ -711,25 +735,25 @@ LoadDLLfuncEx2 (CreateProfile, userenv, 1, 1) LoadDLLfunc (DestroyEnvironmentBlock, userenv) LoadDLLfunc (LoadUserProfileW, userenv) -LoadDLLfuncEx3 (waveInAddBuffer, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInClose, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInGetNumDevs, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInOpen, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInPrepareHeader, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInReset, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInStart, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveInUnprepareHeader, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutClose, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutGetNumDevs, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutGetVolume, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutOpen, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutPrepareHeader, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutReset, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutSetVolume, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutUnprepareHeader, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutWrite, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutMessage, winmm, 1, 0, 1) -LoadDLLfuncEx3 (waveOutGetDevCapsA, winmm, 1, 0, 1) +LoadDLLfuncEx3 (waveInAddBuffer, winmm, 1, 0) +LoadDLLfuncEx3 (waveInClose, winmm, 1, 0) +LoadDLLfuncEx3 (waveInGetNumDevs, winmm, 1, 0) +LoadDLLfuncEx3 (waveInOpen, winmm, 1, 0) +LoadDLLfuncEx3 (waveInPrepareHeader, winmm, 1, 0) +LoadDLLfuncEx3 (waveInReset, winmm, 1, 0) +LoadDLLfuncEx3 (waveInStart, winmm, 1, 0) +LoadDLLfuncEx3 (waveInUnprepareHeader, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutClose, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutGetNumDevs, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutGetVolume, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutOpen, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutPrepareHeader, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutReset, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutSetVolume, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutUnprepareHeader, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutWrite, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutMessage, winmm, 1, 0) +LoadDLLfuncEx3 (waveOutGetDevCapsA, winmm, 1, 0) LoadDLLfunc (accept, ws2_32) LoadDLLfunc (bind, ws2_32) -- 2.49.0.windows.1