@@ -16,20 +16,21 @@ bool NO_COPY wsock_started;
1616
1717/* Macro for defining "auto-load" functions.
1818 * Note that this is self-modifying code *gasp*.
19- * The first invocation of a routine will trigger the loading of
20- * the DLL. This will then be followed by the discovery of
21- * the procedure's entry point, which is placed into the location
22- * pointed to by the stack pointer. This code then changes
23- * the "call" operand which invoked it to a "jmp" which will
24- * transfer directly to the DLL function on the next invocation.
19+ *
20+ * The first invocation of a auto-load function will trigger the loading of the
21+ * DLL. This will then be followed by the discovery of the procedure's entry
22+ * point. The pointer the auto-load routine calls through (located via the
23+ * return address) is then updated to transfer directly to the DLL function on
24+ * the next invocation.
2525 *
2626 * Subsequent calls to routines whose transfer address has not been
2727 * determined will skip the "load the dll" step, starting at the
2828 * "discovery of the entry point" step.
2929 *
30- * So, immediately following the the call to one of the above routines
31- * we have:
32- * DLL info (4/8 bytes) Pointer to a block of information concerning
30+ * So, immediately following the code to call to one of the above routines
31+ * we have a struct func_info:
32+ *
33+ * DLL info (8 bytes) Pointer to a block of information concerning
3334 * the DLL (see below).
3435 * DLL notimp (2 bytes) Bool value flagging that non-existence of this
3536 * function is not a fatal error.
@@ -43,49 +44,62 @@ bool NO_COPY wsock_started;
4344 * func name (n bytes) asciz string containing the name of the function
4445 * to be loaded.
4546 *
46- * The DLL info block consists of the following
47- * load_state (4/8 bytes) Pointer to a word containing the routine used
48- * to eventually invoke the function. Initially
49- * points to an init function which loads the DLL,
50- * gets the process's load address, changes the contents
51- * here to point to the function address, and changes
52- * the address argument of the initial jmp call.
53- * On x86_64, the jmp is not tweaked directly. Rather,
54- * the address of the Win32 function is stored in the
55- * aforementioned Win32 function address slot and fetched
56- * there for a jmp *%rax call. This indirection is
57- * necessary to workaround the lack of a jmp opcode with
58- * offset values > 32 bit. If the initialization has
59- * been done, only the load part is done.
60- * DLL handle (4/8 bytes) The handle to use when loading the DLL.
47+ * The DLL info block (struct dll_info) consists of the following
48+ *
49+ * load_state (8 bytes) Pointer to the routine used to eventually invoke the
50+ * function.
51+ *
52+ * Initially, this points to an init function which loads
53+ * the DLL, then calls a function to which calls
54+ * GetProcAddress for the given DLL handle and function
55+ * name
56+ *
57+ * If the DLL load has already been done, only the
58+ * GetProcAddress part is done.
59+ *
60+ * The jump is not tweaked directly. Rather, the address
61+ * of the Win32 function is stored in the aforementioned
62+ * Win32 function address slot and fetched there for an
63+ * indirect jump. (This is necessary to access the full
64+ * 64-bit address space.)
65+ *
66+ * DLL handle (8 bytes) The handle to use when loading the DLL.
6167 * DLL locker (4 bytes) Word to use to avoid multi-thread access during
6268 * initialization.
63- * extra init (4/ 8 bytes) Extra initialization function.
64- * DLL name (n bytes) asciz string containing the name of the DLL.
69+ * extra init (8 bytes) Extra initialization function.
70+ * DLL name (2n bytes) WCHAR string containing the name of the DLL.
6571 */
6672
73+ #if defined(__x86_64__)
74+ # define WORD64 " .quad"
75+ #elif defined(__aarch64__)
76+ # define WORD64 " .xword"
77+ #else
78+ # error unimplemented for this target
79+ #endif
80+
6781/* LoadDLLprime is used to prime the DLL info information, providing an
6882 additional initialization routine to call prior to calling the first
69- function. */
70- #ifdef __x86_64__
71- #define LoadDLLprime (dllname, init_also, no_resolve_on_fork ) __asm__ (" \n \
72- .ifndef " #dllname " _primed \n \
73- .section .data_cygwin_nocopy,\" w\" \n \
74- .align 8 \n \
75- ." #dllname " _info: \n \
76- .quad _std_dll_init \n \
77- .quad " #no_resolve_on_fork " \n \
78- .long -1 \n \
79- .align 8 \n \
80- .quad " #init_also " \n \
81- .string16 \" " #dllname " .dll\" \n \
82- .text \n \
83- .set " #dllname " _primed, 1 \n \
84- .endif \n \
83+ function.
84+
85+ The macro WORD64 stands in for .quad/.xword, and .balign (which means
86+ "align to N bytes" on all targets, unlike .align) is used for
87+ alignment. */
88+ #define LoadDLLprime (dllname, init_also, no_resolve_on_fork ) __asm__ (" \n \
89+ .ifndef " #dllname " _primed \n \
90+ .section .data_cygwin_nocopy,\" w\" \n \
91+ .balign 8 \n \
92+ ." #dllname " _info: \n \
93+ " WORD64 " _std_dll_init \n \
94+ " WORD64 " " #no_resolve_on_fork " \n \
95+ .long -1 \n \
96+ .balign 8 \n \
97+ " WORD64 " " #init_also " \n \
98+ .string16 \" " #dllname " .dll\" \n \
99+ .text \n \
100+ .set " #dllname " _primed, 1 \n \
101+ .endif \n \
85102" );
86- #else
87- #error unimplemented for this target
88- #endif
89103
90104/* Standard DLL load macro. May invoke a fatal error if the function isn't
91105 found. */
@@ -97,7 +111,7 @@ bool NO_COPY wsock_started;
97111 LoadDLLfuncEx3 (name, dllname, notimp, err, 0 )
98112
99113/* Main DLL setup stuff. */
100- #ifdef __x86_64__
114+ #if defined( __x86_64__)
101115#define LoadDLLfuncEx3 (name, dllname, notimp, err, no_resolve_on_fork ) \
102116 LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
103117 __asm__ (" \n \
@@ -123,6 +137,39 @@ _win32_" #name ": \n\
123137 .asciz \" " #name " \" \n \
124138 .text \n \
125139" );
140+ #elif defined(__aarch64__)
141+ #define LoadDLLfuncEx3 (name, dllname, notimp, err, no_resolve_on_fork ) \
142+ LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
143+ __asm__ ( " \n \
144+ .section ." #dllname " _autoload_text,\" wx\" \n \
145+ .global " #name " \n \
146+ .global _win32_" #name " \n \
147+ .p2align 4 \n \
148+ " #name " : \n \
149+ _win32_" #name " : \n \
150+ adr x16, 3f // x16 = &func_addr slot (label 3 == func_info+12)\n \
151+ ldr x16, [x16] // x16 = *(&func_addr) = label 1 initially, real addr later\n \
152+ br x16 // fast-path branch into the resolved DLL func\n \
153+ 1: \n \
154+ sub sp, sp, #80 // reserve 80 byte frame for caller's arg regs + LR\n \
155+ stp x0, x1, [sp, #0] \n \
156+ stp x2, x3, [sp, #16] \n \
157+ stp x4, x5, [sp, #32] \n \
158+ stp x6, x7, [sp, #48] \n \
159+ stp x8, x30, [sp, #64] // x30 here == label 2 == &func_info\n \
160+ adr x16, 2f // x16 = &func_info\n \
161+ ldr x17, [x16] // x17 = func_info->dll (= dll_info*)\n \
162+ ldr x17, [x17] // x17 = dll_info->load_state (e.g. _std_dll_init)\n \
163+ blr x17 // call thunk; LR = label 2 = arg to std_dll_init\n \
164+ 2: \n \
165+ .xword ." #dllname " _info // +0 func_info.dll\n \
166+ .hword " #notimp " // +8 func_info.decoration[lo]\n \
167+ .hword ((" #err " ) & 0xffff) // +10 func_info.decoration[hi]\n \
168+ 3: \n \
169+ .xword 1b // +12 func_info.func_addr (patched by dll_func_load)\n \
170+ .asciz \" " #name " \" // +20 func_info.name (asciz)\n \
171+ .text \n \
172+ " );
126173#else
127174#error unimplemented for this target
128175#endif
@@ -141,7 +188,7 @@ extern "C" void dll_chain () __asm__ ("dll_chain");
141188
142189extern " C" {
143190
144- #ifdef __x86_64__
191+ #if defined( __x86_64__)
145192__asm__ (" \n \
146193 .section .rdata,\" r\" \n \
147194msg1: \n \
@@ -203,6 +250,60 @@ dll_chain: \n\
203250 push %rax # Restore 'return address' \n \
204251 jmp *%rdx # Jump to next init function \n \
205252" );
253+ #elif defined(__aarch64__)
254+ __asm__ ( " \n \
255+ .section .rdata,\" r\" \n \
256+ msg1: \n \
257+ .ascii \" couldn't dynamically determine load address for '%s' (handle %p), %E\\ 0\" \n \
258+ \n \
259+ .text \n \
260+ .p2align 2 \n \
261+ noload: \n \
262+ ldr x2, [sp] // x2 = func_info* (pushed by dll_chain)\n \
263+ ldr w3, [x2, #8] // w3 = func_info.decoration\n \
264+ tbz w3, #0, 1f // notimp bit clear -> fatal\n \
265+ asr w4, w3, #16 // w4 = err (sign-extend high half)\n \
266+ str w4, [sp, #8] // spill into dll_chain's xzr slot\n \
267+ mov w0, #127 // ERROR_PROC_NOT_FOUND \n \
268+ bl SetLastError \n \
269+ ldr w0, [sp, #8] // reload err as return value\n \
270+ ldr x30, [sp, #88] // restore caller LR (16 + 72)\n \
271+ add sp, sp, #96 // drop dll_chain (16) + trampoline (80) frames\n \
272+ ret \n \
273+ 1: \n \
274+ add x1, x2, #20 // x1 = &func_info.name \n \
275+ ldr x3, [x2] // x3 = dll_info* \n \
276+ ldr x2, [x3, #8] // x2 = dll_info->handle \n \
277+ adrp x0, msg1 \n \
278+ add x0, x0, #:lo12:msg1 \n \
279+ bl api_fatal // never returns \n \
280+ \n \
281+ .globl dll_func_load \n \
282+ dll_func_load: \n \
283+ ldr x2, [sp] // x2 = func_info* (pushed by dll_chain)\n \
284+ ldr x3, [x2] // x3 = dll_info* \n \
285+ ldr x0, [x3, #8] // x0 = dll handle (arg1 to GetProcAddress)\n \
286+ add x1, x2, #20 // x1 = &func_info.name (arg2)\n \
287+ bl GetProcAddress \n \
288+ cbz x0, noload // 0 -> not found, jump to error path\n \
289+ ldr x2, [sp] // reload func_info* \n \
290+ str x0, [x2, #12] // patch func_addr slot (label 3)\n \
291+ sub x16, x2, #52 // x16 = trampoline entry; '#52' = 12 byte prologue\n \
292+ // + 40 byte slow path; keep in sync with LoadDLLfuncEx3\n \
293+ add sp, sp, #16 // drop dll_chain frame \n \
294+ ldp x0, x1, [sp, #0] // restore caller's arg regs\n \
295+ ldp x2, x3, [sp, #16] \n \
296+ ldp x4, x5, [sp, #32] \n \
297+ ldp x6, x7, [sp, #48] \n \
298+ ldp x8, x30, [sp, #64] \n \
299+ add sp, sp, #80 // drop trampoline frame \n \
300+ br x16 // re-enter trampoline; fast path now hits real func\n \
301+ \n \
302+ .global dll_chain \n \
303+ dll_chain: \n \
304+ stp x0, xzr, [sp, #-16]! // x0 = func_info* (= ret.high); push for dll_func_load\n \
305+ br x1 // x1 = dll->init (= ret.low); tail-call resolver\n \
306+ " );
206307#else
207308#error unimplemented for this target
208309#endif
@@ -222,7 +323,7 @@ struct dll_info
222323struct func_info
223324{
224325 struct dll_info *dll;
225- LONG decoration;
326+ LONG decoration; // actually notimp & error;
226327 UINT_PTR func_addr;
227328 char name[];
228329};
@@ -260,7 +361,7 @@ dll_load (HANDLE& handle, PWCHAR name)
260361#define RETRY_COUNT 10
261362
262363/* The standard DLL initialization routine. */
263- #ifdef __x86_64__
364+ #if defined( __x86_64__)
264365
265366/* On x86_64, we need assembler wrappers for std_dll_init and wsock_init.
266367 In the x86_64 ABI it's no safe bet that frame[1] (aka 8(%rbp)) contains
@@ -300,6 +401,26 @@ _" #func ": \n\
300401
301402INIT_WRAPPER (std_dll_init)
302403
404+ #elif defined(__aarch64__)
405+ #define INIT_WRAPPER (func ) __asm__ ( " \n \
406+ .text \n \
407+ .p2align 2 \n \
408+ .seh_proc _" #func " \n \
409+ _" #func " : \n \
410+ stp x29, x30, [sp, #-16]! \n \
411+ .seh_save_fplr_x 16 \n \
412+ .seh_endprologue \n \
413+ mov x0, x30 \n \
414+ bl " #func " \n \
415+ ldp x29, xzr, [sp], #16 \n \
416+ adrp x30, dll_chain \n \
417+ add x30, x30, #:lo12:dll_chain \n \
418+ ret \n \
419+ .seh_endproc \n \
420+ " );
421+
422+ INIT_WRAPPER (std_dll_init)
423+
303424#else
304425#error unimplemented for this target
305426#endif
@@ -360,7 +481,7 @@ std_dll_init (struct func_info *func)
360481
361482/* Initialization function for winsock stuff. */
362483
363- #ifdef __x86_64__
484+ #if defined( __x86_64__) || defined(__aarch64__)
364485/* See above comment preceeding std_dll_init. */
365486INIT_WRAPPER (wsock_init)
366487#else
0 commit comments