Index: ChangeLog.libffi =================================================================== RCS file: /cvs/libffi/libffi/ChangeLog.libffi,v retrieving revision 1.50 diff -u -b -w -r1.50 ChangeLog.libffi --- ChangeLog.libffi 4 Jun 2009 16:29:58 -0000 1.50 +++ ChangeLog.libffi 8 Jun 2009 19:20:19 -0000 @@ -1,3 +1,19 @@ +2009-06-08 Timothy Wall + + * Makefile.am, + configure.ac, + include/ffi.h.in, + include/ffi_common.h, + src/closures.c, + src/dlmalloc.c, + src/x86/ffi.c, + src/x86/ffitarget.h, + src/x86/win64.S (new), + README: Added win64 support (mingw or MSVC) + * ltcf-c.sh: properly escape cygwin/w32 path + * man/ffi_call.3: Clarify size requirements for return value. + * src/x86/ffi64.c: Fix filename in comment. + 2009-06-04 Andrew Haley * src/powerpc/ffitarget.h: Fix misapplied merge from gcc. Index: Makefile.am =================================================================== RCS file: /cvs/libffi/libffi/Makefile.am,v retrieving revision 1.23 diff -u -b -w -r1.23 Makefile.am --- Makefile.am 21 Feb 2008 13:36:18 -0000 1.23 +++ Makefile.am 8 Jun 2009 19:20:19 -0000 @@ -25,8 +25,8 @@ src/sh64/ffi.c src/sh64/sysv.S src/sh64/ffitarget.h \ src/sparc/v8.S src/sparc/v9.S src/sparc/ffitarget.h \ src/sparc/ffi.c src/x86/darwin64.S \ - src/x86/ffi.c src/x86/sysv.S src/x86/win32.S src/x86/darwin.S \ - src/x86/freebsd.S \ + src/x86/ffi.c src/x86/sysv.S src/x86/win32.S src/x86/win64.S \ + src/x86/darwin.S src/x86/freebsd.S \ src/x86/ffi64.c src/x86/unix64.S src/x86/ffitarget.h \ src/pa/ffitarget.h src/pa/ffi.c src/pa/linux.S src/pa/hpux32.S \ src/frv/ffi.c src/frv/eabi.S src/frv/ffitarget.h src/dlmalloc.c \ @@ -102,6 +102,9 @@ if X86_WIN32 nodist_libffi_la_SOURCES += src/x86/ffi.c src/x86/win32.S endif +if X86_WIN64 +nodist_libffi_la_SOURCES += src/x86/ffi.c src/x86/win64.S +endif if X86_DARWIN nodist_libffi_la_SOURCES += src/x86/ffi.c src/x86/darwin.S src/x86/ffi64.c src/x86/darwin64.S endif Index: README =================================================================== RCS file: /cvs/libffi/libffi/README,v retrieving revision 1.27 diff -u -b -w -r1.27 README --- README 19 Dec 2008 16:13:45 -0000 1.27 +++ README 8 Jun 2009 19:20:19 -0000 @@ -58,6 +58,7 @@ x86 linux x86 openbsd x86 solaris + x86-64 mingw x86-64 darwin x86-64 linux x86-64 OS X Index: configure.ac =================================================================== RCS file: /cvs/libffi/libffi/configure.ac,v retrieving revision 1.25 diff -u -b -w -r1.25 configure.ac --- configure.ac 3 Jun 2009 17:42:56 -0000 1.25 +++ configure.ac 8 Jun 2009 19:20:23 -0000 @@ -148,6 +148,7 @@ TARGET=X86_DARWIN; TARGETDIR=x86 ;; x86_64-*-cygwin* | x86_64-*-mingw*) + TARGET=X86_WIN64; TARGETDIR=x86 ;; x86_64-*-*) TARGET=X86_64; TARGETDIR=x86 @@ -165,6 +166,7 @@ AM_CONDITIONAL(X86, test x$TARGET = xX86) AM_CONDITIONAL(X86_FREEBSD, test x$TARGET = xX86_FREEBSD) AM_CONDITIONAL(X86_WIN32, test x$TARGET = xX86_WIN32) +AM_CONDITIONAL(X86_WIN64, test x$TARGET = xX86_WIN64) AM_CONDITIONAL(X86_DARWIN, test x$TARGET = xX86_DARWIN) AM_CONDITIONAL(ALPHA, test x$TARGET = xALPHA) AM_CONDITIONAL(IA64, test x$TARGET = xIA64) Index: ltcf-c.sh =================================================================== RCS file: /cvs/libffi/libffi/ltcf-c.sh,v retrieving revision 1.1 diff -u -b -w -r1.1 ltcf-c.sh --- ltcf-c.sh 29 Jan 2008 12:30:10 -0000 1.1 +++ ltcf-c.sh 8 Jun 2009 19:20:23 -0000 @@ -376,7 +376,7 @@ old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path='`cygpath -w "$srcfile"`' + fix_srcfile_path='\`cygpath -w \"\$srcfile\"\`' ;; darwin* | rhapsody*) Index: include/ffi.h.in =================================================================== RCS file: /cvs/libffi/libffi/include/ffi.h.in,v retrieving revision 1.12 diff -u -b -w -r1.12 ffi.h.in --- include/ffi.h.in 3 Apr 2008 18:57:34 -0000 1.12 +++ include/ffi.h.in 8 Jun 2009 19:20:23 -0000 @@ -67,6 +67,10 @@ #ifndef LIBFFI_ASM +#ifdef _MSC_VER +#define __attribute__(X) +#endif + #include #include Index: include/ffi_common.h =================================================================== RCS file: /cvs/libffi/libffi/include/ffi_common.h,v retrieving revision 1.7 diff -u -b -w -r1.7 ffi_common.h --- include/ffi_common.h 29 Jan 2008 15:15:17 -0000 1.7 +++ include/ffi_common.h 8 Jun 2009 19:20:23 -0000 @@ -18,7 +18,10 @@ /* Do not move this. Some versions of AIX are very picky about where this is positioned. */ #ifdef __GNUC__ +/* mingw64 defines this already in malloc.h. */ +#ifndef alloca # define alloca __builtin_alloca +#endif # define MAYBE_UNUSED __attribute__((__unused__)) #else # define MAYBE_UNUSED @@ -29,11 +32,15 @@ #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ +# ifdef _MSC_VER +# define alloca _alloca +# else char *alloca (); # endif # endif # endif #endif +#endif /* Check for the existence of memcpy. */ #if STDC_HEADERS @@ -77,6 +84,16 @@ } extended_cif; /* Terse sized type definitions. */ +#ifdef _MSC_VER +typedef unsigned char UINT8; +typedef signed char SINT8; +typedef unsigned short UINT16; +typedef signed short SINT16; +typedef unsigned int UINT32; +typedef signed int SINT32; +typedef unsigned __int64 UINT64; +typedef signed __int64 SINT64; +#else typedef unsigned int UINT8 __attribute__((__mode__(__QI__))); typedef signed int SINT8 __attribute__((__mode__(__QI__))); typedef unsigned int UINT16 __attribute__((__mode__(__HI__))); @@ -85,6 +102,7 @@ typedef signed int SINT32 __attribute__((__mode__(__SI__))); typedef unsigned int UINT64 __attribute__((__mode__(__DI__))); typedef signed int SINT64 __attribute__((__mode__(__DI__))); +#endif typedef float FLOAT32; Index: man/ffi_call.3 =================================================================== RCS file: /cvs/libffi/libffi/man/ffi_call.3,v retrieving revision 1.2 diff -u -b -w -r1.2 ffi_call.3 --- man/ffi_call.3 15 Feb 2008 15:30:26 -0000 1.2 +++ man/ffi_call.3 8 Jun 2009 19:20:23 -0000 @@ -34,8 +34,8 @@ .Nm ffi_call . .Pp .Fa rvalue -must point to storage that is sizeof(long) or larger. For smaller -return value sizes, the +must point to storage that is sizeof(ffi_arg) or larger for non-floating point +types. For smaller-sized return value types, the .Nm ffi_arg or .Nm ffi_sarg Index: src/closures.c =================================================================== RCS file: /cvs/libffi/libffi/src/closures.c,v retrieving revision 1.2 diff -u -b -w -r1.2 closures.c --- src/closures.c 15 Feb 2008 01:24:05 -0000 1.2 +++ src/closures.c 8 Jun 2009 19:20:23 -0000 @@ -42,6 +42,13 @@ locations in the virtual memory space, one location writable and another executable. */ # define FFI_MMAP_EXEC_WRIT 1 +# define HAVE_MNTENT 1 +# endif +# if defined(X86_WIN32) || defined(X86_WIN64) +/* Windows systems may have Data Execution Protection (DEP) enabled, + which requires the use of VirtualMalloc/VirtualFree to alloc/free + executable memory. */ +# define FFI_MMAP_EXEC_WRIT 1 # endif #endif @@ -60,7 +67,11 @@ #define USE_LOCKS 1 #define USE_DL_PREFIX 1 +#ifdef __GNUC__ +#ifndef USE_BUILTIN_FFS #define USE_BUILTIN_FFS 1 +#endif +#endif /* We need to use mmap, not sbrk. */ #define HAVE_MORECORE 0 @@ -90,10 +101,15 @@ #include #include #include +#ifndef _MSC_VER #include +#endif #include #include +#if !defined(X86_WIN32) && !defined(X86_WIN64) +#ifdef HAVE_MNTENT #include +#endif /* HAVE_MNTENT */ #include #include @@ -150,6 +166,7 @@ #define is_selinux_enabled() 0 #endif +#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */ /* Declare all functions defined in dlmalloc.c as static. */ static void *dlmalloc(size_t); @@ -168,9 +185,11 @@ static size_t dlmalloc_usable_size(void*) MAYBE_UNUSED; static void dlmalloc_stats(void) MAYBE_UNUSED; +#if !defined(X86_WIN32) && !defined(X86_WIN64) /* Use these for mmap and munmap within dlmalloc.c. */ static void *dlmmap(void *, size_t, int, int, int, off_t); static int dlmunmap(void *, size_t); +#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */ #define mmap dlmmap #define munmap dlmunmap @@ -180,6 +199,8 @@ #undef mmap #undef munmap +#if !defined(X86_WIN32) && !defined(X86_WIN64) + /* A mutex used to synchronize access to *exec* variables in this file. */ static pthread_mutex_t open_temp_exec_file_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -232,6 +253,7 @@ return open_temp_exec_file_dir (value); } +#ifdef HAVE_MNTENT /* Open a temporary file in an executable and writable mount point listed in the mounts file. Subsequent calls with the same mounts keep searching for mount points in the same file. Providing NULL @@ -278,6 +300,7 @@ return fd; } } +#endif /* HAVE_MNTENT */ /* Instructions to look for a location to hold a temporary file that can be mapped in for execution. */ @@ -292,8 +315,10 @@ { open_temp_exec_file_dir, "/var/tmp", 0 }, { open_temp_exec_file_dir, "/dev/shm", 0 }, { open_temp_exec_file_env, "HOME", 0 }, +#ifdef HAVE_MNTENT { open_temp_exec_file_mnt, "/etc/mtab", 1 }, { open_temp_exec_file_mnt, "/proc/mounts", 1 }, +#endif /* HAVE_MNTENT */ }; /* Current index into open_temp_exec_file_opts. */ @@ -489,6 +514,8 @@ } #endif +#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */ + /* Allocate a chunk of memory with the given size. Returns a pointer to the writable address, and sets *CODE to the executable corresponding virtual address. */ Index: src/dlmalloc.c =================================================================== RCS file: /cvs/libffi/libffi/src/dlmalloc.c,v retrieving revision 1.1 diff -u -b -w -r1.1 dlmalloc.c --- src/dlmalloc.c 29 Jan 2008 15:16:43 -0000 1.1 +++ src/dlmalloc.c 8 Jun 2009 19:20:24 -0000 @@ -1140,9 +1140,9 @@ /*------------------------------ internal #includes ---------------------- */ -#ifdef WIN32 +#ifdef _MSC_VER #pragma warning( disable : 4146 ) /* no "unsigned" warnings */ -#endif /* WIN32 */ +#endif /* _MSC_VER */ #include /* for printing in malloc_stats */ @@ -1315,14 +1315,14 @@ /* Win32 MMAP via VirtualAlloc */ static void* win32mmap(size_t size) { - void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ static void* win32direct_mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); + PAGE_EXECUTE_READWRITE); return (ptr != 0)? ptr: MFAIL; } Index: src/prep_cif.c =================================================================== RCS file: /cvs/libffi/libffi/src/prep_cif.c,v retrieving revision 1.7 diff -u -b -w -r1.7 prep_cif.c --- src/prep_cif.c 15 Feb 2008 01:24:05 -0000 1.7 +++ src/prep_cif.c 8 Jun 2009 19:20:24 -0000 @@ -39,9 +39,11 @@ FFI_ASSERT(arg != NULL); - FFI_ASSERT(arg->elements != NULL); FFI_ASSERT(arg->size == 0); - FFI_ASSERT(arg->alignment == 0); + + if (arg->elements == NULL || arg->alignment != 0) { + return FFI_BAD_TYPEDEF; + } ptr = &(arg->elements[0]); @@ -93,7 +95,12 @@ ffi_type **ptr; FFI_ASSERT(cif != NULL); - FFI_ASSERT((abi > FFI_FIRST_ABI) && (abi <= FFI_DEFAULT_ABI)); + /* FIXME: most targets have a broken definition of FFI_LAST_ABI */ + /* + if (abi < FFI_FIRST_ABI || abi > FFI_LAST_ABI) { + return FFI_BAD_ABI; + } + */ cif->abi = abi; cif->arg_types = atypes; Index: src/x86/ffi.c =================================================================== RCS file: /cvs/libffi/libffi/src/x86/ffi.c,v retrieving revision 1.15 diff -u -b -w -r1.15 ffi.c --- src/x86/ffi.c 3 Apr 2008 18:57:34 -0000 1.15 +++ src/x86/ffi.c 8 Jun 2009 19:20:24 -0000 @@ -28,7 +28,11 @@ DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------- */ -#ifndef __x86_64__ +#if !defined(__x86_64__) || defined(_WIN64) + +#ifdef _WIN64 +#include +#endif #include #include @@ -47,10 +51,15 @@ argp = stack; - if (ecif->cif->flags == FFI_TYPE_STRUCT) + if (ecif->cif->flags == FFI_TYPE_STRUCT +#ifdef X86_WIN64 + && (ecif->cif->rtype->size != 1 && ecif->cif->rtype->size != 2 + && ecif->cif->rtype->size != 4 && ecif->cif->rtype->size != 8) +#endif + ) { *(void **) argp = ecif->rvalue; - argp += 4; + argp += sizeof(void*); } p_argv = ecif->avalue; @@ -62,41 +71,59 @@ size_t z; /* Align if necessary */ - if ((sizeof(int) - 1) & (unsigned) argp) - argp = (char *) ALIGN(argp, sizeof(int)); + if ((sizeof(void*) - 1) & (size_t) argp) + argp = (char *) ALIGN(argp, sizeof(void*)); z = (*p_arg)->size; - if (z < sizeof(int)) +#ifdef X86_WIN64 + if (z > sizeof(ffi_arg) + || ((*p_arg)->type == FFI_TYPE_STRUCT + && (z != 1 && z != 2 && z != 4 && z != 8)) +#if FFI_TYPE_DOUBLE != FFI_TYPE_LONGDOUBLE + || ((*p_arg)->type == FFI_TYPE_LONGDOUBLE) +#endif + ) + { + z = sizeof(ffi_arg); + *(void **)argp = *p_argv; + } + else if ((*p_arg)->type == FFI_TYPE_FLOAT) { - z = sizeof(int); + memcpy(argp, *p_argv, z); + } + else +#endif + if (z < sizeof(ffi_arg)) + { + z = sizeof(ffi_arg); switch ((*p_arg)->type) { case FFI_TYPE_SINT8: - *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv); + *(ffi_sarg *) argp = (ffi_sarg)*(SINT8 *)(* p_argv); break; case FFI_TYPE_UINT8: - *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv); + *(ffi_arg *) argp = (ffi_arg)*(UINT8 *)(* p_argv); break; case FFI_TYPE_SINT16: - *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv); + *(ffi_sarg *) argp = (ffi_sarg)*(SINT16 *)(* p_argv); break; case FFI_TYPE_UINT16: - *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv); + *(ffi_arg *) argp = (ffi_arg)*(UINT16 *)(* p_argv); break; case FFI_TYPE_SINT32: - *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv); + *(ffi_sarg *) argp = (ffi_sarg)*(SINT32 *)(* p_argv); break; case FFI_TYPE_UINT32: - *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); + *(ffi_arg *) argp = (ffi_arg)*(UINT32 *)(* p_argv); break; case FFI_TYPE_STRUCT: - *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); + *(ffi_arg *) argp = *(ffi_arg *)(* p_argv); break; default: @@ -108,7 +135,11 @@ memcpy(argp, *p_argv, z); } p_argv++; +#ifdef X86_WIN64 + argp += (z + sizeof(void*) - 1) & ~(sizeof(void*) - 1); +#else argp += z; +#endif } return; @@ -124,21 +155,32 @@ #ifdef X86 case FFI_TYPE_STRUCT: #endif -#if defined(X86) || defined(X86_DARWIN) +#if defined(X86) || defined(X86_DARWIN) || defined(X86_WIN64) case FFI_TYPE_UINT8: case FFI_TYPE_UINT16: case FFI_TYPE_SINT8: case FFI_TYPE_SINT16: #endif +#ifdef X86_WIN64 + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: +#endif case FFI_TYPE_SINT64: case FFI_TYPE_FLOAT: case FFI_TYPE_DOUBLE: +#ifndef X86_WIN64 +#if FFI_TYPE_DOUBLE != FFI_TYPE_LONGDOUBLE case FFI_TYPE_LONGDOUBLE: +#endif +#endif cif->flags = (unsigned) cif->rtype->type; break; case FFI_TYPE_UINT64: +#ifdef X86_WIN64 + case FFI_TYPE_POINTER: +#endif cif->flags = FFI_TYPE_SINT64; break; @@ -154,7 +196,11 @@ } else if (cif->rtype->size == 4) { +#ifdef X86_WIN64 + cif->flags = FFI_TYPE_SMALL_STRUCT_4B; +#else cif->flags = FFI_TYPE_INT; /* same as int type */ +#endif } else if (cif->rtype->size == 8) { @@ -163,12 +209,23 @@ else { cif->flags = FFI_TYPE_STRUCT; +#ifdef X86_WIN64 + // allocate space for return value pointer + cif->bytes += ALIGN(sizeof(void*), FFI_SIZEOF_ARG); +#endif } break; #endif default: +#ifdef X86_WIN64 + cif->flags = FFI_TYPE_SINT64; + break; + case FFI_TYPE_INT: + cif->flags = FFI_TYPE_SINT32; +#else cif->flags = FFI_TYPE_INT; +#endif break; } @@ -176,6 +233,22 @@ cif->bytes = (cif->bytes + 15) & ~0xF; #endif +#ifdef X86_WIN64 + { + unsigned int i; + ffi_type **ptr; + + for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) + { + if (((*ptr)->alignment - 1) & cif->bytes) + cif->bytes = ALIGN(cif->bytes, (*ptr)->alignment); + cif->bytes += ALIGN((*ptr)->size, FFI_SIZEOF_ARG); + } + } + // ensure space for storing four registers + cif->bytes += 4 * sizeof(ffi_arg); +#endif + return FFI_OK; } @@ -187,6 +260,11 @@ unsigned, unsigned, unsigned *, void (*fn)(void)); #endif /* X86_WIN32 */ +#ifdef X86_WIN64 +extern int +ffi_call_win64(void (*)(char *, extended_cif *), extended_cif *, + unsigned, unsigned, unsigned *, void (*fn)(void)); +#endif void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { @@ -198,17 +276,52 @@ /* 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)) +#ifdef X86_WIN64 + if (rvalue == NULL + && cif->flags == FFI_TYPE_STRUCT + && cif->rtype->size != 1 && cif->rtype->size != 2 + && cif->rtype->size != 4 && cif->rtype->size != 8) + { + ecif.rvalue = alloca((cif->rtype->size + 0xF) & ~0xF); + } +#else + if (rvalue == NULL + && cif->flags == FFI_TYPE_STRUCT) { ecif.rvalue = alloca(cif->rtype->size); } +#endif else ecif.rvalue = rvalue; switch (cif->abi) { +#ifdef X86_WIN64 + case FFI_WIN64: + { + // Make copies of all struct arguments + // NOTE: not sure if responsibility should be here or in caller + unsigned int i; + for (i=0; i < cif->nargs;i++) { + size_t size = cif->arg_types[i]->size; + if ((cif->arg_types[i]->type == FFI_TYPE_STRUCT + && (size != 1 && size != 2 && size != 4 && size != 8)) +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + || cif->arg_types[i]->type == FFI_TYPE_LONGDOUBLE +#endif + ) + { + void *local = alloca(size); + memcpy(local, avalue[i], size); + avalue[i] = local; + } + } + ffi_call_win64(ffi_prep_args, &ecif, cif->bytes, + cif->flags, ecif.rvalue, fn); + } + break; +#else case FFI_SYSV: ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, fn); @@ -219,6 +332,7 @@ ecif.rvalue, fn); break; #endif /* X86_WIN32 */ +#endif /* X86_WIN64 */ default: FFI_ASSERT(0); break; @@ -240,9 +354,42 @@ void FFI_HIDDEN ffi_closure_STDCALL (ffi_closure *) __attribute__ ((regparm(1))); #endif +#ifdef X86_WIN64 +void FFI_HIDDEN ffi_closure_win64 (ffi_closure *); +#endif /* This function is jumped to by the trampoline */ +#ifdef X86_WIN64 +void * FFI_HIDDEN +ffi_closure_win64_inner (ffi_closure *closure, void *args) { + ffi_cif *cif; + void **arg_area; + void *result; + void *resp = &result; + + cif = closure->cif; + arg_area = (void**) alloca (cif->nargs * sizeof (void*)); + + /* this call will initialize ARG_AREA, such that each + * element in that array points to the corresponding + * value on the stack; and if the function returns + * a structure, it will change RESP to point to the + * structure return address. */ + + ffi_prep_incoming_args_SYSV(args, &resp, arg_area, cif); + + (closure->fun) (cif, resp, arg_area, closure->user_data); + + /* The result is returned in rax. This does the right thing for + result types except for floats; we have to 'mov xmm0, rax' in the + caller to correct this. + TODO: structure sizes of 3 5 6 7 are returned by reference, too!!! + */ + return cif->rtype->size > sizeof(void *) ? resp : *(void **)resp; +} + +#else unsigned int FFI_HIDDEN ffi_closure_SYSV_inner (closure, respp, args) ffi_closure *closure; @@ -259,7 +406,7 @@ /* this call will initialize ARG_AREA, such that each * element in that array points to the corresponding * value on the stack; and if the function returns - * a structure, it will re-set RESP to point to the + * a structure, it will change RESP to point to the * structure return address. */ ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); @@ -268,6 +415,7 @@ return cif->flags; } +#endif /* !X86_WIN64 */ static void ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue, @@ -280,10 +428,20 @@ argp = stack; +#ifdef X86_WIN64 + if (cif->rtype->size > sizeof(ffi_arg) + || (cif->flags == FFI_TYPE_STRUCT + && (cif->rtype->size != 1 && cif->rtype->size != 2 + && cif->rtype->size != 4 && cif->rtype->size != 8))) { + *rvalue = *(void **) argp; + argp += sizeof(void *); + } +#else if ( cif->flags == FFI_TYPE_STRUCT ) { *rvalue = *(void **) argp; - argp += 4; + argp += sizeof(void *); } +#endif p_argv = avalue; @@ -292,23 +450,58 @@ size_t z; /* Align if necessary */ - if ((sizeof(int) - 1) & (unsigned) argp) { - argp = (char *) ALIGN(argp, sizeof(int)); + if ((sizeof(void*) - 1) & (size_t) argp) { + argp = (char *) ALIGN(argp, sizeof(void*)); } +#ifdef X86_WIN64 + if ((*p_arg)->size > sizeof(ffi_arg) + || ((*p_arg)->type == FFI_TYPE_STRUCT + && ((*p_arg)->size != 1 && (*p_arg)->size != 2 + && (*p_arg)->size != 4 && (*p_arg)->size != 8))) + { + z = sizeof(void *); + *p_argv = *(void **)argp; + } + else +#endif + { z = (*p_arg)->size; /* because we're little endian, this is what it turns into. */ *p_argv = (void*) argp; + } p_argv++; +#ifdef X86_WIN64 + argp += (z + sizeof(void*) - 1) & ~(sizeof(void*) - 1); +#else argp += z; +#endif } return; } +#define FFI_INIT_TRAMPOLINE_WIN64(TRAMP,FUN,CTX,MASK) \ +{ unsigned char *__tramp = (unsigned char*)(TRAMP); \ + void* __fun = (void*)(FUN); \ + void* __ctx = (void*)(CTX); \ + *(unsigned char*) &__tramp[0] = 0x41; \ + *(unsigned char*) &__tramp[1] = 0xbb; \ + *(unsigned int*) &__tramp[2] = MASK; /* mov $mask, %r11 */ \ + *(unsigned char*) &__tramp[6] = 0x48; \ + *(unsigned char*) &__tramp[7] = 0xb8; \ + *(void**) &__tramp[8] = __ctx; /* mov __ctx, %rax */ \ + *(unsigned char *) &__tramp[16] = 0x49; \ + *(unsigned char *) &__tramp[17] = 0xba; \ + *(void**) &__tramp[18] = __fun; /* mov __fun, %r10 */ \ + *(unsigned char *) &__tramp[26] = 0x41; \ + *(unsigned char *) &__tramp[27] = 0xff; \ + *(unsigned char *) &__tramp[28] = 0xe2; /* jmp %r10 */ \ + } + /* How to make a trampoline. Derived from gcc/config/i386/i386.c. */ #define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX) \ @@ -345,6 +538,18 @@ void *user_data, void *codeloc) { +#ifdef X86_WIN64 +#define ISFLOAT(IDX) (cif->arg_types[IDX]->type == FFI_TYPE_FLOAT || cif->arg_types[IDX]->type == FFI_TYPE_DOUBLE) +#define FLAG(IDX) (cif->nargs>(IDX)&&ISFLOAT(IDX)?(1<<(IDX)):0) + if (cif->abi == FFI_WIN64) + { + int mask = FLAG(0)|FLAG(1)|FLAG(2)|FLAG(3); + FFI_INIT_TRAMPOLINE_WIN64 (&closure->tramp[0], + &ffi_closure_win64, + codeloc, mask); + /* make sure we can execute here */ + } +#else if (cif->abi == FFI_SYSV) { FFI_INIT_TRAMPOLINE (&closure->tramp[0], @@ -358,7 +563,8 @@ &ffi_closure_STDCALL, (void*)codeloc, cif->bytes); } -#endif +#endif /* X86_WIN32 */ +#endif /* !X86_WIN64 */ else { return FFI_BAD_ABI; @@ -472,4 +678,5 @@ #endif -#endif /* __x86_64__ */ +#endif /* !__x86_64__ || X86_WIN64 */ + Index: src/x86/ffi64.c =================================================================== RCS file: /cvs/libffi/libffi/src/x86/ffi64.c,v retrieving revision 1.4 diff -u -b -w -r1.4 ffi64.c --- src/x86/ffi64.c 26 Feb 2008 17:34:36 -0000 1.4 +++ src/x86/ffi64.c 8 Jun 2009 19:20:24 -0000 @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2002, 2007 Bo Thorsen + ffi64.c - Copyright (c) 2002, 2007 Bo Thorsen Copyright (c) 2008 Red Hat, Inc. x86-64 Foreign Function Interface Index: src/x86/ffitarget.h =================================================================== RCS file: /cvs/libffi/libffi/src/x86/ffitarget.h,v retrieving revision 1.5 diff -u -b -w -r1.5 ffitarget.h --- src/x86/ffitarget.h 15 Feb 2008 01:24:06 -0000 1.5 +++ src/x86/ffitarget.h 8 Jun 2009 19:20:24 -0000 @@ -36,11 +36,26 @@ #define X86 #endif +#ifdef X86_WIN64 +#define FFI_SIZEOF_ARG 8 +#define USE_BUILTIN_FFS 0 // not yet implemented in mingw-64 +#endif + /* ---- Generic type definitions ----------------------------------------- */ #ifndef LIBFFI_ASM +#ifdef X86_WIN64 +#ifdef _MSC_VER +typedef unsigned __int64 ffi_arg; +typedef __int64 ffi_sarg; +#else +typedef unsigned long long ffi_arg; +typedef long long ffi_sarg; +#endif +#else typedef unsigned long ffi_arg; typedef signed long ffi_sarg; +#endif typedef enum ffi_abi { FFI_FIRST_ABI = 0, @@ -53,6 +68,11 @@ FFI_DEFAULT_ABI = FFI_SYSV, #endif +#ifdef X86_WIN64 + FFI_WIN64, + FFI_DEFAULT_ABI = FFI_WIN64, +#else + /* ---- Intel x86 and AMD x86-64 - */ #if !defined(X86_WIN32) && (defined(__i386__) || defined(__x86_64__)) FFI_SYSV, @@ -63,6 +83,7 @@ FFI_DEFAULT_ABI = FFI_UNIX64, #endif #endif +#endif /* X86_WIN64 */ FFI_LAST_ABI = FFI_DEFAULT_ABI + 1 } ffi_abi; @@ -73,6 +94,7 @@ #define FFI_CLOSURES 1 #define FFI_TYPE_SMALL_STRUCT_1B (FFI_TYPE_LAST + 1) #define FFI_TYPE_SMALL_STRUCT_2B (FFI_TYPE_LAST + 2) +#define FFI_TYPE_SMALL_STRUCT_4B (FFI_TYPE_LAST + 3) #if defined (X86_64) || (defined (__x86_64__) && defined (X86_DARWIN)) #define FFI_TRAMPOLINE_SIZE 24 @@ -81,10 +103,18 @@ #ifdef X86_WIN32 #define FFI_TRAMPOLINE_SIZE 13 #else +#ifdef X86_WIN64 +#define FFI_TRAMPOLINE_SIZE 29 +#define FFI_NATIVE_RAW_API 0 +#define FFI_NO_RAW_API 1 +#else #define FFI_TRAMPOLINE_SIZE 10 #endif +#endif +#ifndef X86_WIN64 #define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ #endif +#endif #endif Index: src/x86/win64.S =================================================================== RCS file: src/x86/win64.S diff -N src/x86/win64.S --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/x86/win64.S 8 Jun 2009 19:20:24 -0000 @@ -0,0 +1,462 @@ +#define LIBFFI_ASM +#include +#include + +/* Constants for ffi_call_win64 */ +#define STACK 0 +#define PREP_ARGS_FN 32 +#define ECIF 40 +#define CIF_BYTES 48 +#define CIF_FLAGS 56 +#define RVALUE 64 +#define FN 72 + +/* ffi_call_win64 (void (*prep_args_fn)(char *, extended_cif *), + extended_cif *ecif, unsigned bytes, unsigned flags, + unsigned *rvalue, void (*fn)()); + */ + +#ifdef _MSC_VER +PUBLIC ffi_call_win64 + +EXTRN __chkstk:NEAR +EXTRN ffi_closure_win64_inner:NEAR + +_TEXT SEGMENT + +;;; ffi_closure_win64 will be called with these registers set: +;;; rax points to 'closure' +;;; r11 contains a bit mask that specifies which of the +;;; first four parameters are float or double +;;; +;;; It must move the parameters passed in registers to their stack location, +;;; call ffi_closure_win64_inner for the actual work, then return the result. +;;; +ffi_closure_win64 PROC FRAME + ;; copy register arguments onto stack + test r11, 1 + jne first_is_float + mov QWORD PTR [rsp+8], rcx + jmp second +first_is_float: + movlpd QWORD PTR [rsp+8], xmm0 + +second: + test r11, 2 + jne second_is_float + mov QWORD PTR [rsp+16], rdx + jmp third +second_is_float: + movlpd QWORD PTR [rsp+16], xmm1 + +third: + test r11, 4 + jne third_is_float + mov QWORD PTR [rsp+24], r8 + jmp fourth +third_is_float: + movlpd QWORD PTR [rsp+24], xmm2 + +fourth: + test r11, 8 + jne fourth_is_float + mov QWORD PTR [rsp+32], r9 + jmp done +fourth_is_float: + movlpd QWORD PTR [rsp+32], xmm3 + +done: + .ALLOCSTACK 40 + sub rsp, 40 + .ENDPROLOG + mov rcx, rax ; context is first parameter + mov rdx, rsp ; stack is second parameter + add rdx, 48 ; point to start of arguments + mov rax, ffi_closure_win64_inner + call rax ; call the real closure function + add rsp, 40 + movd xmm0, rax ; If the closure returned a float, + ; ffi_closure_win64_inner wrote it to rax + ret 0 +ffi_closure_win64 ENDP + +ffi_call_win64 PROC FRAME + ;; copy registers onto stack + mov QWORD PTR [rsp+32], r9 + mov QWORD PTR [rsp+24], r8 + mov QWORD PTR [rsp+16], rdx + mov QWORD PTR [rsp+8], rcx + .PUSHREG rbp + push rbp + .ALLOCSTACK 48 + sub rsp, 48 ; 00000030H + .SETFRAME rbp, 32 + lea rbp, QWORD PTR [rsp+32] + .ENDPROLOG + + mov eax, DWORD PTR CIF_BYTES[rbp] + add rax, 15 + and rax, -16 + call __chkstk + sub rsp, rax + lea rax, QWORD PTR [rsp+32] + mov QWORD PTR STACK[rbp], rax + + mov rdx, QWORD PTR ECIF[rbp] + mov rcx, QWORD PTR STACK[rbp] + call QWORD PTR PREP_ARGS_FN[rbp] + + mov rsp, QWORD PTR STACK[rbp] + + movlpd xmm3, QWORD PTR [rsp+24] + movd r9, xmm3 + + movlpd xmm2, QWORD PTR [rsp+16] + movd r8, xmm2 + + movlpd xmm1, QWORD PTR [rsp+8] + movd rdx, xmm1 + + movlpd xmm0, QWORD PTR [rsp] + movd rcx, xmm0 + + call QWORD PTR FN[rbp] +ret_struct4b$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_4B + jne ret_struct2b$ + + mov rcx, QWORD PTR RVALUE[rbp] + mov DWORD PTR [rcx], eax + jmp ret_void$ + +ret_struct2b$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_2B + jne ret_struct1b$ + + mov rcx, QWORD PTR RVALUE[rbp] + mov WORD PTR [rcx], ax + jmp ret_void$ + +ret_struct1b$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_1B + jne ret_uint8$ + + mov rcx, QWORD PTR RVALUE[rbp] + mov BYTE PTR [rcx], al + jmp ret_void$ + +ret_uint8$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT8 + jne ret_sint8$ + + mov rcx, QWORD PTR RVALUE[rbp] + movzx rax, al + mov QWORD PTR [rcx], rax + jmp ret_void$ + +ret_sint8$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT8 + jne ret_uint16$ + + mov rcx, QWORD PTR RVALUE[rbp] + movsx rax, al + mov QWORD PTR [rcx], rax + jmp ret_void$ + +ret_uint16$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT16 + jne ret_sint16$ + + mov rcx, QWORD PTR RVALUE[rbp] + movzx rax, ax + mov QWORD PTR [rcx], rax + jmp SHORT ret_void$ + +ret_sint16$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT16 + jne ret_uint32$ + + mov rcx, QWORD PTR RVALUE[rbp] + movsx rax, ax + mov QWORD PTR [rcx], rax + jmp SHORT ret_void$ + +ret_uint32$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT32 + jne ret_sint32$ + + mov rcx, QWORD PTR RVALUE[rbp] + mov eax, eax + mov QWORD PTR [rcx], rax + jmp SHORT ret_void$ + +ret_sint32$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT32 + jne ret_float$ + + mov rcx, QWORD PTR RVALUE[rbp] + cdqe + mov QWORD PTR [rcx], rax + jmp SHORT ret_void$ + +ret_float$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_FLOAT + jne SHORT ret_double$ + + mov rax, QWORD PTR RVALUE[rbp] + movss DWORD PTR [rax], xmm0 + jmp SHORT ret_void$ + +ret_double$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_DOUBLE + jne SHORT ret_sint64$ + + mov rax, QWORD PTR RVALUE[rbp] + movlpd QWORD PTR [rax], xmm0 + jmp SHORT ret_void$ + +ret_sint64$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT64 + jne ret_void$ + + mov rcx, QWORD PTR RVALUE[rbp] + mov QWORD PTR [rcx], rax + jmp SHORT ret_void$ + +ret_void$: + xor rax, rax + + lea rsp, QWORD PTR [rbp+16] + pop rbp + ret 0 +ffi_call_win64 ENDP +_TEXT ENDS +END +#else +.text + +.extern ___chkstk +.extern _ffi_closure_win64_inner + +# ffi_closure_win64 will be called with these registers set: +# rax points to 'closure' +# r11 contains a bit mask that specifies which of the +# first four parameters are float or double +# +# It must move the parameters passed in registers to their stack location, +# call ffi_closure_win64_inner for the actual work, then return the result. +# + .balign 16 + .globl _ffi_closure_win64 +_ffi_closure_win64: + # copy register arguments onto stack + test $1,%r11 + jne .Lfirst_is_float + mov %rcx, 8(%rsp) + jmp .Lsecond +.Lfirst_is_float: + movlpd %xmm0, 8(%rsp) + +.Lsecond: + test $2, %r11 + jne .Lsecond_is_float + mov %rdx, 16(%rsp) + jmp .Lthird +.Lsecond_is_float: + movlpd %xmm1, 16(%rsp) + +.Lthird: + test $4, %r11 + jne .Lthird_is_float + mov %r8,24(%rsp) + jmp .Lfourth +.Lthird_is_float: + movlpd %xmm2, 24(%rsp) + +.Lfourth: + test $8, %r11 + jne .Lfourth_is_float + mov %r9, 32(%rsp) + jmp .Ldone +.Lfourth_is_float: + movlpd %xmm3, 32(%rsp) + +.Ldone: +#.ALLOCSTACK 40 + sub $40, %rsp +#.ENDPROLOG + mov %rax, %rcx # context is first parameter + mov %rsp, %rdx # stack is second parameter + add $48, %rdx # point to start of arguments + mov $_ffi_closure_win64_inner, %rax + callq *%rax # call the real closure function + add $40, %rsp + movq %rax, %xmm0 # If the closure returned a float, + # ffi_closure_win64_inner wrote it to rax + retq +.ffi_closure_win64_end: + + .balign 16 + .globl _ffi_call_win64 +_ffi_call_win64: + # copy registers onto stack + mov %r9,32(%rsp) + mov %r8,24(%rsp) + mov %rdx,16(%rsp) + mov %rcx,8(%rsp) + #.PUSHREG rbp + push %rbp + #.ALLOCSTACK 48 + sub $48,%rsp + #.SETFRAME rbp, 32 + lea 32(%rsp),%rbp + #.ENDPROLOG + + mov CIF_BYTES(%rbp),%eax + add $15, %rax + and $-16, %rax + callq ___chkstk + cmpq $0x1000, %rax + jb Lch_done +Lch_probe: + subq $0x1000,%rsp + orl $0x0, (%rsp) + subq $0x1000,%rax + cmpq $0x1000,%rax + ja Lch_probe +Lch_done: + subq %rax, %rsp + orl $0x0, (%rsp) + lea 32(%rsp), %rax + mov %rax, STACK(%rbp) + + mov ECIF(%rbp), %rdx + mov STACK(%rbp), %rcx + callq *PREP_ARGS_FN(%rbp) + + mov STACK(%rbp), %rsp + + movlpd 24(%rsp), %xmm3 + movd %xmm3, %r9 + + movlpd 16(%rsp), %xmm2 + movd %xmm2, %r8 + + movlpd 8(%rsp), %xmm1 + movd %xmm1, %rdx + + movlpd (%rsp), %xmm0 + movd %xmm0, %rcx + + callq *FN(%rbp) +.Lret_struct4b: + cmpl $FFI_TYPE_SMALL_STRUCT_4B, CIF_FLAGS(%rbp) + jne .Lret_struct2b + + mov RVALUE(%rbp), %rcx + mov %eax, (%rcx) + jmp .Lret_void + +.Lret_struct2b: + cmpl $FFI_TYPE_SMALL_STRUCT_2B, CIF_FLAGS(%rbp) + jne .Lret_struct1b + + mov RVALUE(%rbp), %rcx + mov %ax, (%rcx) + jmp .Lret_void + +.Lret_struct1b: + cmpl $FFI_TYPE_SMALL_STRUCT_1B, CIF_FLAGS(%rbp) + jne .Lret_uint8 + + mov RVALUE(%rbp), %rcx + mov %al, (%rcx) + jmp .Lret_void + +.Lret_uint8: + cmpl $FFI_TYPE_UINT8, CIF_FLAGS(%rbp) + jne .Lret_sint8 + + mov RVALUE(%rbp), %rcx + movzbq %al, %rax + movq %rax, (%rcx) + jmp .Lret_void + +.Lret_sint8: + cmpl $FFI_TYPE_SINT8, CIF_FLAGS(%rbp) + jne .Lret_uint16 + + mov RVALUE(%rbp), %rcx + movsbq %al, %rax + movq %rax, (%rcx) + jmp .Lret_void + +.Lret_uint16: + cmpl $FFI_TYPE_UINT16, CIF_FLAGS(%rbp) + jne .Lret_sint16 + + mov RVALUE(%rbp), %rcx + movzwq %ax, %rax + movq %rax, (%rcx) + jmp .Lret_void + +.Lret_sint16: + cmpl $FFI_TYPE_SINT16, CIF_FLAGS(%rbp) + jne .Lret_uint32 + + mov RVALUE(%rbp), %rcx + movswq %ax, %rax + movq %rax, (%rcx) + jmp .Lret_void + +.Lret_uint32: + cmpl $FFI_TYPE_UINT32, CIF_FLAGS(%rbp) + jne .Lret_sint32 + + mov RVALUE(%rbp), %rcx + movl %eax, %eax + movq %rax, (%rcx) + jmp .Lret_void + +.Lret_sint32: + cmpl $FFI_TYPE_SINT32, CIF_FLAGS(%rbp) + jne .Lret_float + + mov RVALUE(%rbp), %rcx + cltq + movq %rax, (%rcx) + jmp .Lret_void + +.Lret_float: + cmpl $FFI_TYPE_FLOAT, CIF_FLAGS(%rbp) + jne .Lret_double + + mov RVALUE(%rbp), %rax + movss %xmm0, (%rax) + jmp .Lret_void + +.Lret_double: + cmpl $FFI_TYPE_DOUBLE, CIF_FLAGS(%rbp) + jne .Lret_sint64 + + mov RVALUE(%rbp), %rax + movlpd %xmm0, (%rax) + jmp .Lret_void + +.Lret_sint64: + cmpl $FFI_TYPE_SINT64, CIF_FLAGS(%rbp) + jne .Lret_void + + mov RVALUE(%rbp), %rcx + mov %rax, (%rcx) + jmp .Lret_void + +.Lret_void: + xor %rax, %rax + + lea 16(%rbp), %rsp + pop %rbp + retq +.ffi_call_win64_end: +#endif /* !_MSC_VER */ +