[PATCH v2] Cygwin: autoload: Add AArch64 implementation
Chandru Kumaresan <[email protected]> Thu, 4 Jun 2026 07:35:10 +0000
| Newsgroups | gmane.os.cygwin.patches |
|---|---|
| Message-ID | <PN0P287MB02951A11C49A1208A9BA66F392102@PN0P287MB0295.INDP287.PROD.OUTLOOK.COM> |
Hi Evgeny,
>LoadDLLprime contains only data. It might make sense to keep only
>one version for x86_64 and aarch64, and use WORD64 for .quad/.xword.
In v2, we introduced a WORD64 macro:
#if defined(__x86_64__)
# define WORD64 ".quad"
#elif defined(__aarch64__)
# define WORD64 ".xword"
#else
# error unimplemented for this target
#endif
And unified LoadDLLprime into a single shared definition using WORD64,
removing the duplicate x86_64/aarch64 versions.
Also added inline comments throughout AArch64 assembly blocks
documenting execution flow.
Please let me know if anything needs further clarification.
Inline patch:
---
winsup/cygwin/autoload.cc | 161 ++++++++++++++++++++++++++++++++------
1 file changed, 138 insertions(+), 23 deletions(-)
diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index a038997b3..b93fdf3dc 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -64,28 +64,34 @@ bool NO_COPY wsock_started;
* DLL name (n bytes) asciz string containing the name of the DLL.
*/
+#if defined(__x86_64__)
+# define WORD64 ".quad"
+#elif defined(__aarch64__)
+# define WORD64 ".xword"
+#else
+# error unimplemented for this target
+#endif
+
/* LoadDLLprime is used to prime the DLL info information, providing an
additional initialization routine to call prior to calling the first
- function. */
-#ifdef __x86_64__
-#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __asm__ (" \n\
-.ifndef " #dllname "_primed \n\
- .section .data_cygwin_nocopy,\"w\" \n\
- .align 8 \n\
-."#dllname "_info: \n\
- .quad _std_dll_init \n\
- .quad " #no_resolve_on_fork " \n\
- .long -1 \n\
- .align 8 \n\
- .quad " #init_also " \n\
- .string16 \"" #dllname ".dll\" \n\
- .text \n\
- .set " #dllname "_primed, 1 \n\
-.endif \n\
+ function. WORD64 stands in for .quad/.xword, and .balign (which means
+ "align to N bytes" on both targets - "x64" and "arm64) is used for
+ alignment. */
+#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __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\
+ .long -1 \n\
+ .balign 8 \n\
+ " WORD64 " " #init_also " \n\
+ .string16 \"" #dllname ".dll\" \n\
+ .text \n\
+ .set " #dllname "_primed, 1 \n\
+.endif \n\
");
-#else
-#error unimplemented for this target
-#endif
/* Standard DLL load macro. May invoke a fatal error if the function isn't
found. */
@@ -97,7 +103,7 @@ bool NO_COPY wsock_started;
LoadDLLfuncEx3(name, dllname, notimp, err, 0)
/* Main DLL setup stuff. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
#define LoadDLLfuncEx3(name, dllname, notimp, err, no_resolve_on_fork) \
LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
__asm__ (" \n\
@@ -123,10 +129,44 @@ _win32_" #name ": \n\
.asciz \"" #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) \
+ __asm__ ( "\n\
+ .section ." #dllname "_autoload_text,\"wx\" \n\
+ .global " #name " \n\
+ .global _win32_" #name " \n\
+ .p2align 4 \n\
+" #name ": \n\
+_win32_" #name ": \n\
+ adr x16, 3f // x16 = &func_addr slot (label 3 == func_info+12)\n\
+ ldr x16, [x16] // x16 = *(&func_addr) = 1b first time, real addr later\n\
+ br x16 // fast-path branch into the resolved DLL func\n\
+1: \n\
+ sub sp, sp, #80 // reserve 80B frame for caller's arg regs + LR\n\
+ stp x0, x1, [sp, #0] \n\
+ stp x2, x3, [sp, #16] \n\
+ stp x4, x5, [sp, #32] \n\
+ stp x6, x7, [sp, #48] \n\
+ stp x8, x30, [sp, #64] // x30 here == label 2 == &func_info\n\
+ adr x16, 2f // x16 = &func_info\n\
+ ldr x17, [x16] // x17 = func_info->dll (= dll_info*)\n\
+ ldr x17, [x17] // x17 = dll_info->load_state (e.g. _std_dll_init)\n\
+ blr x17 // call thunk; LR = label 2 = arg to std_dll_init\n\
+2: \n\
+ .xword ." #dllname "_info // +0 func_info.dll\n\
+ .hword " #notimp " // +8 func_info.decoration[lo]\n\
+ .hword ((" #err ") & 0xffff) // +10 func_info.decoration[hi]\n\
+3: \n\
+ .xword 1b // +12 func_info.func_addr (patched by dll_func_load)\n\
+ .asciz \"" #name "\" // +20 func_info.name (asciz)\n\
+ .text \n\
+");
#else
#error unimplemented for this target
#endif
+
/* DLL loader helper functions used during initialization. */
/* The function which finds the address, given the name and overwrites
@@ -141,7 +181,7 @@ extern "C" void dll_chain () __asm__ ("dll_chain");
extern "C" {
-#ifdef __x86_64__
+#if defined(__x86_64__)
__asm__ (" \n\
.section .rdata,\"r\" \n\
msg1: \n\
@@ -203,10 +243,65 @@ dll_chain: \n\
push %rax # Restore 'return address' \n\
jmp *%rdx # Jump to next init function \n\
");
+#elif defined(__aarch64__)
+__asm__ ( "\n\
+ .section .rdata,\"r\" \n\
+msg1: \n\
+ .ascii \"couldn't dynamically determine load address for '%s' (handle %p), %E\\0\" \n\
+ \n\
+ .text \n\
+ .p2align 2 \n\
+ noload: \n\
+ ldr x2, [sp] // x2 = func_info* (pushed by dll_chain)\n\
+ ldr w3, [x2, #8] // w3 = func_info.decoration\n\
+ tbz w3, #0, 1f // notimp bit clear -> fatal\n\
+ asr w4, w3, #16 // w4 = err (sign-extend high half)\n\
+ str w4, [sp, #8] // spill into dll_chain's xzr slot\n\
+ mov w0, #127 // ERROR_PROC_NOT_FOUND\n\
+ bl SetLastError \n\
+ ldr w0, [sp, #8] // reload err as return value\n\
+ ldr x30, [sp, #88] // restore caller LR (16 + 72)\n\
+ add sp, sp, #96 // drop dll_chain (16) + trampoline (80) frames\n\
+ ret \n\
+1: \n\
+ add x1, x2, #20 // x1 = &func_info.name\n\
+ ldr x3, [x2] // x3 = dll_info*\n\
+ ldr x2, [x3, #8] // x2 = dll_info->handle\n\
+ adrp x0, msg1 \n\
+ add x0, x0, #:lo12:msg1\n\
+ bl api_fatal // never returns \n\
+ \n\
+ .globl dll_func_load \n\
+dll_func_load: \n\
+ ldr x2, [sp] // x2 = func_info* (pushed by dll_chain)\n\
+ ldr x3, [x2] // x3 = dll_info*\n\
+ ldr x0, [x3, #8] // x0 = dll handle (arg1 to GetProcAddress)\n\
+ add x1, x2, #20 // x1 = &func_info.name (arg2)\n\
+ bl GetProcAddress \n\
+ cbz x0, noload // 0 -> not found, jump to error path\n\
+ ldr x2, [sp] // reload func_info*\n\
+ str x0, [x2, #12] // patch func_addr slot (label 3)\n\
+ sub x16, x2, #52 // x16 = trampoline entry; '#52' = 12B prologue\n\
+ // + 40B slow path; sync with LoadDLLfuncEx3\n\
+ add sp, sp, #16 // drop dll_chain frame\n\
+ ldp x0, x1, [sp, #0] // restore caller's arg regs\n\
+ ldp x2, x3, [sp, #16] \n\
+ ldp x4, x5, [sp, #32] \n\
+ ldp x6, x7, [sp, #48] \n\
+ ldp x8, x30, [sp, #64]\n\
+ add sp, sp, #80 // drop trampoline frame\n\
+ br x16 // re-enter trampoline; fast path now hits real func\n\
+ \n\
+ .global dll_chain \n\
+dll_chain: \n\
+ stp x0, xzr, [sp, #-16]! // x0 = func_info* (= ret.high); push for dll_func_load\n\
+ br x1 // x1 = dll->init (= ret.low); tail-call resolver\n\
+");
#else
#error unimplemented for this target
#endif
+
/* C representations of the two info blocks described above.
FIXME: These structures confuse gdb for some reason. GDB can print
the whole structure but has problems with the name field? */
@@ -260,7 +355,7 @@ dll_load (HANDLE& handle, PWCHAR name)
#define RETRY_COUNT 10
/* The standard DLL initialization routine. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
/* On x86_64, we need assembler wrappers for std_dll_init and wsock_init.
In the x86_64 ABI it's no safe bet that frame[1] (aka 8(%rbp)) contains
@@ -300,6 +395,26 @@ _" #func ": \n\
INIT_WRAPPER (std_dll_init)
+#elif defined(__aarch64__)
+#define INIT_WRAPPER(func) __asm__ ( "\n\
+ .text \n\
+ .p2align 2 \n\
+ .seh_proc _" #func " \n\
+_" #func ": \n\
+ stp x29, x30, [sp, #-16]! \n\
+ .seh_save_fplr_x 16 \n\
+ .seh_endprologue \n\
+ mov x0, x30 \n\
+ bl " #func " \n\
+ ldp x29, xzr, [sp], #16 \n\
+ adrp x30, dll_chain \n\
+ add x30, x30, #:lo12:dll_chain \n\
+ ret \n\
+ .seh_endproc \n\
+");
+
+INIT_WRAPPER (std_dll_init)
+
#else
#error unimplemented for this target
#endif
@@ -360,7 +475,7 @@ std_dll_init (struct func_info *func)
/* Initialization function for winsock stuff. */
-#ifdef __x86_64__
+#if defined(__x86_64__) || defined(__aarch64__)
/* See above comment preceeding std_dll_init. */
INIT_WRAPPER (wsock_init)
#else
--
2.49.0.windows.1
Cygwin-autoload-Add-AArch64-implementation.patch
(application/octet-stream, 11.4 KB)
From 464a2c05ba321f014411f7c0a9af1cb366ceecb8 Mon Sep 17 00:00:00 2001 From: chandru-mcw <[email protected]> Date: Wed, 13 May 2026 16:21:56 +0530 Subject: [PATCH v2] Cygwin: autoload: Add AArch64 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AArch64 assembly implementations of LoadDLLprime, LoadDLLfuncEx3, dll_func_load, dll_chain, and INIT_WRAPPER, mirroring the existing x86_64 lazy-loading mechanism. Also convert #ifdef guards to #if defined() for consistency. 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 | 161 ++++++++++++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 23 deletions(-) diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index a038997b3..b93fdf3dc 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -64,28 +64,34 @@ bool NO_COPY wsock_started; * DLL name (n bytes) asciz string containing the name of the DLL. */ +#if defined(__x86_64__) +# define WORD64 ".quad" +#elif defined(__aarch64__) +# define WORD64 ".xword" +#else +# error unimplemented for this target +#endif + /* LoadDLLprime is used to prime the DLL info information, providing an additional initialization routine to call prior to calling the first - function. */ -#ifdef __x86_64__ -#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __asm__ (" \n\ -.ifndef " #dllname "_primed \n\ - .section .data_cygwin_nocopy,\"w\" \n\ - .align 8 \n\ -."#dllname "_info: \n\ - .quad _std_dll_init \n\ - .quad " #no_resolve_on_fork " \n\ - .long -1 \n\ - .align 8 \n\ - .quad " #init_also " \n\ - .string16 \"" #dllname ".dll\" \n\ - .text \n\ - .set " #dllname "_primed, 1 \n\ -.endif \n\ + function. WORD64 stands in for .quad/.xword, and .balign (which means + "align to N bytes" on both targets - "x64" and "arm64) is used for + alignment. */ +#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __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\ + .long -1 \n\ + .balign 8 \n\ + " WORD64 " " #init_also " \n\ + .string16 \"" #dllname ".dll\" \n\ + .text \n\ + .set " #dllname "_primed, 1 \n\ +.endif \n\ "); -#else -#error unimplemented for this target -#endif /* Standard DLL load macro. May invoke a fatal error if the function isn't found. */ @@ -97,7 +103,7 @@ bool NO_COPY wsock_started; LoadDLLfuncEx3(name, dllname, notimp, err, 0) /* Main DLL setup stuff. */ -#ifdef __x86_64__ +#if defined(__x86_64__) #define LoadDLLfuncEx3(name, dllname, notimp, err, no_resolve_on_fork) \ LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \ __asm__ (" \n\ @@ -123,10 +129,44 @@ _win32_" #name ": \n\ .asciz \"" #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) \ + __asm__ ( "\n\ + .section ." #dllname "_autoload_text,\"wx\" \n\ + .global " #name " \n\ + .global _win32_" #name " \n\ + .p2align 4 \n\ +" #name ": \n\ +_win32_" #name ": \n\ + adr x16, 3f // x16 = &func_addr slot (label 3 == func_info+12)\n\ + ldr x16, [x16] // x16 = *(&func_addr) = 1b first time, real addr later\n\ + br x16 // fast-path branch into the resolved DLL func\n\ +1: \n\ + sub sp, sp, #80 // reserve 80B frame for caller's arg regs + LR\n\ + stp x0, x1, [sp, #0] \n\ + stp x2, x3, [sp, #16] \n\ + stp x4, x5, [sp, #32] \n\ + stp x6, x7, [sp, #48] \n\ + stp x8, x30, [sp, #64] // x30 here == label 2 == &func_info\n\ + adr x16, 2f // x16 = &func_info\n\ + ldr x17, [x16] // x17 = func_info->dll (= dll_info*)\n\ + ldr x17, [x17] // x17 = dll_info->load_state (e.g. _std_dll_init)\n\ + blr x17 // call thunk; LR = label 2 = arg to std_dll_init\n\ +2: \n\ + .xword ." #dllname "_info // +0 func_info.dll\n\ + .hword " #notimp " // +8 func_info.decoration[lo]\n\ + .hword ((" #err ") & 0xffff) // +10 func_info.decoration[hi]\n\ +3: \n\ + .xword 1b // +12 func_info.func_addr (patched by dll_func_load)\n\ + .asciz \"" #name "\" // +20 func_info.name (asciz)\n\ + .text \n\ +"); #else #error unimplemented for this target #endif + /* DLL loader helper functions used during initialization. */ /* The function which finds the address, given the name and overwrites @@ -141,7 +181,7 @@ extern "C" void dll_chain () __asm__ ("dll_chain"); extern "C" { -#ifdef __x86_64__ +#if defined(__x86_64__) __asm__ (" \n\ .section .rdata,\"r\" \n\ msg1: \n\ @@ -203,10 +243,65 @@ dll_chain: \n\ push %rax # Restore 'return address' \n\ jmp *%rdx # Jump to next init function \n\ "); +#elif defined(__aarch64__) +__asm__ ( "\n\ + .section .rdata,\"r\" \n\ +msg1: \n\ + .ascii \"couldn't dynamically determine load address for '%s' (handle %p), %E\\0\" \n\ + \n\ + .text \n\ + .p2align 2 \n\ + noload: \n\ + ldr x2, [sp] // x2 = func_info* (pushed by dll_chain)\n\ + ldr w3, [x2, #8] // w3 = func_info.decoration\n\ + tbz w3, #0, 1f // notimp bit clear -> fatal\n\ + asr w4, w3, #16 // w4 = err (sign-extend high half)\n\ + str w4, [sp, #8] // spill into dll_chain's xzr slot\n\ + mov w0, #127 // ERROR_PROC_NOT_FOUND\n\ + bl SetLastError \n\ + ldr w0, [sp, #8] // reload err as return value\n\ + ldr x30, [sp, #88] // restore caller LR (16 + 72)\n\ + add sp, sp, #96 // drop dll_chain (16) + trampoline (80) frames\n\ + ret \n\ +1: \n\ + add x1, x2, #20 // x1 = &func_info.name\n\ + ldr x3, [x2] // x3 = dll_info*\n\ + ldr x2, [x3, #8] // x2 = dll_info->handle\n\ + adrp x0, msg1 \n\ + add x0, x0, #:lo12:msg1\n\ + bl api_fatal // never returns \n\ + \n\ + .globl dll_func_load \n\ +dll_func_load: \n\ + ldr x2, [sp] // x2 = func_info* (pushed by dll_chain)\n\ + ldr x3, [x2] // x3 = dll_info*\n\ + ldr x0, [x3, #8] // x0 = dll handle (arg1 to GetProcAddress)\n\ + add x1, x2, #20 // x1 = &func_info.name (arg2)\n\ + bl GetProcAddress \n\ + cbz x0, noload // 0 -> not found, jump to error path\n\ + ldr x2, [sp] // reload func_info*\n\ + str x0, [x2, #12] // patch func_addr slot (label 3)\n\ + sub x16, x2, #52 // x16 = trampoline entry; '#52' = 12B prologue\n\ + // + 40B slow path; sync with LoadDLLfuncEx3\n\ + add sp, sp, #16 // drop dll_chain frame\n\ + ldp x0, x1, [sp, #0] // restore caller's arg regs\n\ + ldp x2, x3, [sp, #16] \n\ + ldp x4, x5, [sp, #32] \n\ + ldp x6, x7, [sp, #48] \n\ + ldp x8, x30, [sp, #64]\n\ + add sp, sp, #80 // drop trampoline frame\n\ + br x16 // re-enter trampoline; fast path now hits real func\n\ + \n\ + .global dll_chain \n\ +dll_chain: \n\ + stp x0, xzr, [sp, #-16]! // x0 = func_info* (= ret.high); push for dll_func_load\n\ + br x1 // x1 = dll->init (= ret.low); tail-call resolver\n\ +"); #else #error unimplemented for this target #endif + /* C representations of the two info blocks described above. FIXME: These structures confuse gdb for some reason. GDB can print the whole structure but has problems with the name field? */ @@ -260,7 +355,7 @@ dll_load (HANDLE& handle, PWCHAR name) #define RETRY_COUNT 10 /* The standard DLL initialization routine. */ -#ifdef __x86_64__ +#if defined(__x86_64__) /* On x86_64, we need assembler wrappers for std_dll_init and wsock_init. In the x86_64 ABI it's no safe bet that frame[1] (aka 8(%rbp)) contains @@ -300,6 +395,26 @@ _" #func ": \n\ INIT_WRAPPER (std_dll_init) +#elif defined(__aarch64__) +#define INIT_WRAPPER(func) __asm__ ( "\n\ + .text \n\ + .p2align 2 \n\ + .seh_proc _" #func " \n\ +_" #func ": \n\ + stp x29, x30, [sp, #-16]! \n\ + .seh_save_fplr_x 16 \n\ + .seh_endprologue \n\ + mov x0, x30 \n\ + bl " #func " \n\ + ldp x29, xzr, [sp], #16 \n\ + adrp x30, dll_chain \n\ + add x30, x30, #:lo12:dll_chain \n\ + ret \n\ + .seh_endproc \n\ +"); + +INIT_WRAPPER (std_dll_init) + #else #error unimplemented for this target #endif @@ -360,7 +475,7 @@ std_dll_init (struct func_info *func) /* Initialization function for winsock stuff. */ -#ifdef __x86_64__ +#if defined(__x86_64__) || defined(__aarch64__) /* See above comment preceeding std_dll_init. */ INIT_WRAPPER (wsock_init) #else -- 2.49.0.windows.1