1 /* -----------------------------------------------------------------------
2 sysv.S - Copyright (c) 1998, 2008, 2011 Red Hat, Inc.
3 Copyright (c) 2011 Plausible Labs Cooperative, Inc.
5 ARM Foreign Function Interface
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 ``Software''), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 ----------------------------------------------------------------------- */
30 #include <fficonfig.h>
35 /* GCC 4.8 provides __ARM_ARCH; construct it otherwise. */
37 # if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
38 || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
39 || defined(__ARM_ARCH_7EM__)
41 # elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
42 || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
43 || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \
44 || defined(__ARM_ARCH_6M__)
46 # elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
47 || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
48 || defined(__ARM_ARCH_5TEJ__)
55 /* Conditionally compile unwinder directives. */
57 # define UNWIND(...) __VA_ARGS__
62 #if defined(HAVE_AS_CFI_PSEUDO_OP) && defined(__ARM_EABI__)
63 .cfi_sections .debug_frame
66 #define CONCAT(a, b) CONCAT2(a, b)
67 #define CONCAT2(a, b) a ## b
69 #ifdef __USER_LABEL_PREFIX__
70 # define CNAME(X) CONCAT (__USER_LABEL_PREFIX__, X)
75 # define SIZE(X) .size CNAME(X), . - CNAME(X)
76 # define TYPE(X, Y) .type CNAME(X), Y
82 #define ARM_FUNC_START_LOCAL(name) \
84 TYPE(CNAME(name), %function); \
87 #define ARM_FUNC_START(name) \
89 FFI_HIDDEN(CNAME(name)); \
90 ARM_FUNC_START_LOCAL(name)
92 #define ARM_FUNC_END(name) \
98 /* Windows on ARM is thumb-only */
101 /* Keep the assembly in ARM mode in other cases, for simplicity
102 * (to avoid interworking issues). */
107 /* Aid in defining a jump table with 8 bytes between entries. */
109 /* In thumb mode, instructions can be shorter than expected in arm mode, so
110 * we need to align the start of each case. */
111 # define E(index) .align 3
112 #elif defined(__clang__)
113 /* ??? The clang assembler doesn't handle .if with symbolic expressions. */
117 .if . - 0b - 8*index; \
118 .error "type table out of sync"; \
124 /* We require interworking on LDM, which implies ARMv5T,
125 which implies the existance of BLX. */
129 /* Note that we use STC and LDC to encode VFP instructions,
130 so that we do not need ".fpu vfp", nor get that added to
131 the object file attributes. These will not be executed
132 unless the FFI_VFP abi is used. */
139 ARM_FUNC_START(ffi_call_VFP)
143 cmp r3, #3 @ load only d0 if possible
149 ldcle p11, cr0, [r0] @ vldrle d0, [r0]
150 ldcgt p11, cr0, [r0], {16} @ vldmgt r0, {d0-d7}
152 add r0, r0, #64 @ discard the vfp register args
154 ARM_FUNC_END(ffi_call_VFP)
156 ARM_FUNC_START(ffi_call_SYSV)
160 @ This is a bit of a lie wrt the origin of the unwind info, but
161 @ now we've got the usual frame pointer and two saved registers.
162 UNWIND(.save {fp,lr})
163 UNWIND(.setfp fp, sp)
165 cfi_rel_offset(fp, 0)
166 cfi_rel_offset(lr, 4)
168 mov sp, r0 @ install the stack pointer
169 mov lr, r2 @ move the fn pointer out of the way
170 ldr ip, [fp, #16] @ install the static chain
171 ldmia sp!, {r0-r3} @ move first 4 parameters in registers.
174 @ Load r2 with the pointer to storage for the return value
175 @ Load r3 with the return type code
179 @ Deallocate the stack with the arguments.
181 cfi_def_cfa_register(sp)
183 @ Store values stored in registers.
186 add pc, pc, r3, lsl #3
190 add ip, ip, r3, lsl #3
199 stc p10, cr0, [r2] @ vstr s0, [r2]
206 stc p11, cr0, [r2] @ vstr d0, [r2]
213 stc p11, cr0, [r2], {8} @ vstm r2, {d0-d3}
230 ARM_FUNC_END(ffi_call_SYSV)
235 int ffi_closure_inner_* (cif, fun, user_data, frame)
238 ARM_FUNC_START(ffi_go_closure_SYSV)
240 stmdb sp!, {r0-r3} @ save argument regs
241 cfi_adjust_cfa_offset(16)
242 ldr r0, [ip, #4] @ load cif
243 ldr r1, [ip, #8] @ load fun
244 mov r2, ip @ load user_data
247 ARM_FUNC_END(ffi_go_closure_SYSV)
249 ARM_FUNC_START(ffi_closure_SYSV)
253 ldmfd sp!, {r0, ip} @ restore fp (r0 is used for stack alignment)
255 stmdb sp!, {r0-r3} @ save argument regs
256 cfi_adjust_cfa_offset(16)
258 #if FFI_EXEC_TRAMPOLINE_TABLE
259 ldr ip, [ip] @ ip points to the config page, dereference to get the ffi_closure*
261 ldr r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET] @ load cif
262 ldr r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4] @ load fun
263 ldr r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8] @ load user_data
265 add ip, sp, #16 @ compute entry sp
266 sub sp, sp, #64+32 @ allocate frame
267 cfi_adjust_cfa_offset(64+32)
270 /* Remember that EABI unwind info only applies at call sites.
271 We need do nothing except note the save of the stack pointer
272 and the link registers. */
273 UNWIND(.save {sp,lr})
274 cfi_adjust_cfa_offset(8)
275 cfi_rel_offset(lr, 4)
277 add r3, sp, #8 @ load frame
278 bl CNAME(ffi_closure_inner_SYSV)
280 @ Load values returned in registers.
281 add r2, sp, #8+64 @ load result
282 adr r3, CNAME(ffi_closure_ret)
284 add pc, r3, r0, lsl #3
286 add r3, r3, r0, lsl #3
291 ARM_FUNC_END(ffi_closure_SYSV)
293 ARM_FUNC_START(ffi_go_closure_VFP)
295 stmdb sp!, {r0-r3} @ save argument regs
296 cfi_adjust_cfa_offset(16)
297 ldr r0, [ip, #4] @ load cif
298 ldr r1, [ip, #8] @ load fun
299 mov r2, ip @ load user_data
302 ARM_FUNC_END(ffi_go_closure_VFP)
304 ARM_FUNC_START(ffi_closure_VFP)
308 ldmfd sp!, {r0, ip} @ restore fp (r0 is used for stack alignment)
310 stmdb sp!, {r0-r3} @ save argument regs
311 cfi_adjust_cfa_offset(16)
313 #if FFI_EXEC_TRAMPOLINE_TABLE
314 ldr ip, [ip] @ ip points to the config page, dereference to get the ffi_closure*
316 ldr r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET] @ load cif
317 ldr r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4] @ load fun
318 ldr r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8] @ load user_data
321 sub sp, sp, #64+32 @ allocate frame
322 cfi_adjust_cfa_offset(64+32)
326 stc p11, cr0, [sp], {16} @ vstm sp, {d0-d7}
331 UNWIND(.save {sp,lr})
332 cfi_adjust_cfa_offset(8)
333 cfi_rel_offset(lr, 4)
335 add r3, sp, #8 @ load frame
336 bl CNAME(ffi_closure_inner_VFP)
338 @ Load values returned in registers.
339 add r2, sp, #8+64 @ load result
340 adr r3, CNAME(ffi_closure_ret)
342 add pc, r3, r0, lsl #3
344 add r3, r3, r0, lsl #3
349 ARM_FUNC_END(ffi_closure_VFP)
351 /* Load values returned in registers for both closure entry points.
352 Note that we use LDM with SP in the register set. This is deprecated
353 by ARM, but not yet unpredictable. */
355 ARM_FUNC_START_LOCAL(ffi_closure_ret)
357 cfi_rel_offset(sp, 0)
358 cfi_rel_offset(lr, 4)
364 ldc p10, cr0, [r2] @ vldr s0, [r2]
371 ldc p11, cr0, [r2] @ vldr d0, [r2]
378 ldc p11, cr0, [r2], {8} @ vldm r2, {d0-d3}
401 ARM_FUNC_END(ffi_closure_ret)
403 #if defined(FFI_EXEC_STATIC_TRAMP)
404 ARM_FUNC_START(ffi_closure_SYSV_alt)
405 /* See the comments above trampoline_code_table. */
406 ldr ip, [sp, #4] /* Load closure in ip */
407 add sp, sp, 8 /* Restore the stack */
408 b CNAME(ffi_closure_SYSV)
409 ARM_FUNC_END(ffi_closure_SYSV_alt)
411 ARM_FUNC_START(ffi_closure_VFP_alt)
412 /* See the comments above trampoline_code_table. */
413 ldr ip, [sp, #4] /* Load closure in ip */
414 add sp, sp, 8 /* Restore the stack */
415 b CNAME(ffi_closure_VFP)
416 ARM_FUNC_END(ffi_closure_VFP_alt)
419 * Below is the definition of the trampoline code table. Each element in
420 * the code table is a trampoline.
423 * The trampoline uses register ip (r12). It saves the original value of ip
426 * The trampoline has two parameters - target code to jump to and data for
427 * the target code. The trampoline extracts the parameters from its parameter
428 * block (see tramp_table_map()). The trampoline saves the data address on
429 * the stack. Finally, it jumps to the target code.
431 * The target code can choose to:
433 * - restore the value of ip
434 * - load the data address in a register
435 * - restore the stack pointer to what it was when the trampoline was invoked.
437 .align ARM_TRAMP_MAP_SHIFT
438 ARM_FUNC_START(trampoline_code_table)
439 .rept ARM_TRAMP_MAP_SIZE / ARM_TRAMP_SIZE
440 sub sp, sp, #8 /* Make space on the stack */
441 str ip, [sp] /* Save ip on stack */
442 ldr ip, [pc, #4080] /* Copy data into ip */
443 str ip, [sp, #4] /* Save data on stack */
444 ldr pc, [pc, #4076] /* Copy code into PC */
446 ARM_FUNC_END(trampoline_code_table)
447 .align ARM_TRAMP_MAP_SHIFT
448 #endif /* FFI_EXEC_STATIC_TRAMP */
450 #endif /* FFI_CLOSURES */
452 #if FFI_EXEC_TRAMPOLINE_TABLE
455 #include <mach/machine/vm_param.h>
457 .align PAGE_MAX_SHIFT
458 ARM_FUNC_START(ffi_closure_trampoline_table_page)
459 .rept PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE
460 adr ip, #-PAGE_MAX_SIZE @ the config page is PAGE_MAX_SIZE behind the trampoline page
461 sub ip, #8 @ account for pc bias
462 ldr pc, [ip, #4] @ jump to ffi_closure_SYSV or ffi_closure_VFP
464 ARM_FUNC_END(ffi_closure_trampoline_table_page)
467 #elif defined(_WIN32)
469 ARM_FUNC_START(ffi_arm_trampoline)
474 ARM_FUNC_END(ffi_arm_trampoline)
478 ARM_FUNC_START(ffi_arm_trampoline)
482 ARM_FUNC_END(ffi_arm_trampoline)
484 #endif /* FFI_EXEC_TRAMPOLINE_TABLE */
487 #if defined __ELF__ && defined __linux__
488 .section .note.GNU-stack,"",%progbits