win64: Rewrite
It's way too different from the 32-bit ABIs with which it is currently associated. As seen from all of the existing XFAILs.
This commit is contained in:
281
src/x86/ffiw64.c
Normal file
281
src/x86/ffiw64.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/* -----------------------------------------------------------------------
|
||||
ffiw64.c - Copyright (c) 2014 Red Hat, Inc.
|
||||
|
||||
x86 win64 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 AUTHORS OR COPYRIGHT
|
||||
HOLDERS 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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef X86_WIN64
|
||||
|
||||
struct win64_call_frame
|
||||
{
|
||||
UINT64 rbp; /* 0 */
|
||||
UINT64 retaddr; /* 8 */
|
||||
UINT64 fn; /* 16 */
|
||||
UINT64 flags; /* 24 */
|
||||
UINT64 rvalue; /* 32 */
|
||||
};
|
||||
|
||||
extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
|
||||
void *closure) FFI_HIDDEN;
|
||||
|
||||
ffi_status
|
||||
ffi_prep_cif_machdep (ffi_cif *cif)
|
||||
{
|
||||
int flags, n;
|
||||
|
||||
if (cif->abi != FFI_WIN64)
|
||||
return FFI_BAD_ABI;
|
||||
|
||||
flags = cif->rtype->type;
|
||||
switch (flags)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case FFI_TYPE_LONGDOUBLE:
|
||||
flags = FFI_TYPE_STRUCT;
|
||||
break;
|
||||
case FFI_TYPE_COMPLEX:
|
||||
flags = FFI_TYPE_STRUCT;
|
||||
/* FALLTHRU */
|
||||
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;
|
||||
|
||||
/* Each argument either fits in a register, an 8 byte slot, or is
|
||||
passed by reference with the pointer in the 8 byte slot. */
|
||||
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_WIN64);
|
||||
|
||||
flags = cif->flags;
|
||||
rsize = 0;
|
||||
|
||||
/* If we have no return value for a structure, we need to create one.
|
||||
Otherwise we can ignore the return type entirely. */
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
extern void ffi_closure_win64(void) FFI_HIDDEN;
|
||||
extern void ffi_go_closure_win64(void) FFI_HIDDEN;
|
||||
|
||||
ffi_status
|
||||
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] = {
|
||||
/* leaq -0x7(%rip),%r10 # 0x0 */
|
||||
0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff,
|
||||
/* jmpq *0x3(%rip) # 0x10 */
|
||||
0xff, 0x25, 0x03, 0x00, 0x00, 0x00,
|
||||
/* nopl (%rax) */
|
||||
0x0f, 0x1f, 0x00
|
||||
};
|
||||
void *tramp = closure->tramp;
|
||||
|
||||
if (cif->abi != FFI_WIN64)
|
||||
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;
|
||||
}
|
||||
|
||||
ffi_status
|
||||
ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
|
||||
void (*fun)(ffi_cif*, void*, void**, void*))
|
||||
{
|
||||
if (cif->abi != FFI_WIN64)
|
||||
return FFI_BAD_ABI;
|
||||
|
||||
closure->tramp = ffi_go_closure_win64;
|
||||
closure->cif = cif;
|
||||
closure->fun = fun;
|
||||
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
struct win64_closure_frame
|
||||
{
|
||||
UINT64 rvalue[2];
|
||||
UINT64 fargs[4];
|
||||
UINT64 retaddr;
|
||||
UINT64 args[];
|
||||
};
|
||||
|
||||
int FFI_HIDDEN
|
||||
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;
|
||||
|
||||
/* When returning a structure, the address is in the first argument.
|
||||
We must also be prepared to return the same address in eax, so
|
||||
install that address in the frame and pretend we return a pointer. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Invoke the closure. */
|
||||
fun (cif, rvalue, avalue, user_data);
|
||||
return flags;
|
||||
}
|
||||
|
||||
#endif /* X86_WIN64 */
|
||||
Reference in New Issue
Block a user