• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
126ARM_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, [sp]
133	vldmgt sp, {d0-d7}
134#else
135	ldcle	p11, cr0, [r0]		@ vldrle d0, [sp]
136	ldcgt	p11, cr0, [r0], {16}	@ vldmgt sp, {d0-d7}
137#endif
138	add	r0, r0, #64		@ discard the vfp register args
139	/* FALLTHRU */
140ARM_FUNC_END(ffi_call_VFP)
141
142ARM_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
1730:
174E(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}
181E(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}
188E(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}
195E(ARM_TYPE_INT64)
196	str	r1, [r2, #4]
197	nop
198E(ARM_TYPE_INT)
199	str	r0, [r2]
200	pop	{fp,pc}
201E(ARM_TYPE_VOID)
202	pop	{fp,pc}
203	nop
204E(ARM_TYPE_STRUCT)
205	pop	{fp,pc}
206
207	cfi_endproc
208	UNWIND(.fnend)
209ARM_FUNC_END(ffi_call_SYSV)
210
211
212/*
213	int ffi_closure_inner_* (cif, fun, user_data, frame)
214*/
215
216ARM_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
225ARM_FUNC_END(ffi_go_closure_SYSV)
226
227ARM_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
2390:
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)
261ARM_FUNC_END(ffi_closure_SYSV)
262
263ARM_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
272ARM_FUNC_END(ffi_go_closure_VFP)
273
274ARM_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
2860:
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)
311ARM_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
317ARM_FUNC_START_LOCAL(ffi_closure_ret)
318	cfi_startproc
319	cfi_rel_offset(sp, 0)
320	cfi_rel_offset(lr, 4)
3210:
322E(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}
329E(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}
336E(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}
343E(ARM_TYPE_INT64)
344	ldr	r1, [r2, #4]
345	nop
346E(ARM_TYPE_INT)
347	ldr	r0, [r2]
348	ldm	sp, {sp,pc}
349E(ARM_TYPE_VOID)
350	ldm	sp, {sp,pc}
351	nop
352E(ARM_TYPE_STRUCT)
353	ldm	sp, {sp,pc}
354	cfi_endproc
355ARM_FUNC_END(ffi_closure_ret)
356
357#if FFI_EXEC_TRAMPOLINE_TABLE
358
359#ifdef __MACH__
360#include <mach/machine/vm_param.h>
361
362.align	PAGE_MAX_SHIFT
363ARM_FUNC_START(ffi_closure_trampoline_table_page)
364.rept	PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE
365	adr ip, #-PAGE_MAX_SIZE   @ the config page is PAGE_MAX_SIZE behind the trampoline page
366	sub ip, #8				  @ account for pc bias
367	ldr	pc, [ip, #4]		  @ jump to ffi_closure_SYSV or ffi_closure_VFP
368.endr
369ARM_FUNC_END(ffi_closure_trampoline_table_page)
370#endif
371
372#else
373
374ARM_FUNC_START(ffi_arm_trampoline)
3750:	adr	ip, 0b
376	ldr	pc, 1f
3771:	.long	0
378ARM_FUNC_END(ffi_arm_trampoline)
379
380#endif /* FFI_EXEC_TRAMPOLINE_TABLE */
381#endif /* __arm__ */
382
383#if defined __ELF__ && defined __linux__
384	.section	.note.GNU-stack,"",%progbits
385#endif
386