diff --git a/libffi/ChangeLog.libffi b/libffi/ChangeLog.libffi index 187daa13..19817083 100644 --- a/libffi/ChangeLog.libffi +++ b/libffi/ChangeLog.libffi @@ -1,3 +1,12 @@ +2008-01-31 Timothy Wall + + * testsuite/libffi.call/closure_stdcall.c: Add test for stdcall + closures. + * src/x86/ffitarget.h: Increase size of trampoline for stdcall + closures. + * src/x86/win32.S: Add assembly for stdcall closure. + * src/x86/ffi.c: Initialize stdcall closure trampoline. + 2008-01-30 H.J. Lu PR libffi/34612 diff --git a/libffi/src/x86/ffi.c b/libffi/src/x86/ffi.c index a4d4798b..eef8382c 100644 --- a/libffi/src/x86/ffi.c +++ b/libffi/src/x86/ffi.c @@ -235,6 +235,10 @@ unsigned int FFI_HIDDEN ffi_closure_SYSV_inner (ffi_closure *, void **, void *) __attribute__ ((regparm(1))); void FFI_HIDDEN ffi_closure_raw_SYSV (ffi_raw_closure *) __attribute__ ((regparm(1))); +#ifdef X86_WIN32 +void FFI_HIDDEN ffi_closure_STDCALL (ffi_closure *) + __attribute__ ((regparm(1))); +#endif /* This function is jumped to by the trampoline */ @@ -317,6 +321,19 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue, *(unsigned int*) &__tramp[6] = __dis; /* jmp __fun */ \ }) +#define FFI_INIT_TRAMPOLINE_STDCALL(TRAMP,FUN,CTX,SIZE) \ +({ unsigned char *__tramp = (unsigned char*)(TRAMP); \ + unsigned int __fun = (unsigned int)(FUN); \ + unsigned int __ctx = (unsigned int)(CTX); \ + unsigned int __dis = __fun - ((unsigned int) __tramp + FFI_TRAMPOLINE_SIZE); \ + unsigned short __size = (unsigned short)(SIZE); \ + *(unsigned char*) &__tramp[0] = 0xb8; \ + *(unsigned int*) &__tramp[1] = __ctx; /* movl __ctx, %eax */ \ + *(unsigned char *) &__tramp[5] = 0xe8; \ + *(unsigned int*) &__tramp[6] = __dis; /* call __fun */ \ + *(unsigned char *) &__tramp[10] = 0xc2; \ + *(unsigned short*) &__tramp[11] = __size; /* ret __size */ \ + }) /* the cif must already be prep'ed */ @@ -327,11 +344,24 @@ ffi_prep_closure_loc (ffi_closure* closure, void *user_data, void *codeloc) { - FFI_ASSERT (cif->abi == FFI_SYSV); - - FFI_INIT_TRAMPOLINE (&closure->tramp[0], \ - &ffi_closure_SYSV, \ - codeloc); + if (cif->abi == FFI_SYSV) + { + FFI_INIT_TRAMPOLINE (&closure->tramp[0], + &ffi_closure_SYSV, + (void*)closure); + } +#ifdef X86_WIN32 + else if (cif->abi == FFI_STDCALL) + { + FFI_INIT_TRAMPOLINE_STDCALL (&closure->tramp[0], + &ffi_closure_STDCALL, + (void*)closure, cif->bytes); + } +#endif + else + { + return FFI_BAD_ABI; + } closure->cif = cif; closure->user_data = user_data; @@ -353,7 +383,9 @@ ffi_prep_raw_closure_loc (ffi_raw_closure* closure, { int i; - FFI_ASSERT (cif->abi == FFI_SYSV); + if (cif->abi != FFI_SYSV) { + return FFI_BAD_ABI; + } // we currently don't support certain kinds of arguments for raw // closures. This should be implemented by a separate assembly language diff --git a/libffi/src/x86/ffitarget.h b/libffi/src/x86/ffitarget.h index 3a1427c3..fecdc24a 100644 --- a/libffi/src/x86/ffitarget.h +++ b/libffi/src/x86/ffitarget.h @@ -77,7 +77,11 @@ typedef enum ffi_abi { #define FFI_TRAMPOLINE_SIZE 24 #define FFI_NATIVE_RAW_API 0 #else +#ifdef X86_WIN32 +#define FFI_TRAMPOLINE_SIZE 13 +#else #define FFI_TRAMPOLINE_SIZE 10 +#endif #define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ #endif diff --git a/libffi/src/x86/win32.S b/libffi/src/x86/win32.S index 496953e4..083039ad 100644 --- a/libffi/src/x86/win32.S +++ b/libffi/src/x86/win32.S @@ -258,6 +258,22 @@ sc_epilogue: .ffi_call_STDCALL_end: + .globl _ffi_closure_STDCALL +_ffi_closure_STDCALL: + pushl %ebp + movl %esp, %ebp + subl $40, %esp + leal -24(%ebp), %edx + movl %edx, -12(%ebp) /* resp */ + leal 12(%ebp), %edx /* account for stub return address on stack */ + movl %edx, 4(%esp) /* args */ + leal -12(%ebp), %edx + movl %edx, (%esp) /* &resp */ + call _ffi_closure_SYSV_inner + movl -12(%ebp), %ecx + jmp .Lcls_return_result +.ffi_closure_STDCALL_end: + .globl _ffi_closure_SYSV _ffi_closure_SYSV: pushl %ebp @@ -271,6 +287,7 @@ _ffi_closure_SYSV: movl %edx, (%esp) /* &resp */ call _ffi_closure_SYSV_inner movl -12(%ebp), %ecx +.Lcls_return_result: cmpl $FFI_TYPE_INT, %eax je .Lcls_retint cmpl $FFI_TYPE_FLOAT, %eax diff --git a/libffi/testsuite/libffi.call/closure_stdcall.c b/libffi/testsuite/libffi.call/closure_stdcall.c new file mode 100644 index 00000000..e80b10b2 --- /dev/null +++ b/libffi/testsuite/libffi.call/closure_stdcall.c @@ -0,0 +1,72 @@ +/* Area: closure_call (stdcall convention) + Purpose: Check handling when caller expects stdcall callee + Limitations: none. + PR: none. + Originator: */ + +/* { dg-do run { target i?86-*-cygwin* i?86-*-mingw* } } */ +#include "ffitest.h" + +static void +closure_test_stdcall(ffi_cif* cif __UNUSED__, void* resp, void** args, + void* userdata) +{ + *(ffi_arg*)resp = + (int)*(int *)args[0] + (int)(*(int *)args[1]) + + (int)(*(int *)args[2]) + (int)(*(int *)args[3]) + + (int)(long)userdata; + + printf("%d %d %d %d: %d\n", + (int)*(int *)args[0], (int)(*(int *)args[1]), + (int)(*(int *)args[2]), (int)(*(int *)args[3]), + (int)*(ffi_arg *)resp); + +} + +typedef int (__stdcall *closure_test_type0)(int, int, int, int); + +int main (void) +{ + ffi_cif cif; +#ifndef USING_MMAP + static ffi_closure cl; +#endif + ffi_closure *pcl; + ffi_type * cl_arg_types[17]; + int res; + void* sp_pre; + void* sp_post; + char buf[1024]; + +#ifdef USING_MMAP + pcl = allocate_mmap (sizeof(ffi_closure)); +#else + pcl = &cl; +#endif + + cl_arg_types[0] = &ffi_type_uint; + cl_arg_types[1] = &ffi_type_uint; + cl_arg_types[2] = &ffi_type_uint; + cl_arg_types[3] = &ffi_type_uint; + cl_arg_types[4] = NULL; + + /* Initialize the cif */ + CHECK(ffi_prep_cif(&cif, FFI_STDCALL, 4, + &ffi_type_sint, cl_arg_types) == FFI_OK); + + CHECK(ffi_prep_closure(pcl, &cif, closure_test_stdcall, + (void *) 3 /* userdata */) == FFI_OK); + + asm volatile (" movl %%esp,%0" : "=g" (sp_pre)); + res = (*(closure_test_type0)pcl)(0, 1, 2, 3); + asm volatile (" movl %%esp,%0" : "=g" (sp_post)); + /* { dg-output "0 1 2 3: 9" } */ + + printf("res: %d\n",res); + /* { dg-output "\nres: 9" } */ + + sprintf(buf, "mismatch: pre=%p vs post=%p", sp_pre, sp_post); + printf("stack pointer %s\n", (sp_pre == sp_post ? "match" : buf)); + /* { dg-output "\nstack pointer match" } */ + exit(0); +}