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