diff options
| author | Christoph M. Becker <cmbecker69@gmx.de> | 2019-07-26 14:37:34 +0200 |
|---|---|---|
| committer | Christoph M. Becker <cmbecker69@gmx.de> | 2019-08-04 12:14:11 +0200 |
| commit | 1ed9ebdea558a7ae265dcc460cedd2cbbaa48efc (patch) | |
| tree | 7043e02bc4299160d96a093554c17425c0a9a719 | |
| parent | f12dc90e5e965c9b6ea240a127e303c2c9d7c531 (diff) | |
| download | php-git-1ed9ebdea558a7ae265dcc460cedd2cbbaa48efc.tar.gz | |
Support calling convention specific function name mangling
On Windows certain calling conventions cause C function names to be
mangled, so to import them we have to use the properly mangled names.
| -rw-r--r-- | ext/ffi/ffi.c | 42 | ||||
| -rw-r--r-- | ext/ffi/tests/callconv.phpt | 41 | ||||
| -rw-r--r-- | ext/ffi/tests/callconv_x86.dll | bin | 0 -> 8704 bytes |
3 files changed, 81 insertions, 2 deletions
diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index c665ad151e..552d168fd6 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -765,6 +765,38 @@ again: } /* }}} */ +#if defined(ZEND_WIN32) && (defined(HAVE_FFI_FASTCALL) || defined(HAVE_FFI_STDCALL)) +static size_t zend_ffi_arg_size(zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_type *arg_type; + size_t arg_size = 0; + + ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) { + arg_size += ZEND_FFI_TYPE(arg_type)->size; + } ZEND_HASH_FOREACH_END(); + return arg_size; +} +/* }}} */ +#endif + +static zend_always_inline zend_string *zend_ffi_mangled_func_name(zend_string *name, zend_ffi_type *type) /* {{{ */ +{ +#ifdef ZEND_WIN32 + switch (type->func.abi) { +# ifdef HAVE_FFI_FASTCALL + case FFI_FASTCALL: + return strpprintf(0, "@%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type)); +# endif +# ifdef HAVE_FFI_STDCALL + case FFI_STDCALL: + return strpprintf(0, "_%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type)); +# endif + } +#endif + return zend_string_copy(name); +} +/* }}} */ + #if FFI_CLOSURES typedef struct _zend_ffi_callback_data { zend_fcall_info_cache fcc; @@ -2842,7 +2874,10 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ } sym->addr = addr; } else if (sym->kind == ZEND_FFI_SYM_FUNC) { - addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name)); + zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type)); + + addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name)); + zend_string_release(mangled_name); if (!addr) { zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name)); } @@ -3172,7 +3207,10 @@ ZEND_METHOD(FFI, load) /* {{{ */ } sym->addr = addr; } else if (sym->kind == ZEND_FFI_SYM_FUNC) { - addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name)); + zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type)); + + addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name)); + zend_string_release(mangled_name); if (!addr) { if (preload) { zend_error(E_WARNING, "failed pre-loading '%s', cannot resolve C function '%s'", filename, ZSTR_VAL(name)); diff --git a/ext/ffi/tests/callconv.phpt b/ext/ffi/tests/callconv.phpt new file mode 100644 index 0000000000..aa481de224 --- /dev/null +++ b/ext/ffi/tests/callconv.phpt @@ -0,0 +1,41 @@ +--TEST-- +Different calling conventions +--SKIPIF-- +<?php +require_once('skipif.inc'); +if (substr(PHP_OS, 0, 3) != 'WIN') die('skip for Windows only'); +if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platforms only"); +?> +--FILE-- +<?php +$header = <<<HEADER +void __cdecl cdecl_func(int arg1, double arg2); +void __stdcall stdcall_func(int arg1, double arg2); +void __fastcall fastcall_func(int arg1, double arg2); +HEADER; +$headername = __DIR__ . '/callconv.h'; +$dllname = __DIR__ . "/callconv_x86.dll"; + +$ffi1 = FFI::cdef($header, $dllname); +$ffi1->cdecl_func(1, 2.3); +$ffi1->stdcall_func(4, 5.6); +$ffi1->fastcall_func(7, 8.9); + +file_put_contents($headername, "#define FFI_LIB \"$dllname\"\n$header"); + +$ffi2 = FFI::load($headername); +$ffi2->cdecl_func(2, 3.4); +$ffi2->stdcall_func(5, 6.7); +$ffi2->fastcall_func(8, 9.1); +?> +--EXPECT-- +cdecl: 1, 2.300000 +stdcall: 4, 5.600000 +fastcall: 7, 8.900000 +cdecl: 2, 3.400000 +stdcall: 5, 6.700000 +fastcall: 8, 9.100000 +--CLEAN-- +<?php +unlink(__DIR__ . '/callconv.h'); +?> diff --git a/ext/ffi/tests/callconv_x86.dll b/ext/ffi/tests/callconv_x86.dll Binary files differnew file mode 100644 index 0000000000..f4818d1091 --- /dev/null +++ b/ext/ffi/tests/callconv_x86.dll |
