#if defined(__x86_64__) || defined(_M_AMD64)
#include <ffi.h>
#include <ffi_common.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef X86_WIN64
#define EFI64(name) name
#else
#define EFI64(name) FFI_HIDDEN name##_efi64
#endif
struct win64_call_frame
{
UINT64 rbp;
UINT64 retaddr;
UINT64 fn;
UINT64 flags;
UINT64 rvalue;
};
extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
void *closure) FFI_HIDDEN;
ffi_status FFI_HIDDEN
EFI64(ffi_prep_cif_machdep)(ffi_cif *cif)
{
int flags, n;
switch (cif->abi)
{
case FFI_WIN64:
case FFI_GNUW64:
break;
default:
return FFI_BAD_ABI;
}
flags = cif->rtype->type;
switch (flags)
{
default:
break;
case FFI_TYPE_LONGDOUBLE:
if (cif->abi == FFI_GNUW64)
flags = FFI_TYPE_STRUCT;
break;
case FFI_TYPE_COMPLEX:
flags = FFI_TYPE_STRUCT;
case FFI_TYPE_STRUCT:
switch (cif->rtype->size)
{
case 8:
flags = FFI_TYPE_UINT64;
break;
case 4:
flags = FFI_TYPE_SMALL_STRUCT_4B;
break;
case 2:
flags = FFI_TYPE_SMALL_STRUCT_2B;
break;
case 1:
flags = FFI_TYPE_SMALL_STRUCT_1B;
break;
}
break;
}
cif->flags = flags;
n = cif->nargs;
n += (flags == FFI_TYPE_STRUCT);
if (n < 4)
n = 4;
cif->bytes = n * 8;
return FFI_OK;
}
static void
ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
void **avalue, void *closure)
{
int i, j, n, flags;
UINT64 *stack;
size_t rsize;
struct win64_call_frame *frame;
FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64);
flags = cif->flags;
rsize = 0;
if (rvalue == NULL)
{
if (flags == FFI_TYPE_STRUCT)
rsize = cif->rtype->size;
else
flags = FFI_TYPE_VOID;
}
stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
if (rsize)
rvalue = frame + 1;
frame->fn = (uintptr_t)fn;
frame->flags = flags;
frame->rvalue = (uintptr_t)rvalue;
j = 0;
if (flags == FFI_TYPE_STRUCT)
{
stack[0] = (uintptr_t)rvalue;
j = 1;
}
for (i = 0, n = cif->nargs; i < n; ++i, ++j)
{
switch (cif->arg_types[i]->size)
{
case 8:
stack[j] = *(UINT64 *)avalue[i];
break;
case 4:
stack[j] = *(UINT32 *)avalue[i];
break;
case 2:
stack[j] = *(UINT16 *)avalue[i];
break;
case 1:
stack[j] = *(UINT8 *)avalue[i];
break;
default:
stack[j] = (uintptr_t)avalue[i];
break;
}
}
ffi_call_win64 (stack, frame, closure);
}
void
EFI64(ffi_call)(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
{
ffi_call_int (cif, fn, rvalue, avalue, NULL);
}
void
EFI64(ffi_call_go)(ffi_cif *cif, void (*fn)(void), void *rvalue,
void **avalue, void *closure)
{
ffi_call_int (cif, fn, rvalue, avalue, closure);
}
extern void ffi_closure_win64(void) FFI_HIDDEN;
#ifdef FFI_GO_CLOSURES
extern void ffi_go_closure_win64(void) FFI_HIDDEN;
#endif
ffi_status
EFI64(ffi_prep_closure_loc)(ffi_closure* closure,
ffi_cif* cif,
void (*fun)(ffi_cif*, void*, void**, void*),
void *user_data,
void *codeloc)
{
static const unsigned char trampoline[16] = {
0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff,
0xff, 0x25, 0x03, 0x00, 0x00, 0x00,
0x0f, 0x1f, 0x00
};
char *tramp = closure->tramp;
switch (cif->abi)
{
case FFI_WIN64:
case FFI_GNUW64:
break;
default:
return FFI_BAD_ABI;
}
memcpy (tramp, trampoline, sizeof(trampoline));
*(UINT64 *)(tramp + 16) = (uintptr_t)ffi_closure_win64;
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
return FFI_OK;
}
#ifdef FFI_GO_CLOSURES
ffi_status
EFI64(ffi_prep_go_closure)(ffi_go_closure* closure, ffi_cif* cif,
void (*fun)(ffi_cif*, void*, void**, void*))
{
switch (cif->abi)
{
case FFI_WIN64:
case FFI_GNUW64:
break;
default:
return FFI_BAD_ABI;
}
closure->tramp = ffi_go_closure_win64;
closure->cif = cif;
closure->fun = fun;
return FFI_OK;
}
#endif
struct win64_closure_frame
{
UINT64 rvalue[2];
UINT64 fargs[4];
UINT64 retaddr;
UINT64 args[];
};
int FFI_HIDDEN __attribute__((ms_abi))
ffi_closure_win64_inner(ffi_cif *cif,
void (*fun)(ffi_cif*, void*, void**, void*),
void *user_data,
struct win64_closure_frame *frame)
{
void **avalue;
void *rvalue;
int i, n, nreg, flags;
avalue = alloca(cif->nargs * sizeof(void *));
rvalue = frame->rvalue;
nreg = 0;
flags = cif->flags;
if (flags == FFI_TYPE_STRUCT)
{
rvalue = (void *)(uintptr_t)frame->args[0];
frame->rvalue[0] = frame->args[0];
nreg = 1;
}
for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
{
size_t size = cif->arg_types[i]->size;
size_t type = cif->arg_types[i]->type;
void *a;
if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
{
if (nreg < 4)
a = &frame->fargs[nreg];
else
a = &frame->args[nreg];
}
else if (size == 1 || size == 2 || size == 4 || size == 8)
a = &frame->args[nreg];
else
a = (void *)(uintptr_t)frame->args[nreg];
avalue[i] = a;
}
fun (cif, rvalue, avalue, user_data);
return flags;
}
#endif