Static tramp v5 (#624)
[platform/upstream/libffi.git] / src / arm / sysv.S
1 /* -----------------------------------------------------------------------
2    sysv.S - Copyright (c) 1998, 2008, 2011 Red Hat, Inc.
3             Copyright (c) 2011 Plausible Labs Cooperative, Inc.
4
5    ARM Foreign Function Interface
6
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:
14
15    The above copyright notice and this permission notice shall be included
16    in all copies or substantial portions of the Software.
17
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    ----------------------------------------------------------------------- */
27
28 #ifdef __arm__
29 #define LIBFFI_ASM
30 #include <fficonfig.h>
31 #include <ffi.h>
32 #include <ffi_cfi.h>
33 #include "internal.h"
34
35 /* GCC 4.8 provides __ARM_ARCH; construct it otherwise.  */
36 #ifndef __ARM_ARCH
37 # if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
38      || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
39      || defined(__ARM_ARCH_7EM__)
40 #  define __ARM_ARCH 7
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__)
45 #  define __ARM_ARCH 6
46 # elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
47         || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
48         || defined(__ARM_ARCH_5TEJ__)
49 #  define __ARM_ARCH 5
50 # else
51 #  define __ARM_ARCH 4
52 # endif
53 #endif
54
55 /* Conditionally compile unwinder directives.  */
56 #ifdef __ARM_EABI__
57 # define UNWIND(...)    __VA_ARGS__
58 #else
59 # define UNWIND(...)
60 #endif
61
62 #if defined(HAVE_AS_CFI_PSEUDO_OP) && defined(__ARM_EABI__)
63         .cfi_sections   .debug_frame
64 #endif
65
66 #define CONCAT(a, b)    CONCAT2(a, b)
67 #define CONCAT2(a, b)   a ## b
68
69 #ifdef __USER_LABEL_PREFIX__
70 # define CNAME(X)       CONCAT (__USER_LABEL_PREFIX__, X)
71 #else
72 # define CNAME(X)       X
73 #endif
74 #ifdef __ELF__
75 # define SIZE(X)        .size CNAME(X), . - CNAME(X)
76 # define TYPE(X, Y)     .type CNAME(X), Y
77 #else
78 # define SIZE(X)
79 # define TYPE(X, Y)
80 #endif
81
82 #define ARM_FUNC_START_LOCAL(name)      \
83         .align  3;                      \
84         TYPE(CNAME(name), %function);   \
85         CNAME(name):
86
87 #define ARM_FUNC_START(name)            \
88         .globl CNAME(name);             \
89         FFI_HIDDEN(CNAME(name));        \
90         ARM_FUNC_START_LOCAL(name)
91
92 #define ARM_FUNC_END(name) \
93         SIZE(name)
94
95 /* Aid in defining a jump table with 8 bytes between entries.  */
96 /* ??? The clang assembler doesn't handle .if with symbolic expressions.  */
97 #ifdef __clang__
98 # define E(index)
99 #else
100 # define E(index)                               \
101         .if . - 0b - 8*index;                   \
102         .error "type table out of sync";        \
103         .endif
104 #endif
105
106         .text
107         .syntax unified
108         .arm
109
110 #ifndef __clang__
111         /* We require interworking on LDM, which implies ARMv5T,
112            which implies the existance of BLX.  */
113         .arch   armv5t
114 #endif
115
116         /* Note that we use STC and LDC to encode VFP instructions,
117            so that we do not need ".fpu vfp", nor get that added to
118            the object file attributes.  These will not be executed
119            unless the FFI_VFP abi is used.  */
120
121         @ r0:   stack
122         @ r1:   frame
123         @ r2:   fn
124         @ r3:   vfp_used
125
126 ARM_FUNC_START(ffi_call_VFP)
127         UNWIND(.fnstart)
128         cfi_startproc
129
130         cmp     r3, #3                  @ load only d0 if possible
131 #ifdef __clang__
132         vldrle d0, [r0]
133         vldmgt r0, {d0-d7}
134 #else
135         ldcle   p11, cr0, [r0]          @ vldrle d0, [r0]
136         ldcgt   p11, cr0, [r0], {16}    @ vldmgt r0, {d0-d7}
137 #endif
138         add     r0, r0, #64             @ discard the vfp register args
139         /* FALLTHRU */
140 ARM_FUNC_END(ffi_call_VFP)
141
142 ARM_FUNC_START(ffi_call_SYSV)
143         stm     r1, {fp, lr}
144         mov     fp, r1
145
146         @ This is a bit of a lie wrt the origin of the unwind info, but
147         @ now we've got the usual frame pointer and two saved registers.
148         UNWIND(.save {fp,lr})
149         UNWIND(.setfp fp, sp)
150         cfi_def_cfa(fp, 8)
151         cfi_rel_offset(fp, 0)
152         cfi_rel_offset(lr, 4)
153
154         mov     sp, r0          @ install the stack pointer
155         mov     lr, r2          @ move the fn pointer out of the way
156         ldr     ip, [fp, #16]   @ install the static chain
157         ldmia   sp!, {r0-r3}    @ move first 4 parameters in registers.
158         blx     lr              @ call fn
159
160         @ Load r2 with the pointer to storage for the return value
161         @ Load r3 with the return type code
162         ldr     r2, [fp, #8]
163         ldr     r3, [fp, #12]
164
165         @ Deallocate the stack with the arguments.
166         mov     sp, fp
167         cfi_def_cfa_register(sp)
168
169         @ Store values stored in registers.
170         .align  3
171         add     pc, pc, r3, lsl #3
172         nop
173 0:
174 E(ARM_TYPE_VFP_S)
175 #ifdef __clang__
176         vstr s0, [r2]
177 #else
178         stc     p10, cr0, [r2]          @ vstr s0, [r2]
179 #endif
180         pop     {fp,pc}
181 E(ARM_TYPE_VFP_D)
182 #ifdef __clang__
183         vstr d0, [r2]
184 #else
185         stc     p11, cr0, [r2]          @ vstr d0, [r2]
186 #endif
187         pop     {fp,pc}
188 E(ARM_TYPE_VFP_N)
189 #ifdef __clang__
190         vstm r2, {d0-d3}
191 #else
192         stc     p11, cr0, [r2], {8}     @ vstm r2, {d0-d3}
193 #endif
194         pop     {fp,pc}
195 E(ARM_TYPE_INT64)
196         str     r1, [r2, #4]
197         nop
198 E(ARM_TYPE_INT)
199         str     r0, [r2]
200         pop     {fp,pc}
201 E(ARM_TYPE_VOID)
202         pop     {fp,pc}
203         nop
204 E(ARM_TYPE_STRUCT)
205         pop     {fp,pc}
206
207         cfi_endproc
208         UNWIND(.fnend)
209 ARM_FUNC_END(ffi_call_SYSV)
210
211
212 /*
213         int ffi_closure_inner_* (cif, fun, user_data, frame)
214 */
215
216 ARM_FUNC_START(ffi_go_closure_SYSV)
217         cfi_startproc
218         stmdb   sp!, {r0-r3}                    @ save argument regs
219         cfi_adjust_cfa_offset(16)
220         ldr     r0, [ip, #4]                    @ load cif
221         ldr     r1, [ip, #8]                    @ load fun
222         mov     r2, ip                          @ load user_data
223         b       0f
224         cfi_endproc
225 ARM_FUNC_END(ffi_go_closure_SYSV)
226
227 ARM_FUNC_START(ffi_closure_SYSV)
228         UNWIND(.fnstart)
229         cfi_startproc
230         stmdb   sp!, {r0-r3}                    @ save argument regs
231         cfi_adjust_cfa_offset(16)
232
233 #if FFI_EXEC_TRAMPOLINE_TABLE
234         ldr ip, [ip]                            @ ip points to the config page, dereference to get the ffi_closure*
235 #endif
236         ldr     r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET]        @ load cif
237         ldr     r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4]  @ load fun
238         ldr     r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8]  @ load user_data
239 0:
240         add     ip, sp, #16                     @ compute entry sp
241         sub     sp, sp, #64+32                  @ allocate frame
242         cfi_adjust_cfa_offset(64+32)
243         stmdb   sp!, {ip,lr}
244
245         /* Remember that EABI unwind info only applies at call sites.
246            We need do nothing except note the save of the stack pointer
247            and the link registers.  */
248         UNWIND(.save {sp,lr})
249         cfi_adjust_cfa_offset(8)
250         cfi_rel_offset(lr, 4)
251
252         add     r3, sp, #8                      @ load frame
253         bl      CNAME(ffi_closure_inner_SYSV)
254
255         @ Load values returned in registers.
256         add     r2, sp, #8+64                   @ load result
257         adr     r3, CNAME(ffi_closure_ret)
258         add     pc, r3, r0, lsl #3
259         cfi_endproc
260         UNWIND(.fnend)
261 ARM_FUNC_END(ffi_closure_SYSV)
262
263 ARM_FUNC_START(ffi_go_closure_VFP)
264         cfi_startproc
265         stmdb   sp!, {r0-r3}                    @ save argument regs
266         cfi_adjust_cfa_offset(16)
267         ldr     r0, [ip, #4]                    @ load cif
268         ldr     r1, [ip, #8]                    @ load fun
269         mov     r2, ip                          @ load user_data
270         b       0f
271         cfi_endproc
272 ARM_FUNC_END(ffi_go_closure_VFP)
273
274 ARM_FUNC_START(ffi_closure_VFP)
275         UNWIND(.fnstart)
276         cfi_startproc
277         stmdb   sp!, {r0-r3}                    @ save argument regs
278         cfi_adjust_cfa_offset(16)
279
280 #if FFI_EXEC_TRAMPOLINE_TABLE
281         ldr ip, [ip]                            @ ip points to the config page, dereference to get the ffi_closure*
282 #endif
283         ldr     r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET]        @ load cif
284         ldr     r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4]  @ load fun
285         ldr     r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8]  @ load user_data
286 0:
287         add     ip, sp, #16
288         sub     sp, sp, #64+32                  @ allocate frame
289         cfi_adjust_cfa_offset(64+32)
290 #ifdef __clang__
291         vstm sp, {d0-d7}
292 #else
293         stc     p11, cr0, [sp], {16}            @ vstm sp, {d0-d7}
294 #endif
295         stmdb   sp!, {ip,lr}
296
297         /* See above.  */
298         UNWIND(.save {sp,lr})
299         cfi_adjust_cfa_offset(8)
300         cfi_rel_offset(lr, 4)
301
302         add     r3, sp, #8                      @ load frame
303         bl      CNAME(ffi_closure_inner_VFP)
304
305         @ Load values returned in registers.
306         add     r2, sp, #8+64                   @ load result
307         adr     r3, CNAME(ffi_closure_ret)
308         add     pc, r3, r0, lsl #3
309         cfi_endproc
310         UNWIND(.fnend)
311 ARM_FUNC_END(ffi_closure_VFP)
312
313 /* Load values returned in registers for both closure entry points.
314    Note that we use LDM with SP in the register set.  This is deprecated
315    by ARM, but not yet unpredictable.  */
316
317 ARM_FUNC_START_LOCAL(ffi_closure_ret)
318         cfi_startproc
319         cfi_rel_offset(sp, 0)
320         cfi_rel_offset(lr, 4)
321 0:
322 E(ARM_TYPE_VFP_S)
323 #ifdef __clang__
324         vldr s0, [r2]
325 #else
326         ldc     p10, cr0, [r2]                  @ vldr s0, [r2]
327 #endif
328         ldm     sp, {sp,pc}
329 E(ARM_TYPE_VFP_D)
330 #ifdef __clang__
331         vldr d0, [r2]
332 #else
333         ldc     p11, cr0, [r2]                  @ vldr d0, [r2]
334 #endif
335         ldm     sp, {sp,pc}
336 E(ARM_TYPE_VFP_N)
337 #ifdef __clang__
338         vldm r2, {d0-d3}
339 #else
340         ldc     p11, cr0, [r2], {8}             @ vldm r2, {d0-d3}
341 #endif
342         ldm     sp, {sp,pc}
343 E(ARM_TYPE_INT64)
344         ldr     r1, [r2, #4]
345         nop
346 E(ARM_TYPE_INT)
347         ldr     r0, [r2]
348         ldm     sp, {sp,pc}
349 E(ARM_TYPE_VOID)
350         ldm     sp, {sp,pc}
351         nop
352 E(ARM_TYPE_STRUCT)
353         ldm     sp, {sp,pc}
354         cfi_endproc
355 ARM_FUNC_END(ffi_closure_ret)
356
357 #if defined(FFI_EXEC_STATIC_TRAMP)
358 ARM_FUNC_START(ffi_closure_SYSV_alt)
359         /* See the comments above trampoline_code_table. */
360         ldr     ip, [sp, #4]                    /* Load closure in ip */
361         add     sp, sp, 8                       /* Restore the stack */
362         b       CNAME(ffi_closure_SYSV)
363 ARM_FUNC_END(ffi_closure_SYSV_alt)
364
365 ARM_FUNC_START(ffi_closure_VFP_alt)
366         /* See the comments above trampoline_code_table. */
367         ldr     ip, [sp, #4]                    /* Load closure in ip */
368         add     sp, sp, 8                       /* Restore the stack */
369         b       CNAME(ffi_closure_VFP)
370 ARM_FUNC_END(ffi_closure_VFP_alt)
371
372 /*
373  * Below is the definition of the trampoline code table. Each element in
374  * the code table is a trampoline.
375  */
376 /*
377  * The trampoline uses register ip (r12). It saves the original value of ip
378  * on the stack.
379  *
380  * The trampoline has two parameters - target code to jump to and data for
381  * the target code. The trampoline extracts the parameters from its parameter
382  * block (see tramp_table_map()). The trampoline saves the data address on
383  * the stack. Finally, it jumps to the target code.
384  *
385  * The target code can choose to:
386  *
387  * - restore the value of ip
388  * - load the data address in a register
389  * - restore the stack pointer to what it was when the trampoline was invoked.
390  */
391         .align  ARM_TRAMP_MAP_SHIFT
392 ARM_FUNC_START(trampoline_code_table)
393         .rept   ARM_TRAMP_MAP_SIZE / ARM_TRAMP_SIZE
394         sub     sp, sp, #8              /* Make space on the stack */
395         str     ip, [sp]                /* Save ip on stack */
396         ldr     ip, [pc, #4080]         /* Copy data into ip */
397         str     ip, [sp, #4]            /* Save data on stack */
398         ldr     pc, [pc, #4076]         /* Copy code into PC */
399         .endr
400 ARM_FUNC_END(trampoline_code_table)
401         .align  ARM_TRAMP_MAP_SHIFT
402 #endif /* FFI_EXEC_STATIC_TRAMP */
403
404 #if FFI_EXEC_TRAMPOLINE_TABLE
405
406 #ifdef __MACH__
407 #include <mach/machine/vm_param.h>
408
409 .align  PAGE_MAX_SHIFT
410 ARM_FUNC_START(ffi_closure_trampoline_table_page)
411 .rept   PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE
412         adr ip, #-PAGE_MAX_SIZE   @ the config page is PAGE_MAX_SIZE behind the trampoline page
413         sub ip, #8                                @ account for pc bias
414         ldr     pc, [ip, #4]              @ jump to ffi_closure_SYSV or ffi_closure_VFP
415 .endr
416 ARM_FUNC_END(ffi_closure_trampoline_table_page)
417 #endif
418
419 #else
420
421 ARM_FUNC_START(ffi_arm_trampoline)
422 0:      adr     ip, 0b
423         ldr     pc, 1f
424 1:      .long   0
425 ARM_FUNC_END(ffi_arm_trampoline)
426
427 #endif /* FFI_EXEC_TRAMPOLINE_TABLE */
428 #endif /* __arm__ */
429
430 #if defined __ELF__ && defined __linux__
431         .section        .note.GNU-stack,"",%progbits
432 #endif