Skip to content

Commit 5980223

Browse files
chandru-mcwjon-turney
authored andcommitted
Cygwin: autoload: Add AArch64 implementation
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ň <radek.barton@microsoft.com> Signed-off-by: Thirumalai Nagalingam <thirumalai.nagalingam@multicorewareinc.com> Signed-off-by: Chandru Kumaresan <chandru.kumaresan@multicorewareinc.com>
1 parent 7fd670a commit 5980223

1 file changed

Lines changed: 171 additions & 50 deletions

File tree

winsup/cygwin/autoload.cc

Lines changed: 171 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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

142189
extern "C" {
143190

144-
#ifdef __x86_64__
191+
#if defined(__x86_64__)
145192
__asm__ (" \n\
146193
.section .rdata,\"r\" \n\
147194
msg1: \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
222323
struct 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

301402
INIT_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. */
365486
INIT_WRAPPER (wsock_init)
366487
#else

0 commit comments

Comments
 (0)