Skip to content

Commit 1ed9ebd

Browse files
committed
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.
1 parent f12dc90 commit 1ed9ebd

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

ext/ffi/ffi.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,38 @@ static zend_always_inline int zend_ffi_zval_to_cdata(void *ptr, zend_ffi_type *t
765765
}
766766
/* }}} */
767767

768+
#if defined(ZEND_WIN32) && (defined(HAVE_FFI_FASTCALL) || defined(HAVE_FFI_STDCALL))
769+
static size_t zend_ffi_arg_size(zend_ffi_type *type) /* {{{ */
770+
{
771+
zend_ffi_type *arg_type;
772+
size_t arg_size = 0;
773+
774+
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
775+
arg_size += ZEND_FFI_TYPE(arg_type)->size;
776+
} ZEND_HASH_FOREACH_END();
777+
return arg_size;
778+
}
779+
/* }}} */
780+
#endif
781+
782+
static zend_always_inline zend_string *zend_ffi_mangled_func_name(zend_string *name, zend_ffi_type *type) /* {{{ */
783+
{
784+
#ifdef ZEND_WIN32
785+
switch (type->func.abi) {
786+
# ifdef HAVE_FFI_FASTCALL
787+
case FFI_FASTCALL:
788+
return strpprintf(0, "@%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
789+
# endif
790+
# ifdef HAVE_FFI_STDCALL
791+
case FFI_STDCALL:
792+
return strpprintf(0, "_%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
793+
# endif
794+
}
795+
#endif
796+
return zend_string_copy(name);
797+
}
798+
/* }}} */
799+
768800
#if FFI_CLOSURES
769801
typedef struct _zend_ffi_callback_data {
770802
zend_fcall_info_cache fcc;
@@ -2842,7 +2874,10 @@ ZEND_METHOD(FFI, cdef) /* {{{ */
28422874
}
28432875
sym->addr = addr;
28442876
} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
2845-
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
2877+
zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type));
2878+
2879+
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name));
2880+
zend_string_release(mangled_name);
28462881
if (!addr) {
28472882
zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
28482883
}
@@ -3172,7 +3207,10 @@ ZEND_METHOD(FFI, load) /* {{{ */
31723207
}
31733208
sym->addr = addr;
31743209
} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
3175-
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
3210+
zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type));
3211+
3212+
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name));
3213+
zend_string_release(mangled_name);
31763214
if (!addr) {
31773215
if (preload) {
31783216
zend_error(E_WARNING, "failed pre-loading '%s', cannot resolve C function '%s'", filename, ZSTR_VAL(name));

ext/ffi/tests/callconv.phpt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Different calling conventions
3+
--SKIPIF--
4+
<?php
5+
require_once('skipif.inc');
6+
if (substr(PHP_OS, 0, 3) != 'WIN') die('skip for Windows only');
7+
if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platforms only");
8+
?>
9+
--FILE--
10+
<?php
11+
$header = <<<HEADER
12+
void __cdecl cdecl_func(int arg1, double arg2);
13+
void __stdcall stdcall_func(int arg1, double arg2);
14+
void __fastcall fastcall_func(int arg1, double arg2);
15+
HEADER;
16+
$headername = __DIR__ . '/callconv.h';
17+
$dllname = __DIR__ . "/callconv_x86.dll";
18+
19+
$ffi1 = FFI::cdef($header, $dllname);
20+
$ffi1->cdecl_func(1, 2.3);
21+
$ffi1->stdcall_func(4, 5.6);
22+
$ffi1->fastcall_func(7, 8.9);
23+
24+
file_put_contents($headername, "#define FFI_LIB \"$dllname\"\n$header");
25+
26+
$ffi2 = FFI::load($headername);
27+
$ffi2->cdecl_func(2, 3.4);
28+
$ffi2->stdcall_func(5, 6.7);
29+
$ffi2->fastcall_func(8, 9.1);
30+
?>
31+
--EXPECT--
32+
cdecl: 1, 2.300000
33+
stdcall: 4, 5.600000
34+
fastcall: 7, 8.900000
35+
cdecl: 2, 3.400000
36+
stdcall: 5, 6.700000
37+
fastcall: 8, 9.100000
38+
--CLEAN--
39+
<?php
40+
unlink(__DIR__ . '/callconv.h');
41+
?>

ext/ffi/tests/callconv_x86.dll

8.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)