; ----------------------------------------------------------------------- ; sysv.S - Copyright (c) 1998 Red Hat, Inc. ; ; 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 ; ``Software''), to deal in the Software without restriction, including ; without limitation the rights to use, copy, modify, merge, publish, ; distribute, sublicense, and/or sell copies of the Software, and to ; permit persons to whom the Software is furnished to do so, subject to ; the following conditions: ; ; The above copyright notice and this permission notice shall be included ; in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS ; OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ; IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR ; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ; OTHER DEALINGS IN THE SOFTWARE. ; ----------------------------------------------------------------------- */ ;#define LIBFFI_ASM ;#include ;#include ;#ifdef HAVE_MACHINE_ASM_H ;#include ;#else ;#ifdef __USER_LABEL_PREFIX__ ;#define CONCAT1(a, b) CONCAT2(a, b) ;#define CONCAT2(a, b) a ## b ;/* Use the right prefix for global labels. */ ;#define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x) ;#else ;#define CNAME(x) x ;#endif ;#define ENTRY(x) .globl CNAME(x); .type CNAME(x),%function; CNAME(x): ;#endif FFI_TYPE_VOID EQU 0 FFI_TYPE_INT EQU 1 FFI_TYPE_FLOAT EQU 2 FFI_TYPE_DOUBLE EQU 3 ;FFI_TYPE_LONGDOUBLE EQU 4 FFI_TYPE_UINT8 EQU 5 FFI_TYPE_SINT8 EQU 6 FFI_TYPE_UINT16 EQU 7 FFI_TYPE_SINT16 EQU 8 FFI_TYPE_UINT32 EQU 9 FFI_TYPE_SINT32 EQU 10 FFI_TYPE_UINT64 EQU 11 FFI_TYPE_SINT64 EQU 12 FFI_TYPE_STRUCT EQU 13 FFI_TYPE_POINTER EQU 14 ; WinCE always uses software floating point (I think) __SOFTFP__ EQU {TRUE} AREA |.text|, CODE, ARM ; .text ; a1: ffi_prep_args ; a2: &ecif ; a3: cif->bytes ; a4: fig->flags ; sp+0: ecif.rvalue ; sp+4: fn ; This assumes we are using gas. ;ENTRY(ffi_call_SYSV) EXPORT |ffi_call_SYSV| |ffi_call_SYSV| PROC ; Save registers stmfd sp!, {a1-a4, fp, lr} mov fp, sp ; Make room for all of the new args. sub sp, fp, a3 ; Place all of the ffi_prep_args in position mov ip, a1 mov a1, sp ; a2 already set ; And call mov lr, pc mov pc, ip ; move first 4 parameters in registers ldr a1, [sp, #0] ldr a2, [sp, #4] ldr a3, [sp, #8] ldr a4, [sp, #12] ; and adjust stack ldr ip, [fp, #8] cmp ip, #16 movge ip, #16 add sp, sp, ip ; call function mov lr, pc ldr pc, [fp, #28] ; Remove the space we pushed for the args mov sp, fp ; Load a3 with the pointer to storage for the return value ldr a3, [sp, #24] ; Load a4 with the return type code ldr a4, [sp, #12] ; If the return value pointer is NULL, assume no return value. cmp a3, #0 beq call_epilogue ; return INT cmp a4, #FFI_TYPE_INT streq a1, [a3] beq call_epilogue ; return FLOAT cmp a4, #FFI_TYPE_FLOAT [ __SOFTFP__ ;ifdef __SOFTFP__ streq a1, [a3] | ;else stfeqs f0, [a3] ] ;endif beq call_epilogue ; return DOUBLE or LONGDOUBLE cmp a4, #FFI_TYPE_DOUBLE [ __SOFTFP__ ;ifdef __SOFTFP__ stmeqia a3, {a1, a2} | ;else stfeqd f0, [a3] ] ;endif beq call_epilogue ; return SINT64 or UINT64 cmp a4, #FFI_TYPE_SINT64 stmeqia a3, {a1, a2} call_epilogue ldmfd sp!, {a1-a4, fp, pc} ;.ffi_call_SYSV_end: ;.size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV) ENDP RESERVE_RETURN EQU 16 ; This function is called by the trampoline ; It is NOT callable from C ; ip = pointer to struct ffi_closure IMPORT |ffi_closure_SYSV_inner| EXPORT |ffi_closure_SYSV| |ffi_closure_SYSV| PROC ; Store the argument registers on the stack stmfd sp!, {a1-a4} ; Push the return address onto the stack stmfd sp!, {lr} mov a1, ip ; first arg = address of ffi_closure add a2, sp, #4 ; second arg = sp+4 (points to saved a1) ; Allocate space for a non-struct return value sub sp, sp, #RESERVE_RETURN mov a3, sp ; third arg = return value address ; static unsigned int ; ffi_closure_SYSV_inner (ffi_closure *closure, char *in_args, void *rvalue) bl ffi_closure_SYSV_inner ; a1 now contains the return type code ; At this point the return value is on the stack ; Transfer it to the correct registers if necessary ; return INT cmp a1, #FFI_TYPE_INT ldreq a1, [sp] beq closure_epilogue ; return FLOAT cmp a1, #FFI_TYPE_FLOAT [ __SOFTFP__ ;ifdef __SOFTFP__ ldreq a1, [sp] | ;else stfeqs f0, [sp] ] ;endif beq closure_epilogue ; return DOUBLE or LONGDOUBLE cmp a1, #FFI_TYPE_DOUBLE [ __SOFTFP__ ;ifdef __SOFTFP__ ldmeqia sp, {a1, a2} | ;else stfeqd f0, [sp] ] ;endif beq closure_epilogue ; return SINT64 or UINT64 cmp a1, #FFI_TYPE_SINT64 ldmeqia sp, {a1, a2} closure_epilogue add sp, sp, #RESERVE_RETURN ; remove return value buffer ldmfd sp!, {ip} ; ip = pop return address add sp, sp, #16 ; remove saved argument registers {a1-a4} from the stack mov pc, ip ; return ENDP ; ffi_closure_SYSV END