mirror of
https://github.com/partic2/libffi
synced 2026-11-10 18:17:19 +00:00
arm: Rewrite ffi_call
Use the trick to allocate the stack frame for ffi_call_SYSV within ffi_call itself.
This commit is contained in:
+130
-294
@@ -1,8 +1,8 @@
|
||||
/* -----------------------------------------------------------------------
|
||||
sysv.S - Copyright (c) 1998, 2008, 2011 Red Hat, Inc.
|
||||
Copyright (c) 2011 Plausible Labs Cooperative, Inc.
|
||||
|
||||
ARM Foreign Function Interface
|
||||
|
||||
ARM Foreign Function Interface
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -28,219 +28,155 @@
|
||||
#define LIBFFI_ASM
|
||||
#include <fficonfig.h>
|
||||
#include <ffi.h>
|
||||
#ifdef HAVE_MACHINE_ASM_H
|
||||
#include <machine/asm.h>
|
||||
#else
|
||||
#ifdef __USER_LABEL_PREFIX__
|
||||
#define CONCAT1(a, b) CONCAT2(a, b)
|
||||
#define CONCAT2(a, b) a ## b
|
||||
#include <ffi_cfi.h>
|
||||
#include "internal.h"
|
||||
|
||||
/* Use the right prefix for global labels. */
|
||||
#define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
|
||||
#else
|
||||
#define CNAME(x) x
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#define ENTRY(x) .globl _##x; _##x:
|
||||
#else
|
||||
#define ENTRY(x) .globl CNAME(x); .type CNAME(x),%function; CNAME(x):
|
||||
#endif /* __APPLE__ */
|
||||
#endif
|
||||
|
||||
#ifdef __ELF__
|
||||
#define LSYM(x) .x
|
||||
#else
|
||||
#define LSYM(x) x
|
||||
#endif
|
||||
|
||||
/* Use the SOFTFP return value ABI on Mac OS X, as per the iOS ABI
|
||||
Function Call Guide */
|
||||
#ifdef __APPLE__
|
||||
#define __SOFTFP__
|
||||
#endif
|
||||
|
||||
/* We need a better way of testing for this, but for now, this is all
|
||||
we can do. */
|
||||
@ This selects the minimum architecture level required.
|
||||
#define __ARM_ARCH__ 3
|
||||
|
||||
#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)
|
||||
# undef __ARM_ARCH__
|
||||
# define __ARM_ARCH__ 4
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
|
||||
|| defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
|
||||
|| defined(__ARM_ARCH_5TEJ__)
|
||||
# undef __ARM_ARCH__
|
||||
# define __ARM_ARCH__ 5
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|
||||
/* GCC 4.8 provides __ARM_ARCH; construct it otherwise. */
|
||||
#ifndef __ARM_ARCH
|
||||
# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
|
||||
|| defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
|
||||
|| defined(__ARM_ARCH_7EM__)
|
||||
# define __ARM_ARCH 7
|
||||
# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|
||||
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
|
||||
|| defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \
|
||||
|| defined(__ARM_ARCH_6M__)
|
||||
# undef __ARM_ARCH__
|
||||
# define __ARM_ARCH__ 6
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
|
||||
|| defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
|
||||
|| defined(__ARM_ARCH_7EM__)
|
||||
# undef __ARM_ARCH__
|
||||
# define __ARM_ARCH__ 7
|
||||
#endif
|
||||
|
||||
#if __ARM_ARCH__ >= 5
|
||||
# define call_reg(x) blx x
|
||||
#elif defined (__ARM_ARCH_4T__)
|
||||
# define call_reg(x) mov lr, pc ; bx x
|
||||
# if defined(__thumb__) || defined(__THUMB_INTERWORK__)
|
||||
# define __INTERWORKING__
|
||||
# define __ARM_ARCH 6
|
||||
# elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
|
||||
|| defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
|
||||
|| defined(__ARM_ARCH_5TEJ__)
|
||||
# define __ARM_ARCH 5
|
||||
# else
|
||||
# define __ARM_ARCH 4
|
||||
# endif
|
||||
#else
|
||||
# define call_reg(x) mov lr, pc ; mov pc, x
|
||||
#endif
|
||||
|
||||
/* Conditionally compile unwinder directives. */
|
||||
.macro UNWIND text:vararg
|
||||
#ifdef __ARM_EABI__
|
||||
#define UNWIND
|
||||
#else
|
||||
#define UNWIND @
|
||||
\text
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
|
||||
#if defined(__thumb__) && !defined(__THUMB_INTERWORK__)
|
||||
#define ARM_FUNC_START(name) \
|
||||
.text; \
|
||||
.align 2; \
|
||||
.thumb; \
|
||||
.thumb_func; \
|
||||
ENTRY(name); \
|
||||
bx pc; \
|
||||
nop; \
|
||||
.arm; \
|
||||
UNWIND .fnstart; \
|
||||
_L__##name:
|
||||
#else
|
||||
#define ARM_FUNC_START(name) \
|
||||
.text; \
|
||||
.align 2; \
|
||||
.arm; \
|
||||
ENTRY(name); \
|
||||
UNWIND .fnstart
|
||||
.endm
|
||||
#if defined(HAVE_AS_CFI_PSEUDO_OP) && defined(__ARM_EABI__)
|
||||
.cfi_sections .debug_frame
|
||||
#endif
|
||||
|
||||
.macro RETLDM regs=, cond=, dirn=ia
|
||||
#if defined (__INTERWORKING__)
|
||||
.ifc "\regs",""
|
||||
ldr\cond lr, [sp], #4
|
||||
.else
|
||||
ldm\cond\dirn sp!, {\regs, lr}
|
||||
.endif
|
||||
bx\cond lr
|
||||
#define CONCAT(a, b) CONCAT2(a, b)
|
||||
#define CONCAT2(a, b) a ## b
|
||||
|
||||
#ifdef __USER_LABEL_PREFIX__
|
||||
# define CNAME(X) CONCAT (__USER_LABEL_PREFIX__, X)
|
||||
#else
|
||||
.ifc "\regs",""
|
||||
ldr\cond pc, [sp], #4
|
||||
.else
|
||||
ldm\cond\dirn sp!, {\regs, pc}
|
||||
.endif
|
||||
# define CNAME(X) X
|
||||
#endif
|
||||
#ifdef __ELF__
|
||||
# define SIZE(X) .size CNAME(X), . - CNAME(X)
|
||||
# define TYPE(X, Y) .type CNAME(X), Y
|
||||
#else
|
||||
# define SIZE(X)
|
||||
# define TYPE(X, Y)
|
||||
#endif
|
||||
|
||||
#define ARM_FUNC_START(name, gl) \
|
||||
.align 3; \
|
||||
.ifne gl; .globl CNAME(name); FFI_HIDDEN(CNAME(name)); .endif; \
|
||||
TYPE(name, %function); \
|
||||
CNAME(name):
|
||||
|
||||
#define ARM_FUNC_END(name) \
|
||||
SIZE(name)
|
||||
|
||||
/* Aid in defining a jump table with 8 bytes between entries. */
|
||||
.macro E index
|
||||
.if . - 0b - 8*\index
|
||||
.error "type table out of sync"
|
||||
.endif
|
||||
.endm
|
||||
|
||||
@ r0: ffi_prep_args
|
||||
@ r1: &ecif
|
||||
@ r2: cif->bytes
|
||||
@ r3: fig->flags
|
||||
@ sp+0: ecif.rvalue
|
||||
.text
|
||||
.syntax unified
|
||||
.arm
|
||||
|
||||
@ This assumes we are using gas.
|
||||
ARM_FUNC_START(ffi_call_SYSV)
|
||||
@ Save registers
|
||||
stmfd sp!, {r0-r3, fp, lr}
|
||||
UNWIND .save {r0-r3, fp, lr}
|
||||
mov fp, sp
|
||||
/* We require interworking on LDM, which implies ARMv5T,
|
||||
which implies the existance of BLX. */
|
||||
.arch armv5t
|
||||
|
||||
UNWIND .setfp fp, sp
|
||||
/* Note that we use STC and LDC to encode VFP instructions,
|
||||
so that we do not need ".fpu vfp", nor get that added to
|
||||
the object file attributes. These will not be executed
|
||||
unless the FFI_VFP abi is used. */
|
||||
|
||||
@ Make room for all of the new args.
|
||||
sub sp, fp, r2
|
||||
@ r0: stack
|
||||
@ r1: frame
|
||||
@ r2: fn
|
||||
@ r3: vfp_used
|
||||
|
||||
@ Place all of the ffi_prep_args in position
|
||||
mov r0, sp
|
||||
@ r1 already set
|
||||
ARM_FUNC_START(ffi_call_VFP, 1)
|
||||
UNWIND .fnstart
|
||||
cfi_startproc
|
||||
|
||||
@ Call ffi_prep_args(stack, &ecif)
|
||||
bl CNAME(ffi_prep_args_SYSV)
|
||||
cmp r3, #3 @ load only d0 if possible
|
||||
ldcle p11, cr0, [r0] @ vldrle d0, [sp]
|
||||
ldcgt p11, cr0, [r0], {16} @ vldmgt sp, {d0-d7}
|
||||
add r0, r0, #64 @ discard the vfp register args
|
||||
/* FALLTHRU */
|
||||
ARM_FUNC_END(ffi_call_VFP)
|
||||
|
||||
@ move first 4 parameters in registers
|
||||
ldmia sp, {r0-r3}
|
||||
ARM_FUNC_START(ffi_call_SYSV, 1)
|
||||
stm r1, {fp, lr}
|
||||
mov fp, r1
|
||||
|
||||
@ and adjust stack
|
||||
sub lr, fp, sp @ cif->bytes == fp - sp
|
||||
ldr ip, [fp] @ load fn() in advance
|
||||
cmp lr, #16
|
||||
movhs lr, #16
|
||||
add sp, sp, lr
|
||||
@ This is a bit of a lie wrt the origin of the unwind info, but
|
||||
@ now we've got the usual frame pointer and two saved registers.
|
||||
UNWIND .save {fp,lr}
|
||||
UNWIND .setfp fp, sp
|
||||
cfi_def_cfa(fp, 8)
|
||||
cfi_rel_offset(fp, 0)
|
||||
cfi_rel_offset(lr, 4)
|
||||
|
||||
@ call (fn) (...)
|
||||
call_reg(ip)
|
||||
|
||||
@ Remove the space we pushed for the args
|
||||
mov sp, fp
|
||||
mov sp, r0 @ install the stack pointer
|
||||
mov lr, r2 @ move the fn pointer out of the way
|
||||
ldmia sp!, {r0-r3} @ move first 4 parameters in registers.
|
||||
blx lr @ call fn
|
||||
|
||||
@ Load r2 with the pointer to storage for the return value
|
||||
ldr r2, [sp, #24]
|
||||
@ Load r3 with the return type code
|
||||
ldr r2, [fp, #8]
|
||||
ldr r3, [fp, #12]
|
||||
|
||||
@ Load r3 with the return type code
|
||||
ldr r3, [sp, #12]
|
||||
@ Deallocate the stack with the arguments.
|
||||
mov sp, fp
|
||||
cfi_def_cfa_register(sp)
|
||||
|
||||
@ If the return value pointer is NULL, assume no return value.
|
||||
cmp r2, #0
|
||||
beq LSYM(Lepilogue)
|
||||
@ Store values stored in registers.
|
||||
.align 3
|
||||
add pc, pc, r3, lsl #3
|
||||
nop
|
||||
0:
|
||||
E ARM_TYPE_VFP_S
|
||||
stc p10, cr0, [r2] @ vstr s0, [r2]
|
||||
pop {fp,pc}
|
||||
E ARM_TYPE_VFP_D
|
||||
stc p11, cr0, [r2] @ vstr d0, [r2]
|
||||
pop {fp,pc}
|
||||
E ARM_TYPE_VFP_N
|
||||
stc p11, cr0, [r2], {8} @ vstm r2, {d0-d3}
|
||||
pop {fp,pc}
|
||||
E ARM_TYPE_INT64
|
||||
str r1, [r2, #4]
|
||||
nop
|
||||
E ARM_TYPE_INT
|
||||
str r0, [r2]
|
||||
pop {fp,pc}
|
||||
E ARM_TYPE_VOID
|
||||
pop {fp,pc}
|
||||
nop
|
||||
E ARM_TYPE_STRUCT
|
||||
pop {fp,pc}
|
||||
|
||||
@ return INT
|
||||
cmp r3, #FFI_TYPE_INT
|
||||
#if defined(__SOFTFP__) || defined(__ARM_EABI__)
|
||||
cmpne r3, #FFI_TYPE_FLOAT
|
||||
#endif
|
||||
streq r0, [r2]
|
||||
beq LSYM(Lepilogue)
|
||||
|
||||
@ return INT64
|
||||
cmp r3, #FFI_TYPE_SINT64
|
||||
#if defined(__SOFTFP__) || defined(__ARM_EABI__)
|
||||
cmpne r3, #FFI_TYPE_DOUBLE
|
||||
#endif
|
||||
stmiaeq r2, {r0, r1}
|
||||
|
||||
#if !defined(__SOFTFP__) && !defined(__ARM_EABI__)
|
||||
beq LSYM(Lepilogue)
|
||||
|
||||
@ return FLOAT
|
||||
cmp r3, #FFI_TYPE_FLOAT
|
||||
stfeqs f0, [r2]
|
||||
beq LSYM(Lepilogue)
|
||||
|
||||
@ return DOUBLE or LONGDOUBLE
|
||||
cmp r3, #FFI_TYPE_DOUBLE
|
||||
stfeqd f0, [r2]
|
||||
#endif
|
||||
|
||||
LSYM(Lepilogue):
|
||||
#if defined (__INTERWORKING__)
|
||||
ldmia sp!, {r0-r3,fp, lr}
|
||||
bx lr
|
||||
#else
|
||||
ldmia sp!, {r0-r3,fp, pc}
|
||||
#endif
|
||||
|
||||
.ffi_call_SYSV_end:
|
||||
UNWIND .fnend
|
||||
#ifdef __ELF__
|
||||
.size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
|
||||
#endif
|
||||
cfi_endproc
|
||||
UNWIND .fnend
|
||||
ARM_FUNC_END(ffi_call_SYSV)
|
||||
|
||||
|
||||
/*
|
||||
@@ -251,7 +187,8 @@ LSYM(Lepilogue):
|
||||
void *args;
|
||||
*/
|
||||
|
||||
ARM_FUNC_START(ffi_closure_SYSV)
|
||||
ARM_FUNC_START(ffi_closure_SYSV, 1)
|
||||
UNWIND .fnstart
|
||||
UNWIND .pad #16
|
||||
add ip, sp, #16
|
||||
stmfd sp!, {ip, lr}
|
||||
@@ -310,116 +247,16 @@ ARM_FUNC_START(ffi_closure_SYSV)
|
||||
ldfd f0, [sp]
|
||||
b .Lclosure_epilogue
|
||||
#endif
|
||||
|
||||
.ffi_closure_SYSV_end:
|
||||
UNWIND .fnend
|
||||
#ifdef __ELF__
|
||||
.size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV)
|
||||
#endif
|
||||
ARM_FUNC_END(ffi_closure_SYSV)
|
||||
|
||||
|
||||
/* Below are VFP hard-float ABI call and closure implementations.
|
||||
Add VFP FPU directive here. This is only compiled into the library
|
||||
under EABI. */
|
||||
#ifdef __ARM_EABI__
|
||||
.fpu vfp
|
||||
|
||||
@ r0: fn
|
||||
@ r1: &ecif
|
||||
@ r2: cif->bytes
|
||||
@ r3: fig->flags
|
||||
@ sp+0: ecif.rvalue
|
||||
|
||||
ARM_FUNC_START(ffi_call_VFP)
|
||||
@ Save registers
|
||||
stmfd sp!, {r0-r3, fp, lr}
|
||||
UNWIND .save {r0-r3, fp, lr}
|
||||
mov fp, sp
|
||||
UNWIND .setfp fp, sp
|
||||
|
||||
@ Make room for all of the new args.
|
||||
sub sp, sp, r2
|
||||
|
||||
@ Make room for loading VFP args
|
||||
sub sp, sp, #64
|
||||
|
||||
@ Place all of the ffi_prep_args in position
|
||||
mov r0, sp
|
||||
@ r1 already set
|
||||
sub r2, fp, #64 @ VFP scratch space
|
||||
|
||||
@ Call ffi_prep_args(stack, &ecif, vfp_space)
|
||||
bl CNAME(ffi_prep_args_VFP)
|
||||
|
||||
@ Load VFP register args if needed
|
||||
cmp r0, #0
|
||||
mov ip, fp
|
||||
beq LSYM(Lbase_args)
|
||||
|
||||
@ Load only d0 if possible
|
||||
cmp r0, #3
|
||||
sub ip, fp, #64
|
||||
flddle d0, [ip]
|
||||
fldmiadgt ip, {d0-d7}
|
||||
|
||||
LSYM(Lbase_args):
|
||||
@ move first 4 parameters in registers
|
||||
ldmia sp, {r0-r3}
|
||||
|
||||
@ and adjust stack
|
||||
sub lr, ip, sp @ cif->bytes == (fp - 64) - sp
|
||||
ldr ip, [fp] @ load fn() in advance
|
||||
cmp lr, #16
|
||||
movhs lr, #16
|
||||
add sp, sp, lr
|
||||
|
||||
@ call (fn) (...)
|
||||
call_reg(ip)
|
||||
|
||||
@ Remove the space we pushed for the args
|
||||
mov sp, fp
|
||||
|
||||
@ Load r2 with the pointer to storage for
|
||||
@ the return value
|
||||
ldr r2, [sp, #24]
|
||||
|
||||
@ Load r3 with the return type code
|
||||
ldr r3, [sp, #12]
|
||||
|
||||
@ If the return value pointer is NULL,
|
||||
@ assume no return value.
|
||||
cmp r2, #0
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
cmp r3, #FFI_TYPE_INT
|
||||
streq r0, [r2]
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
cmp r3, #FFI_TYPE_SINT64
|
||||
stmeqia r2, {r0, r1}
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
cmp r3, #FFI_TYPE_FLOAT
|
||||
fstseq s0, [r2]
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
cmp r3, #FFI_TYPE_DOUBLE
|
||||
fstdeq d0, [r2]
|
||||
beq LSYM(Lepilogue_vfp)
|
||||
|
||||
cmp r3, #FFI_TYPE_STRUCT_VFP_FLOAT
|
||||
cmpne r3, #FFI_TYPE_STRUCT_VFP_DOUBLE
|
||||
fstmiadeq r2, {d0-d3}
|
||||
|
||||
LSYM(Lepilogue_vfp):
|
||||
RETLDM "r0-r3,fp"
|
||||
|
||||
.ffi_call_VFP_end:
|
||||
UNWIND .fnend
|
||||
.size CNAME(ffi_call_VFP),.ffi_call_VFP_end-CNAME(ffi_call_VFP)
|
||||
|
||||
|
||||
ARM_FUNC_START(ffi_closure_VFP)
|
||||
ARM_FUNC_START(ffi_closure_VFP, 1)
|
||||
UNWIND .fnstart
|
||||
fstmfdd sp!, {d0-d7}
|
||||
@ r0-r3, then d0-d7
|
||||
UNWIND .pad #80
|
||||
@@ -475,16 +312,15 @@ ARM_FUNC_START(ffi_closure_VFP)
|
||||
.Lretdouble_struct_vfp:
|
||||
fldmiad sp, {d0-d3}
|
||||
b .Lclosure_epilogue_vfp
|
||||
|
||||
.ffi_closure_VFP_end:
|
||||
UNWIND .fnend
|
||||
.size CNAME(ffi_closure_VFP),.ffi_closure_VFP_end-CNAME(ffi_closure_VFP)
|
||||
ARM_FUNC_END(ffi_closure_VFP)
|
||||
#endif
|
||||
|
||||
ENTRY(ffi_arm_trampoline)
|
||||
ARM_FUNC_START(ffi_arm_trampoline, 1)
|
||||
stmfd sp!, {r0-r3}
|
||||
ldr r0, [pc]
|
||||
ldr pc, [pc]
|
||||
ARM_FUNC_END(ffi_arm_trampoline)
|
||||
|
||||
#if defined __ELF__ && defined __linux__
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
|
||||
Reference in New Issue
Block a user