x86: Rewrite ffi_call
Decouple the assembly from FFI_TYPE_*. Merge prep_args with ffi_call, passing the frame and the stack to the assembly. Note that this patch isn't really standalone, as this breaks closures.
This commit is contained in:
603
src/x86/ffi.c
603
src/x86/ffi.c
@@ -29,10 +29,10 @@
|
|||||||
----------------------------------------------------------------------- */
|
----------------------------------------------------------------------- */
|
||||||
|
|
||||||
#ifndef __x86_64__
|
#ifndef __x86_64__
|
||||||
|
|
||||||
#include <ffi.h>
|
#include <ffi.h>
|
||||||
#include <ffi_common.h>
|
#include <ffi_common.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
|
/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
|
||||||
all further uses in this file will refer to the 80-bit type. */
|
all further uses in this file will refer to the 80-bit type. */
|
||||||
@@ -45,276 +45,277 @@
|
|||||||
# define FFI_TYPE_LONGDOUBLE 4
|
# define FFI_TYPE_LONGDOUBLE 4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__declspec)
|
||||||
|
# define __declspec(x) __attribute__((x))
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ffi_prep_args is called by the assembly routine once stack space
|
/* Perform machine dependent cif processing. */
|
||||||
has been allocated for the function's arguments */
|
ffi_status FFI_HIDDEN
|
||||||
|
ffi_prep_cif_machdep(ffi_cif *cif)
|
||||||
unsigned int ffi_prep_args(char *stack, extended_cif *ecif);
|
|
||||||
unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
|
|
||||||
{
|
{
|
||||||
register unsigned int i;
|
size_t bytes = 0;
|
||||||
register void **p_argv;
|
int i, n, flags, cabi = cif->abi;
|
||||||
register char *argp;
|
|
||||||
register ffi_type **p_arg;
|
|
||||||
const int cabi = ecif->cif->abi;
|
|
||||||
const int dir = (cabi == FFI_PASCAL || cabi == FFI_REGISTER) ? -1 : +1;
|
|
||||||
unsigned int stack_args_count = 0;
|
|
||||||
void *p_stack_data[3];
|
|
||||||
char *argp2 = stack;
|
|
||||||
|
|
||||||
argp = stack;
|
switch (cabi)
|
||||||
|
|
||||||
if ((ecif->cif->flags == FFI_TYPE_STRUCT
|
|
||||||
|| ecif->cif->flags == FFI_TYPE_MS_STRUCT))
|
|
||||||
{
|
{
|
||||||
/* For fastcall/thiscall/register this is first register-passed
|
case FFI_SYSV:
|
||||||
argument. */
|
case FFI_STDCALL:
|
||||||
if (cabi == FFI_THISCALL || cabi == FFI_FASTCALL || cabi == FFI_REGISTER)
|
case FFI_THISCALL:
|
||||||
{
|
case FFI_FASTCALL:
|
||||||
p_stack_data[stack_args_count] = argp;
|
case FFI_MS_CDECL:
|
||||||
++stack_args_count;
|
case FFI_PASCAL:
|
||||||
}
|
case FFI_REGISTER:
|
||||||
|
|
||||||
*(void **) argp = ecif->rvalue;
|
|
||||||
argp += sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
p_arg = ecif->cif->arg_types;
|
|
||||||
p_argv = ecif->avalue;
|
|
||||||
if (dir < 0)
|
|
||||||
{
|
|
||||||
const int nargs = ecif->cif->nargs - 1;
|
|
||||||
if (nargs > 0)
|
|
||||||
{
|
|
||||||
p_arg += nargs;
|
|
||||||
p_argv += nargs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = ecif->cif->nargs;
|
|
||||||
i != 0;
|
|
||||||
i--, p_arg += dir, p_argv += dir)
|
|
||||||
{
|
|
||||||
/* Align if necessary */
|
|
||||||
if ((sizeof(void*) - 1) & (size_t) argp)
|
|
||||||
argp = (char *) ALIGN(argp, sizeof(void*));
|
|
||||||
|
|
||||||
size_t z = (*p_arg)->size;
|
|
||||||
|
|
||||||
if (z < FFI_SIZEOF_ARG)
|
|
||||||
{
|
|
||||||
z = FFI_SIZEOF_ARG;
|
|
||||||
switch ((*p_arg)->type)
|
|
||||||
{
|
|
||||||
case FFI_TYPE_SINT8:
|
|
||||||
*(ffi_sarg *) argp = (ffi_sarg)*(SINT8 *)(* p_argv);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FFI_TYPE_UINT8:
|
|
||||||
*(ffi_arg *) argp = (ffi_arg)*(UINT8 *)(* p_argv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FFI_TYPE_SINT16:
|
|
||||||
*(ffi_sarg *) argp = (ffi_sarg)*(SINT16 *)(* p_argv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FFI_TYPE_UINT16:
|
|
||||||
*(ffi_arg *) argp = (ffi_arg)*(UINT16 *)(* p_argv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FFI_TYPE_SINT32:
|
|
||||||
*(ffi_sarg *) argp = (ffi_sarg)*(SINT32 *)(* p_argv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FFI_TYPE_UINT32:
|
|
||||||
*(ffi_arg *) argp = (ffi_arg)*(UINT32 *)(* p_argv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FFI_TYPE_STRUCT:
|
|
||||||
*(ffi_arg *) argp = *(ffi_arg *)(* p_argv);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
FFI_ASSERT(0);
|
return FFI_BAD_ABI;
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(argp, *p_argv, z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For thiscall/fastcall/register convention register-passed arguments
|
|
||||||
are the first two none-floating-point arguments with a size
|
|
||||||
smaller or equal to sizeof (void*). */
|
|
||||||
if ((z == FFI_SIZEOF_ARG)
|
|
||||||
&& ((cabi == FFI_REGISTER)
|
|
||||||
|| (cabi == FFI_THISCALL && stack_args_count < 1)
|
|
||||||
|| (cabi == FFI_FASTCALL && stack_args_count < 2))
|
|
||||||
&& ((*p_arg)->type != FFI_TYPE_FLOAT && (*p_arg)->type != FFI_TYPE_STRUCT)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (dir < 0 && stack_args_count > 2)
|
|
||||||
{
|
|
||||||
/* Iterating arguments backwards, so first register-passed argument
|
|
||||||
will be passed last. Shift temporary values to make place. */
|
|
||||||
p_stack_data[0] = p_stack_data[1];
|
|
||||||
p_stack_data[1] = p_stack_data[2];
|
|
||||||
stack_args_count = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
p_stack_data[stack_args_count] = argp;
|
|
||||||
++stack_args_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
argp += z;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to move the register-passed arguments for thiscall,
|
|
||||||
fastcall, register on top of stack, so that those can be moved
|
|
||||||
to registers by call-handler. */
|
|
||||||
if (stack_args_count > 0)
|
|
||||||
{
|
|
||||||
if (dir < 0 && stack_args_count > 1)
|
|
||||||
{
|
|
||||||
/* Reverse order if iterating arguments backwards */
|
|
||||||
ffi_arg tmp = *(ffi_arg*) p_stack_data[0];
|
|
||||||
*(ffi_arg*) p_stack_data[0] = *(ffi_arg*) p_stack_data[stack_args_count - 1];
|
|
||||||
*(ffi_arg*) p_stack_data[stack_args_count - 1] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < stack_args_count; i++)
|
|
||||||
{
|
|
||||||
if (p_stack_data[i] != argp2)
|
|
||||||
{
|
|
||||||
ffi_arg tmp = *(ffi_arg*) p_stack_data[i];
|
|
||||||
memmove (argp2 + FFI_SIZEOF_ARG, argp2, (size_t) ((char*) p_stack_data[i] - (char*)argp2));
|
|
||||||
*(ffi_arg *) argp2 = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
argp2 += FFI_SIZEOF_ARG;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack_args_count;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform machine dependent cif processing */
|
|
||||||
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
ffi_type **ptr;
|
|
||||||
|
|
||||||
/* Set the return type flag */
|
|
||||||
switch (cif->rtype->type)
|
switch (cif->rtype->type)
|
||||||
{
|
{
|
||||||
case FFI_TYPE_VOID:
|
case FFI_TYPE_VOID:
|
||||||
case FFI_TYPE_UINT8:
|
flags = X86_RET_VOID;
|
||||||
case FFI_TYPE_UINT16:
|
break;
|
||||||
case FFI_TYPE_SINT8:
|
|
||||||
case FFI_TYPE_SINT16:
|
|
||||||
case FFI_TYPE_SINT64:
|
|
||||||
case FFI_TYPE_FLOAT:
|
case FFI_TYPE_FLOAT:
|
||||||
|
flags = X86_RET_FLOAT;
|
||||||
|
break;
|
||||||
case FFI_TYPE_DOUBLE:
|
case FFI_TYPE_DOUBLE:
|
||||||
|
flags = X86_RET_DOUBLE;
|
||||||
|
break;
|
||||||
case FFI_TYPE_LONGDOUBLE:
|
case FFI_TYPE_LONGDOUBLE:
|
||||||
cif->flags = (unsigned) cif->rtype->type;
|
flags = X86_RET_LDOUBLE;
|
||||||
break;
|
break;
|
||||||
|
case FFI_TYPE_UINT8:
|
||||||
|
flags = X86_RET_UINT8;
|
||||||
|
break;
|
||||||
|
case FFI_TYPE_UINT16:
|
||||||
|
flags = X86_RET_UINT16;
|
||||||
|
break;
|
||||||
|
case FFI_TYPE_SINT8:
|
||||||
|
flags = X86_RET_SINT8;
|
||||||
|
break;
|
||||||
|
case FFI_TYPE_SINT16:
|
||||||
|
flags = X86_RET_SINT16;
|
||||||
|
break;
|
||||||
|
case FFI_TYPE_INT:
|
||||||
|
case FFI_TYPE_SINT32:
|
||||||
|
case FFI_TYPE_UINT32:
|
||||||
|
case FFI_TYPE_POINTER:
|
||||||
|
flags = X86_RET_INT32;
|
||||||
|
break;
|
||||||
|
case FFI_TYPE_SINT64:
|
||||||
case FFI_TYPE_UINT64:
|
case FFI_TYPE_UINT64:
|
||||||
cif->flags = FFI_TYPE_SINT64;
|
flags = X86_RET_INT64;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FFI_TYPE_STRUCT:
|
case FFI_TYPE_STRUCT:
|
||||||
#ifndef X86
|
#ifndef X86
|
||||||
/* ??? This should be a different ABI rather than an ifdef. */
|
/* ??? This should be a different ABI rather than an ifdef. */
|
||||||
if (cif->rtype->size == 1)
|
if (cif->rtype->size == 1)
|
||||||
cif->flags = FFI_TYPE_SMALL_STRUCT_1B; /* same as char size */
|
flags = X86_RET_STRUCT_1B;
|
||||||
else if (cif->rtype->size == 2)
|
else if (cif->rtype->size == 2)
|
||||||
cif->flags = FFI_TYPE_SMALL_STRUCT_2B; /* same as short size */
|
flags = X86_RET_STRUCT_2B;
|
||||||
else if (cif->rtype->size == 4)
|
else if (cif->rtype->size == 4)
|
||||||
cif->flags = FFI_TYPE_INT; /* same as int type */
|
flags = X86_RET_INT32;
|
||||||
else if (cif->rtype->size == 8)
|
else if (cif->rtype->size == 8)
|
||||||
cif->flags = FFI_TYPE_SINT64; /* same as int64 type */
|
flags = X86_RET_INT64;
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (cif->abi == FFI_MS_CDECL)
|
switch (cabi)
|
||||||
cif->flags = FFI_TYPE_MS_STRUCT;
|
|
||||||
else
|
|
||||||
cif->flags = FFI_TYPE_STRUCT;
|
|
||||||
/* Allocate space for return value pointer. */
|
|
||||||
cif->bytes += ALIGN(sizeof(void*), FFI_SIZEOF_ARG);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
cif->flags = FFI_TYPE_INT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
|
|
||||||
{
|
{
|
||||||
if (((*ptr)->alignment - 1) & cif->bytes)
|
case FFI_THISCALL:
|
||||||
cif->bytes = ALIGN(cif->bytes, (*ptr)->alignment);
|
case FFI_FASTCALL:
|
||||||
cif->bytes += (unsigned)ALIGN((*ptr)->size, FFI_SIZEOF_ARG);
|
case FFI_STDCALL:
|
||||||
|
case FFI_MS_CDECL:
|
||||||
|
flags = X86_RET_STRUCTARG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
flags = X86_RET_STRUCTPOP;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
/* Allocate space for return value pointer. */
|
||||||
|
bytes += ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return FFI_BAD_TYPEDEF;
|
||||||
|
}
|
||||||
|
cif->flags = flags;
|
||||||
|
|
||||||
if (cif->abi == FFI_SYSV)
|
for (i = 0, n = cif->nargs; i < n; i++)
|
||||||
cif->bytes = ALIGN (cif->bytes, 15);
|
{
|
||||||
|
ffi_type *t = cif->arg_types[i];
|
||||||
|
|
||||||
|
bytes = ALIGN (bytes, t->alignment);
|
||||||
|
bytes += ALIGN (t->size, FFI_SIZEOF_ARG);
|
||||||
|
}
|
||||||
|
cif->bytes = ALIGN (bytes, 16);
|
||||||
|
|
||||||
return FFI_OK;
|
return FFI_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void
|
static ffi_arg
|
||||||
ffi_call_win32(unsigned int (*)(char *, extended_cif *), extended_cif *,
|
extend_basic_type(void *arg, int type)
|
||||||
unsigned, unsigned, unsigned, unsigned *, void (*fn)(void));
|
|
||||||
extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *,
|
|
||||||
unsigned, unsigned, unsigned *, void (*fn)(void));
|
|
||||||
|
|
||||||
void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
|
||||||
{
|
{
|
||||||
extended_cif ecif;
|
switch (type)
|
||||||
|
|
||||||
ecif.cif = cif;
|
|
||||||
ecif.avalue = avalue;
|
|
||||||
|
|
||||||
/* If the return value is a struct and we don't have a return */
|
|
||||||
/* value address then we need to make one */
|
|
||||||
|
|
||||||
if (rvalue == NULL
|
|
||||||
&& (cif->flags == FFI_TYPE_STRUCT
|
|
||||||
|| cif->flags == FFI_TYPE_MS_STRUCT))
|
|
||||||
{
|
{
|
||||||
ecif.rvalue = alloca(cif->rtype->size);
|
case FFI_TYPE_SINT8:
|
||||||
|
return *(SINT8 *)arg;
|
||||||
|
case FFI_TYPE_UINT8:
|
||||||
|
return *(UINT8 *)arg;
|
||||||
|
case FFI_TYPE_SINT16:
|
||||||
|
return *(SINT16 *)arg;
|
||||||
|
case FFI_TYPE_UINT16:
|
||||||
|
return *(UINT16 *)arg;
|
||||||
|
|
||||||
|
case FFI_TYPE_SINT32:
|
||||||
|
case FFI_TYPE_UINT32:
|
||||||
|
case FFI_TYPE_POINTER:
|
||||||
|
case FFI_TYPE_FLOAT:
|
||||||
|
return *(UINT32 *)arg;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
ecif.rvalue = rvalue;
|
|
||||||
|
|
||||||
|
struct call_frame
|
||||||
|
{
|
||||||
|
void *ebp; /* 0 */
|
||||||
|
void *retaddr; /* 4 */
|
||||||
|
void (*fn)(void); /* 8 */
|
||||||
|
int flags; /* 12 */
|
||||||
|
void *rvalue; /* 16 */
|
||||||
|
unsigned regs[3]; /* 20-28 */
|
||||||
|
};
|
||||||
|
|
||||||
switch (cif->abi)
|
struct abi_params
|
||||||
|
{
|
||||||
|
int dir; /* parameter growth direction */
|
||||||
|
int nregs; /* number of register parameters */
|
||||||
|
int regs[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct abi_params abi_params[FFI_LAST_ABI] = {
|
||||||
|
[FFI_SYSV] = { 1, 0 },
|
||||||
|
[FFI_THISCALL] = { 1, 1, { R_ECX } },
|
||||||
|
[FFI_FASTCALL] = { 1, 2, { R_ECX, R_EDX } },
|
||||||
|
[FFI_STDCALL] = { 1, 0 },
|
||||||
|
[FFI_PASCAL] = { -1, 0 },
|
||||||
|
[FFI_REGISTER] = { -1, 3, { R_EAX, R_EDX, R_ECX } },
|
||||||
|
[FFI_MS_CDECL] = { 1, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void ffi_call_i386(struct call_frame *, char *)
|
||||||
|
FFI_HIDDEN __declspec(fastcall);
|
||||||
|
|
||||||
|
void
|
||||||
|
ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||||
|
{
|
||||||
|
size_t rsize, bytes;
|
||||||
|
struct call_frame *frame;
|
||||||
|
char *stack, *argp;
|
||||||
|
ffi_type **arg_types;
|
||||||
|
int flags, cabi, i, n, dir, narg_reg;
|
||||||
|
const struct abi_params *pabi;
|
||||||
|
|
||||||
|
flags = cif->flags;
|
||||||
|
cabi = cif->abi;
|
||||||
|
pabi = &abi_params[cabi];
|
||||||
|
dir = pabi->dir;
|
||||||
|
|
||||||
|
rsize = 0;
|
||||||
|
if (rvalue == NULL)
|
||||||
{
|
{
|
||||||
#ifndef X86_WIN32
|
switch (flags)
|
||||||
case FFI_SYSV:
|
{
|
||||||
ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue,
|
case X86_RET_FLOAT:
|
||||||
fn);
|
case X86_RET_DOUBLE:
|
||||||
break;
|
case X86_RET_LDOUBLE:
|
||||||
#else
|
case X86_RET_STRUCTPOP:
|
||||||
case FFI_SYSV:
|
case X86_RET_STRUCTARG:
|
||||||
case FFI_MS_CDECL:
|
/* The float cases need to pop the 387 stack.
|
||||||
#endif
|
The struct cases need to pass a valid pointer to the callee. */
|
||||||
case FFI_STDCALL:
|
rsize = cif->rtype->size;
|
||||||
case FFI_THISCALL:
|
|
||||||
case FFI_FASTCALL:
|
|
||||||
case FFI_PASCAL:
|
|
||||||
case FFI_REGISTER:
|
|
||||||
ffi_call_win32(ffi_prep_args, &ecif, cif->abi, cif->bytes, cif->flags,
|
|
||||||
ecif.rvalue, fn);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
FFI_ASSERT(0);
|
/* We can pretend that the callee returns nothing. */
|
||||||
|
flags = X86_RET_VOID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = cif->bytes;
|
||||||
|
stack = alloca(bytes + sizeof(*frame) + rsize);
|
||||||
|
argp = (dir < 0 ? stack + bytes : stack);
|
||||||
|
frame = (struct call_frame *)(stack + bytes);
|
||||||
|
if (rsize)
|
||||||
|
rvalue = frame + 1;
|
||||||
|
|
||||||
|
frame->fn = fn;
|
||||||
|
frame->flags = flags;
|
||||||
|
frame->rvalue = rvalue;
|
||||||
|
|
||||||
|
narg_reg = 0;
|
||||||
|
switch (flags)
|
||||||
|
{
|
||||||
|
case X86_RET_STRUCTARG:
|
||||||
|
/* The pointer is passed as the first argument. */
|
||||||
|
if (pabi->nregs > 0)
|
||||||
|
{
|
||||||
|
frame->regs[pabi->regs[0]] = (unsigned)rvalue;
|
||||||
|
narg_reg = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fallthru */
|
||||||
|
case X86_RET_STRUCTPOP:
|
||||||
|
*(void **)argp = rvalue;
|
||||||
|
argp += sizeof(void *);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_types = cif->arg_types;
|
||||||
|
for (i = 0, n = cif->nargs; i < n; i++)
|
||||||
|
{
|
||||||
|
ffi_type *ty = arg_types[i];
|
||||||
|
void *valp = avalue[i];
|
||||||
|
size_t z = ty->size;
|
||||||
|
int t = ty->type;
|
||||||
|
|
||||||
|
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT)
|
||||||
|
{
|
||||||
|
ffi_arg val = extend_basic_type (valp, t);
|
||||||
|
|
||||||
|
if (t != FFI_TYPE_FLOAT && narg_reg < pabi->nregs)
|
||||||
|
frame->regs[pabi->regs[narg_reg++]] = val;
|
||||||
|
else if (dir < 0)
|
||||||
|
{
|
||||||
|
argp -= 4;
|
||||||
|
*(ffi_arg *)argp = val;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(ffi_arg *)argp = val;
|
||||||
|
argp += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t za = ALIGN (z, FFI_SIZEOF_ARG);
|
||||||
|
if (dir < 0)
|
||||||
|
{
|
||||||
|
argp -= za;
|
||||||
|
memcpy (argp, valp, z);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy (argp, valp, z);
|
||||||
|
argp += za;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FFI_ASSERT (dir > 0 || argp == stack);
|
||||||
|
|
||||||
|
ffi_call_i386 (frame, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -641,88 +642,92 @@ ffi_prep_raw_closure_loc (ffi_raw_closure* closure,
|
|||||||
return FFI_OK;
|
return FFI_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
ffi_prep_args_raw(char *stack, extended_cif *ecif)
|
|
||||||
{
|
|
||||||
const ffi_cif *cif = ecif->cif;
|
|
||||||
unsigned int i, passed_regs = 0;
|
|
||||||
|
|
||||||
const unsigned int abi = cif->abi;
|
|
||||||
const unsigned int max_regs = (abi == FFI_THISCALL) ? 1
|
|
||||||
: (abi == FFI_FASTCALL) ? 2
|
|
||||||
: (abi == FFI_REGISTER) ? 3
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
if (cif->flags == FFI_TYPE_STRUCT)
|
|
||||||
++passed_regs;
|
|
||||||
|
|
||||||
for (i = 0; i < cif->nargs && passed_regs <= max_regs; i++)
|
|
||||||
{
|
|
||||||
if (cif->arg_types[i]->type == FFI_TYPE_FLOAT
|
|
||||||
|| cif->arg_types[i]->type == FFI_TYPE_STRUCT)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size_t sz = cif->arg_types[i]->size;
|
|
||||||
if (sz == 0 || sz > FFI_SIZEOF_ARG)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
++passed_regs;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy (stack, ecif->avalue, cif->bytes);
|
|
||||||
return passed_regs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we borrow this routine from libffi (it must be changed, though, to
|
|
||||||
* actually call the function passed in the first argument. as of
|
|
||||||
* libffi-1.20, this is not the case.)
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *fake_avalue)
|
ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *avalue)
|
||||||
{
|
{
|
||||||
extended_cif ecif;
|
size_t rsize, bytes;
|
||||||
void **avalue = (void **)fake_avalue;
|
struct call_frame *frame;
|
||||||
|
char *stack, *argp;
|
||||||
|
ffi_type **arg_types;
|
||||||
|
int flags, cabi, i, n, narg_reg;
|
||||||
|
const struct abi_params *pabi;
|
||||||
|
|
||||||
ecif.cif = cif;
|
flags = cif->flags;
|
||||||
ecif.avalue = avalue;
|
cabi = cif->abi;
|
||||||
|
pabi = &abi_params[cabi];
|
||||||
|
|
||||||
/* If the return value is a struct and we don't have a return */
|
rsize = 0;
|
||||||
/* value address then we need to make one */
|
if (rvalue == NULL)
|
||||||
|
|
||||||
if (rvalue == NULL
|
|
||||||
&& (cif->flags == FFI_TYPE_STRUCT
|
|
||||||
|| cif->flags == FFI_TYPE_MS_STRUCT))
|
|
||||||
{
|
{
|
||||||
ecif.rvalue = alloca(cif->rtype->size);
|
switch (flags)
|
||||||
}
|
|
||||||
else
|
|
||||||
ecif.rvalue = rvalue;
|
|
||||||
|
|
||||||
|
|
||||||
switch (cif->abi)
|
|
||||||
{
|
{
|
||||||
#ifndef X86_WIN32
|
case X86_RET_FLOAT:
|
||||||
case FFI_SYSV:
|
case X86_RET_DOUBLE:
|
||||||
ffi_call_SYSV(ffi_prep_args_raw, &ecif, cif->bytes, cif->flags,
|
case X86_RET_LDOUBLE:
|
||||||
ecif.rvalue, fn);
|
case X86_RET_STRUCTPOP:
|
||||||
break;
|
case X86_RET_STRUCTARG:
|
||||||
#else
|
/* The float cases need to pop the 387 stack.
|
||||||
case FFI_SYSV:
|
The struct cases need to pass a valid pointer to the callee. */
|
||||||
case FFI_MS_CDECL:
|
rsize = cif->rtype->size;
|
||||||
#endif
|
|
||||||
case FFI_STDCALL:
|
|
||||||
case FFI_THISCALL:
|
|
||||||
case FFI_FASTCALL:
|
|
||||||
case FFI_PASCAL:
|
|
||||||
case FFI_REGISTER:
|
|
||||||
ffi_call_win32(ffi_prep_args_raw, &ecif, cif->abi, cif->bytes, cif->flags,
|
|
||||||
ecif.rvalue, fn);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
FFI_ASSERT(0);
|
/* We can pretend that the callee returns nothing. */
|
||||||
|
flags = X86_RET_VOID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = cif->bytes;
|
||||||
|
argp = stack = alloca(bytes + sizeof(*frame) + rsize);
|
||||||
|
frame = (struct call_frame *)(stack + bytes);
|
||||||
|
if (rsize)
|
||||||
|
rvalue = frame + 1;
|
||||||
|
|
||||||
|
narg_reg = 0;
|
||||||
|
switch (flags)
|
||||||
|
{
|
||||||
|
case X86_RET_STRUCTARG:
|
||||||
|
/* The pointer is passed as the first argument. */
|
||||||
|
if (pabi->nregs > 0)
|
||||||
|
{
|
||||||
|
frame->regs[pabi->regs[0]] = (unsigned)rvalue;
|
||||||
|
narg_reg = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fallthru */
|
||||||
|
case X86_RET_STRUCTPOP:
|
||||||
|
*(void **)argp = rvalue;
|
||||||
|
argp += sizeof(void *);
|
||||||
|
bytes -= sizeof(void *);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_types = cif->arg_types;
|
||||||
|
for (i = 0, n = cif->nargs; narg_reg < pabi->nregs && i < n; i++)
|
||||||
|
{
|
||||||
|
ffi_type *ty = arg_types[i];
|
||||||
|
size_t z = ty->size;
|
||||||
|
int t = ty->type;
|
||||||
|
|
||||||
|
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT && t != FFI_TYPE_FLOAT)
|
||||||
|
{
|
||||||
|
ffi_arg val = extend_basic_type (avalue, t);
|
||||||
|
frame->regs[pabi->regs[narg_reg++]] = val;
|
||||||
|
z = FFI_SIZEOF_ARG;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy (argp, avalue, z);
|
||||||
|
z = ALIGN (z, FFI_SIZEOF_ARG);
|
||||||
|
argp += z;
|
||||||
|
}
|
||||||
|
avalue += z;
|
||||||
|
bytes -= z;
|
||||||
|
}
|
||||||
|
if (i < n)
|
||||||
|
memcpy (argp, avalue, bytes);
|
||||||
|
|
||||||
|
ffi_call_i386 (frame, stack);
|
||||||
}
|
}
|
||||||
#endif /* !FFI_NO_RAW_API */
|
#endif /* !FFI_NO_RAW_API */
|
||||||
#endif /* !__x86_64__ */
|
#endif /* !__x86_64__ */
|
||||||
|
|||||||
@@ -98,11 +98,7 @@ typedef enum ffi_abi {
|
|||||||
FFI_PASCAL = 6,
|
FFI_PASCAL = 6,
|
||||||
FFI_REGISTER = 7,
|
FFI_REGISTER = 7,
|
||||||
FFI_LAST_ABI,
|
FFI_LAST_ABI,
|
||||||
# ifdef _MSC_VER
|
|
||||||
FFI_DEFAULT_ABI = FFI_MS_CDECL
|
FFI_DEFAULT_ABI = FFI_MS_CDECL
|
||||||
# else
|
|
||||||
FFI_DEFAULT_ABI = FFI_SYSV
|
|
||||||
# endif
|
|
||||||
#else
|
#else
|
||||||
FFI_FIRST_ABI = 0,
|
FFI_FIRST_ABI = 0,
|
||||||
FFI_SYSV = 1,
|
FFI_SYSV = 1,
|
||||||
|
|||||||
23
src/x86/internal.h
Normal file
23
src/x86/internal.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#define X86_RET_FLOAT 0
|
||||||
|
#define X86_RET_DOUBLE 1
|
||||||
|
#define X86_RET_LDOUBLE 2
|
||||||
|
#define X86_RET_SINT8 3
|
||||||
|
#define X86_RET_SINT16 4
|
||||||
|
#define X86_RET_UINT8 5
|
||||||
|
#define X86_RET_UINT16 6
|
||||||
|
#define X86_RET_INT64 7
|
||||||
|
#define X86_RET_INT32 8
|
||||||
|
#define X86_RET_VOID 9
|
||||||
|
#define X86_RET_STRUCTPOP 10
|
||||||
|
#define X86_RET_STRUCTARG 11
|
||||||
|
#define X86_RET_STRUCT_1B 12
|
||||||
|
#define X86_RET_STRUCT_2B 13
|
||||||
|
#define X86_RET_UNUSED14 14
|
||||||
|
#define X86_RET_UNUSED15 15
|
||||||
|
|
||||||
|
#define X86_RET_TYPE_MASK 15
|
||||||
|
#define X86_RET_POP_SHIFT 4
|
||||||
|
|
||||||
|
#define R_EAX 0
|
||||||
|
#define R_EDX 1
|
||||||
|
#define R_ECX 2
|
||||||
237
src/x86/sysv.S
237
src/x86/sysv.S
@@ -31,143 +31,144 @@
|
|||||||
#include <fficonfig.h>
|
#include <fficonfig.h>
|
||||||
#include <ffi.h>
|
#include <ffi.h>
|
||||||
#include <ffi_cfi.h>
|
#include <ffi_cfi.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
.text
|
#define C2(X, Y) X ## Y
|
||||||
|
#define C1(X, Y) C2(X, Y)
|
||||||
|
#ifdef __USER_LABEL_PREFIX__
|
||||||
|
# define C(X) C1(__USER_LABEL_PREFIX__, X)
|
||||||
|
#else
|
||||||
|
# define C(X) X
|
||||||
|
#endif
|
||||||
|
|
||||||
.globl ffi_prep_args
|
#ifdef __ELF__
|
||||||
|
# define ENDF(X) .type X,@function; .size X, . - X
|
||||||
|
#else
|
||||||
|
# define ENDF(X)
|
||||||
|
#endif
|
||||||
|
|
||||||
.align 4
|
/* This macro allows the safe creation of jump tables without an
|
||||||
.globl ffi_call_SYSV
|
actual table. The entry points into the table are all 8 bytes.
|
||||||
.type ffi_call_SYSV,@function
|
The use of ORG asserts that we're at the correct location. */
|
||||||
|
#define E(X) .align 8; .org 0b + X * 8
|
||||||
|
|
||||||
ffi_call_SYSV:
|
.text
|
||||||
|
.align 16
|
||||||
|
.globl C(ffi_call_i386)
|
||||||
|
FFI_HIDDEN(C(ffi_call_i386))
|
||||||
|
|
||||||
|
/* This is declared as
|
||||||
|
|
||||||
|
void ffi_call_i386(struct ffi_call_frame *frame, char *argp)
|
||||||
|
__attribute__((fastcall));
|
||||||
|
|
||||||
|
This the arguments are present in
|
||||||
|
|
||||||
|
ecx: frame
|
||||||
|
edx: argp
|
||||||
|
*/
|
||||||
|
|
||||||
|
C(ffi_call_i386):
|
||||||
cfi_startproc
|
cfi_startproc
|
||||||
pushl %ebp
|
movl (%esp), %eax /* move the return address */
|
||||||
cfi_adjust_cfa_offset(4)
|
movl %ebp, (%ecx) /* store %ebp into local frame */
|
||||||
|
movl %eax, 4(%ecx) /* store retaddr into local frame */
|
||||||
|
|
||||||
|
/* New stack frame based off ebp. This is a itty bit of unwind
|
||||||
|
trickery in that the CFA *has* changed. There is no easy way
|
||||||
|
to describe it correctly on entry to the function. Fortunately,
|
||||||
|
it doesn't matter too much since at all points we can correctly
|
||||||
|
unwind back to ffi_call. Note that the location to which we
|
||||||
|
moved the return address is (the new) CFA-4, so from the
|
||||||
|
perspective of the unwind info, it hasn't moved. */
|
||||||
|
movl %ecx, %ebp
|
||||||
|
cfi_def_cfa(%ebp, 8)
|
||||||
cfi_rel_offset(%ebp, 0)
|
cfi_rel_offset(%ebp, 0)
|
||||||
movl %esp,%ebp
|
|
||||||
cfi_def_cfa_register(%ebp)
|
|
||||||
/* Make room for all of the new args. */
|
|
||||||
movl 16(%ebp),%ecx
|
|
||||||
subl %ecx,%esp
|
|
||||||
|
|
||||||
/* Align the stack pointer to 16-bytes */
|
movl %edx, %esp /* set outgoing argument stack */
|
||||||
andl $0xfffffff0, %esp
|
movl 20+R_EAX*4(%ebp), %eax /* set register arguments */
|
||||||
|
movl 20+R_EDX*4(%ebp), %edx
|
||||||
|
movl 20+R_ECX*4(%ebp), %ecx
|
||||||
|
|
||||||
movl %esp,%eax
|
|
||||||
|
|
||||||
/* Place all of the ffi_prep_args in position */
|
|
||||||
pushl 12(%ebp)
|
|
||||||
pushl %eax
|
|
||||||
call *8(%ebp)
|
call *8(%ebp)
|
||||||
|
|
||||||
/* Return stack to previous state and call the function */
|
movl 12(%ebp), %ecx /* load return type code */
|
||||||
addl $8,%esp
|
movl %ebx, 8(%ebp) /* preserve %ebx */
|
||||||
|
cfi_rel_offset(%ebp, 8)
|
||||||
|
|
||||||
call *28(%ebp)
|
andl $X86_RET_TYPE_MASK, %ecx
|
||||||
|
#ifdef __PIC__
|
||||||
/* Load %ecx with the return type code */
|
call __x86.get_pc_thunk.bx
|
||||||
movl 20(%ebp),%ecx
|
1: leal 0f-1b(%ebx, %ecx, 8), %ebx
|
||||||
|
#else
|
||||||
/* Protect %esi. We're going to pop it in the epilogue. */
|
leal 0f(,%ecx, 8), %ebx
|
||||||
pushl %esi
|
#endif
|
||||||
|
movl 16(%ebp), %ecx /* load result address */
|
||||||
/* If the return value pointer is NULL, assume no return value. */
|
jmp *%ebx
|
||||||
cmpl $0,24(%ebp)
|
|
||||||
jne 0f
|
|
||||||
|
|
||||||
/* Even if there is no space for the return value, we are
|
|
||||||
obliged to handle floating-point values. */
|
|
||||||
cmpl $FFI_TYPE_FLOAT,%ecx
|
|
||||||
jne noretval
|
|
||||||
fstp %st(0)
|
|
||||||
|
|
||||||
jmp epilogue
|
|
||||||
|
|
||||||
|
.align 8
|
||||||
0:
|
0:
|
||||||
call 1f
|
E(X86_RET_FLOAT)
|
||||||
|
|
||||||
.Lstore_table:
|
|
||||||
.long noretval-.Lstore_table /* FFI_TYPE_VOID */
|
|
||||||
.long retint-.Lstore_table /* FFI_TYPE_INT */
|
|
||||||
.long retfloat-.Lstore_table /* FFI_TYPE_FLOAT */
|
|
||||||
.long retdouble-.Lstore_table /* FFI_TYPE_DOUBLE */
|
|
||||||
.long retlongdouble-.Lstore_table /* FFI_TYPE_LONGDOUBLE */
|
|
||||||
.long retuint8-.Lstore_table /* FFI_TYPE_UINT8 */
|
|
||||||
.long retsint8-.Lstore_table /* FFI_TYPE_SINT8 */
|
|
||||||
.long retuint16-.Lstore_table /* FFI_TYPE_UINT16 */
|
|
||||||
.long retsint16-.Lstore_table /* FFI_TYPE_SINT16 */
|
|
||||||
.long retint-.Lstore_table /* FFI_TYPE_UINT32 */
|
|
||||||
.long retint-.Lstore_table /* FFI_TYPE_SINT32 */
|
|
||||||
.long retint64-.Lstore_table /* FFI_TYPE_UINT64 */
|
|
||||||
.long retint64-.Lstore_table /* FFI_TYPE_SINT64 */
|
|
||||||
.long retstruct-.Lstore_table /* FFI_TYPE_STRUCT */
|
|
||||||
.long retint-.Lstore_table /* FFI_TYPE_POINTER */
|
|
||||||
|
|
||||||
1:
|
|
||||||
pop %esi
|
|
||||||
add (%esi, %ecx, 4), %esi
|
|
||||||
jmp *%esi
|
|
||||||
|
|
||||||
/* Sign/zero extend as appropriate. */
|
|
||||||
retsint8:
|
|
||||||
movsbl %al, %eax
|
|
||||||
jmp retint
|
|
||||||
|
|
||||||
retsint16:
|
|
||||||
movswl %ax, %eax
|
|
||||||
jmp retint
|
|
||||||
|
|
||||||
retuint8:
|
|
||||||
movzbl %al, %eax
|
|
||||||
jmp retint
|
|
||||||
|
|
||||||
retuint16:
|
|
||||||
movzwl %ax, %eax
|
|
||||||
jmp retint
|
|
||||||
|
|
||||||
retfloat:
|
|
||||||
/* Load %ecx with the pointer to storage for the return value */
|
|
||||||
movl 24(%ebp),%ecx
|
|
||||||
fstps (%ecx)
|
fstps (%ecx)
|
||||||
jmp epilogue
|
jmp 9f
|
||||||
|
E(X86_RET_DOUBLE)
|
||||||
retdouble:
|
|
||||||
/* Load %ecx with the pointer to storage for the return value */
|
|
||||||
movl 24(%ebp),%ecx
|
|
||||||
fstpl (%ecx)
|
fstpl (%ecx)
|
||||||
jmp epilogue
|
jmp 9f
|
||||||
|
E(X86_RET_LDOUBLE)
|
||||||
retlongdouble:
|
|
||||||
/* Load %ecx with the pointer to storage for the return value */
|
|
||||||
movl 24(%ebp),%ecx
|
|
||||||
fstpt (%ecx)
|
fstpt (%ecx)
|
||||||
jmp epilogue
|
jmp 9f
|
||||||
|
E(X86_RET_SINT8)
|
||||||
retint64:
|
movsbl %al, %eax
|
||||||
/* Load %ecx with the pointer to storage for the return value */
|
mov %eax, (%ecx)
|
||||||
movl 24(%ebp),%ecx
|
jmp 9f
|
||||||
movl %eax,0(%ecx)
|
E(X86_RET_SINT16)
|
||||||
movl %edx,4(%ecx)
|
movswl %ax, %eax
|
||||||
jmp epilogue
|
mov %eax, (%ecx)
|
||||||
|
jmp 9f
|
||||||
retint:
|
E(X86_RET_UINT8)
|
||||||
/* Load %ecx with the pointer to storage for the return value */
|
movzbl %al, %eax
|
||||||
movl 24(%ebp),%ecx
|
mov %eax, (%ecx)
|
||||||
movl %eax,0(%ecx)
|
jmp 9f
|
||||||
|
E(X86_RET_UINT16)
|
||||||
retstruct:
|
movzwl %ax, %eax
|
||||||
/* Nothing to do! */
|
mov %eax, (%ecx)
|
||||||
|
jmp 9f
|
||||||
noretval:
|
E(X86_RET_INT64)
|
||||||
epilogue:
|
movl %edx, 4(%ecx)
|
||||||
popl %esi
|
/* fallthru */
|
||||||
movl %ebp,%esp
|
E(X86_RET_INT32)
|
||||||
|
movl %eax, (%ecx)
|
||||||
|
/* fallthru */
|
||||||
|
E(X86_RET_VOID)
|
||||||
|
9: movl 8(%ebp), %ebx
|
||||||
|
movl %ebp, %esp
|
||||||
popl %ebp
|
popl %ebp
|
||||||
|
cfi_remember_state
|
||||||
|
cfi_def_cfa(%esp, 4)
|
||||||
|
cfi_restore(%ebx)
|
||||||
|
cfi_restore(%ebp)
|
||||||
ret
|
ret
|
||||||
|
cfi_restore_state
|
||||||
|
|
||||||
|
E(X86_RET_STRUCTPOP)
|
||||||
|
jmp 9b
|
||||||
|
E(X86_RET_STRUCTARG)
|
||||||
|
jmp 9b
|
||||||
|
E(X86_RET_STRUCT_1B)
|
||||||
|
movb %al, (%ecx)
|
||||||
|
jmp 9b
|
||||||
|
E(X86_RET_STRUCT_2B)
|
||||||
|
movw %ax, (%ecx)
|
||||||
|
jmp 9b
|
||||||
|
|
||||||
|
/* Fill out the table so that bad values are predictable. */
|
||||||
|
E(X86_RET_UNUSED14)
|
||||||
|
ud2
|
||||||
|
E(X86_RET_UNUSED15)
|
||||||
|
ud2
|
||||||
|
|
||||||
cfi_endproc
|
cfi_endproc
|
||||||
.ffi_call_SYSV_end:
|
ENDF(C(ffi_call_i386))
|
||||||
.size ffi_call_SYSV,.ffi_call_SYSV_end-ffi_call_SYSV
|
|
||||||
|
|
||||||
.align 4
|
.align 4
|
||||||
FFI_HIDDEN (ffi_closure_SYSV)
|
FFI_HIDDEN (ffi_closure_SYSV)
|
||||||
|
|||||||
Reference in New Issue
Block a user