[newlib-cygwin/main] Cygwin: autoload: Add AArch64 implementation
Jon Turney via Cygwin-cvs <[email protected]> Sat, 13 Jun 2026 13:31:02 +0000 (GMT)
| Newsgroups | gmane.os.cygwin.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3D59802232c7b=
597e13b952f65fe0f1b77b6678d73
commit 59802232c7b597e13b952f65fe0f1b77b6678d73
Author: Chandru Kumaresan <chandru.kumaresan-ftNtnR/FhrXEC4y91aVriVaTQe2KTcn/@public.gmane.org>
Date: Wed May 13 16:21:56 2026 +0530
Cygwin: autoload: Add AArch64 implementation
=20
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.
=20
Signed-off-by: Radek Barto=C5=88 <[email protected]>
Signed-off-by: Thirumalai Nagalingam <thirumalai.nagalingam@multicorewa=
reinc.com>
Signed-off-by: Chandru Kumaresan <chandru.kumaresan-ftNtnR/FhrXEC4y91aVridAWLNoT+7d/@public.gmane.org=
m>
Diff:
---
winsup/cygwin/autoload.cc | 221 +++++++++++++++++++++++++++++++++++-------=
----
1 file changed, 171 insertions(+), 50 deletions(-)
diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index a038997b3..7054511b6 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -16,20 +16,21 @@ bool NO_COPY wsock_started;
=20
/* Macro for defining "auto-load" functions.
* Note that this is self-modifying code *gasp*.
- * The first invocation of a routine will trigger the loading of
- * the DLL. This will then be followed by the discovery of
- * the procedure's entry point, which is placed into the location
- * pointed to by the stack pointer. This code then changes
- * the "call" operand which invoked it to a "jmp" which will
- * transfer directly to the DLL function on the next invocation.
+ *
+ * The first invocation of a auto-load function will trigger the loading o=
f the
+ * DLL. This will then be followed by the discovery of the procedure's en=
try
+ * point. The pointer the auto-load routine calls through (located via the
+ * return address) is then updated to transfer directly to the DLL functio=
n on
+ * the next invocation.
*
* Subsequent calls to routines whose transfer address has not been
* determined will skip the "load the dll" step, starting at the
* "discovery of the entry point" step.
*
- * So, immediately following the the call to one of the above routines
- * we have:
- * DLL info (4/8 bytes) Pointer to a block of information concerning
+ * So, immediately following the code to call to one of the above routines
+ * we have a struct func_info:
+ *
+ * DLL info (8 bytes) Pointer to a block of information concerning
* the DLL (see below).
* DLL notimp (2 bytes) Bool value flagging that non-existence of this
* function is not a fatal error.
@@ -43,49 +44,62 @@ bool NO_COPY wsock_started;
* func name (n bytes) asciz string containing the name of the function
* to be loaded.
*
- * The DLL info block consists of the following
- * load_state (4/8 bytes) Pointer to a word containing the routine used
- * to eventually invoke the function. Initially
- * points to an init function which loads the DLL,
- * gets the process's load address, changes the contents
- * here to point to the function address, and changes
- * the address argument of the initial jmp call.
- * On x86_64, the jmp is not tweaked directly. Rather,
- * the address of the Win32 function is stored in the
- * aforementioned Win32 function address slot and fetched
- * there for a jmp *%rax call. This indirection is
- * necessary to workaround the lack of a jmp opcode with
- * offset values > 32 bit. If the initialization has
- * been done, only the load part is done.
- * DLL handle (4/8 bytes) The handle to use when loading the DLL.
+ * The DLL info block (struct dll_info) consists of the following
+ *
+ * load_state (8 bytes) Pointer to the routine used to eventually invoke =
the
+ * function.
+ *
+ * Initially, this points to an init function which loads
+ * the DLL, then calls a function to which calls
+ * GetProcAddress for the given DLL handle and function
+ * name
+ *
+ * If the DLL load has already been done, only the
+ * GetProcAddress part is done.
+ *
+ * The jump is not tweaked directly. Rather, the address
+ * of the Win32 function is stored in the aforementioned
+ * Win32 function address slot and fetched there for an
+ * indirect jump. (This is necessary to access the full
+ * 64-bit address space.)
+ *
+ * DLL handle (8 bytes) The handle to use when loading the DLL.
* DLL locker (4 bytes) Word to use to avoid multi-thread access during
* initialization.
- * extra init (4/8 bytes) Extra initialization function.
- * DLL name (n bytes) asciz string containing the name of the DLL.
+ * extra init (8 bytes) Extra initialization function.
+ * DLL name (2n bytes) WCHAR string containing the name of the DLL.
*/
=20
+#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.
+
+ 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\
+.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
=20
/* Standard DLL load macro. May invoke a fatal error if the function isn't
found. */
@@ -97,7 +111,7 @@ bool NO_COPY wsock_started;
LoadDLLfuncEx3(name, dllname, notimp, err, 0)
=20
/* 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,6 +137,39 @@ _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 =3D &func_addr slot (label 3 =3D=3D f=
unc_info+12)\n\
+ ldr x16, [x16] // x16 =3D *(&func_addr) =3D label 1 initial=
ly, real addr later\n\
+ br x16 // fast-path branch into the resolved DLL fu=
nc\n\
+1: \n\
+ sub sp, sp, #80 // reserve 80 byte frame for caller's arg re=
gs + 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 =3D=3D label 2 =3D=3D &func_in=
fo\n\
+ adr x16, 2f // x16 =3D &func_info\n\
+ ldr x17, [x16] // x17 =3D func_info->dll (=3D dll_info*)\n\
+ ldr x17, [x17] // x17 =3D dll_info->load_state (e.g. _std_d=
ll_init)\n\
+ blr x17 // call thunk; LR =3D label 2 =3D 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
@@ -141,7 +188,7 @@ extern "C" void dll_chain () __asm__ ("dll_chain");
=20
extern "C" {
=20
-#ifdef __x86_64__
+#if defined(__x86_64__)
__asm__ (" \n\
.section .rdata,\"r\" \n\
msg1: \n\
@@ -203,6 +250,60 @@ 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 =3D func_info* (pushed by dll_chain)\=
n\
+ ldr w3, [x2, #8] // w3 =3D func_info.decoration\n\
+ tbz w3, #0, 1f // notimp bit clear -> fatal\n\
+ asr w4, w3, #16 // w4 =3D 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) fr=
ames\n\
+ ret \n\
+1: \n\
+ add x1, x2, #20 // x1 =3D &func_info.name \n\
+ ldr x3, [x2] // x3 =3D dll_info* \n\
+ ldr x2, [x3, #8] // x2 =3D 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 =3D func_info* (pushed by dll_chain)\=
n\
+ ldr x3, [x2] // x3 =3D dll_info* \n\
+ ldr x0, [x3, #8] // x0 =3D dll handle (arg1 to GetProcAddres=
s)\n\
+ add x1, x2, #20 // x1 =3D &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 =3D trampoline entry; '#52' =3D 12 b=
yte prologue\n\
+ // + 40 byte slow path; keep in sync with L=
oadDLLfuncEx3\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 =3D func_info* (=3D ret.high); pus=
h for dll_func_load\n\
+ br x1 // x1 =3D dll->init (=3D ret.low); tail-=
call resolver\n\
+");
#else
#error unimplemented for this target
#endif
@@ -222,7 +323,7 @@ struct dll_info
struct func_info
{
struct dll_info *dll;
- LONG decoration;
+ LONG decoration; // actually notimp & error;
UINT_PTR func_addr;
char name[];
};
@@ -260,7 +361,7 @@ dll_load (HANDLE& handle, PWCHAR name)
#define RETRY_COUNT 10
=20
/* The standard DLL initialization routine. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
=20
/* 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 +401,26 @@ _" #func ": \n\
=20
INIT_WRAPPER (std_dll_init)
=20
+#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 +481,7 @@ std_dll_init (struct func_info *func)
=20
/* Initialization function for winsock stuff. */
=20
-#ifdef __x86_64__
+#if defined(__x86_64__) || defined(__aarch64__)
/* See above comment preceeding std_dll_init. */
INIT_WRAPPER (wsock_init)
#else