| /* ----------------------------------------------------------------------- | |
| ffi.c - Copyright (C) 2013 IBM | |
| Copyright (C) 2011 Anthony Green | |
| Copyright (C) 2011 Kyle Moffett | |
| Copyright (C) 2008 Red Hat, Inc | |
| Copyright (C) 2007, 2008 Free Software Foundation, Inc | |
| Copyright (c) 1998 Geoffrey Keating | |
| PowerPC 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 THE AUTHOR 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. | |
| ----------------------------------------------------------------------- */ | |
| #include "ffi.h" | |
| #include "ffi_common.h" | |
| #include "ffi_powerpc.h" | |
| #if HAVE_LONG_DOUBLE_VARIANT | |
| /* Adjust ffi_type_longdouble. */ | |
| void FFI_HIDDEN | |
| ffi_prep_types (ffi_abi abi) | |
| { | |
| # if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
| # ifdef POWERPC64 | |
| ffi_prep_types_linux64 (abi); | |
| # else | |
| ffi_prep_types_sysv (abi); | |
| # endif | |
| # endif | |
| } | |
| #endif | |
| /* Perform machine dependent cif processing */ | |
| ffi_status FFI_HIDDEN | |
| ffi_prep_cif_machdep (ffi_cif *cif) | |
| { | |
| #ifdef POWERPC64 | |
| return ffi_prep_cif_linux64 (cif); | |
| #else | |
| return ffi_prep_cif_sysv (cif); | |
| #endif | |
| } | |
| ffi_status FFI_HIDDEN | |
| ffi_prep_cif_machdep_var (ffi_cif *cif, | |
| unsigned int nfixedargs MAYBE_UNUSED, | |
| unsigned int ntotalargs MAYBE_UNUSED) | |
| { | |
| #ifdef POWERPC64 | |
| return ffi_prep_cif_linux64_var (cif, nfixedargs, ntotalargs); | |
| #else | |
| return ffi_prep_cif_sysv (cif); | |
| #endif | |
| } | |
| static void | |
| ffi_call_int (ffi_cif *cif, | |
| void (*fn) (void), | |
| void *rvalue, | |
| void **avalue, | |
| void *closure) | |
| { | |
| /* The final SYSV ABI says that structures smaller or equal 8 bytes | |
| are returned in r3/r4. A draft ABI used by linux instead returns | |
| them in memory. | |
| We bounce-buffer SYSV small struct return values so that sysv.S | |
| can write r3 and r4 to memory without worrying about struct size. | |
| For ELFv2 ABI, use a bounce buffer for homogeneous structs too, | |
| for similar reasons. This bounce buffer must be aligned to 16 | |
| bytes for use with homogeneous structs of vectors (float128). */ | |
| float128 smst_buffer[8]; | |
| extended_cif ecif; | |
| ecif.cif = cif; | |
| ecif.avalue = avalue; | |
| ecif.rvalue = rvalue; | |
| if ((cif->flags & FLAG_RETURNS_SMST) != 0) | |
| ecif.rvalue = smst_buffer; | |
| /* Ensure that we have a valid struct return value. | |
| FIXME: Isn't this just papering over a user problem? */ | |
| else if (!rvalue && cif->rtype->type == FFI_TYPE_STRUCT) | |
| ecif.rvalue = alloca (cif->rtype->size); | |
| #ifdef POWERPC64 | |
| ffi_call_LINUX64 (&ecif, fn, ecif.rvalue, cif->flags, closure, | |
| -(long) cif->bytes); | |
| #else | |
| ffi_call_SYSV (&ecif, fn, ecif.rvalue, cif->flags, closure, -cif->bytes); | |
| #endif | |
| /* Check for a bounce-buffered return value */ | |
| if (rvalue && ecif.rvalue == smst_buffer) | |
| { | |
| unsigned int rsize = cif->rtype->size; | |
| #ifndef __LITTLE_ENDIAN__ | |
| /* The SYSV ABI returns a structure of up to 4 bytes in size | |
| left-padded in r3. */ | |
| # ifndef POWERPC64 | |
| if (rsize <= 4) | |
| memcpy (rvalue, (char *) smst_buffer + 4 - rsize, rsize); | |
| else | |
| # endif | |
| /* The SYSV ABI returns a structure of up to 8 bytes in size | |
| left-padded in r3/r4, and the ELFv2 ABI similarly returns a | |
| structure of up to 8 bytes in size left-padded in r3. But | |
| note that a structure of a single float is not paddded. */ | |
| if (rsize <= 8 && (cif->flags & FLAG_RETURNS_FP) == 0) | |
| memcpy (rvalue, (char *) smst_buffer + 8 - rsize, rsize); | |
| else | |
| #endif | |
| memcpy (rvalue, smst_buffer, rsize); | |
| } | |
| } | |
| void | |
| ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue) | |
| { | |
| ffi_call_int (cif, fn, rvalue, avalue, NULL); | |
| } | |
| void | |
| ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue, | |
| void *closure) | |
| { | |
| ffi_call_int (cif, fn, rvalue, avalue, closure); | |
| } | |
| ffi_status | |
| ffi_prep_closure_loc (ffi_closure *closure, | |
| ffi_cif *cif, | |
| void (*fun) (ffi_cif *, void *, void **, void *), | |
| void *user_data, | |
| void *codeloc) | |
| { | |
| #ifdef POWERPC64 | |
| return ffi_prep_closure_loc_linux64 (closure, cif, fun, user_data, codeloc); | |
| #else | |
| return ffi_prep_closure_loc_sysv (closure, cif, fun, user_data, codeloc); | |
| #endif | |
| } | |
| ffi_status | |
| ffi_prep_go_closure (ffi_go_closure *closure, | |
| ffi_cif *cif, | |
| void (*fun) (ffi_cif *, void *, void **, void *)) | |
| { | |
| #ifdef POWERPC64 | |
| closure->tramp = ffi_go_closure_linux64; | |
| #else | |
| closure->tramp = ffi_go_closure_sysv; | |
| #endif | |
| closure->cif = cif; | |
| closure->fun = fun; | |
| return FFI_OK; | |
| } |