mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
FFI: support symbol lookup without specifying lib on Windows
This works similar to `dlsym(RTLD_DEFAULT, …)` with the caveat that symbols on Windows may not be unique, and are usually qualified by the module they are exported from. That means that wrong symbols may be fetched, potentially causing serious issues; therefore this usage is not recommended for production purposes, but is a nice simplification for quick experiments and the ext/ffi test suite. Closes GH-16351.
This commit is contained in:
parent
9504fcfc0f
commit
db991bc0f1
17 changed files with 57 additions and 135 deletions
|
@ -116,6 +116,11 @@ PHP 8.5 UPGRADE NOTES
|
||||||
PHP_RELEASE_VERSION are now always numbers. Previously, they have been
|
PHP_RELEASE_VERSION are now always numbers. Previously, they have been
|
||||||
strings for buildconf builds.
|
strings for buildconf builds.
|
||||||
|
|
||||||
|
* FFI:
|
||||||
|
. It is no longer necessary to specify the library when using FFI::cdef()
|
||||||
|
and FFI::load(). However, this convenience feature should not be used in
|
||||||
|
production.
|
||||||
|
|
||||||
========================================
|
========================================
|
||||||
13. Other Changes
|
13. Other Changes
|
||||||
========================================
|
========================================
|
||||||
|
|
|
@ -2974,6 +2974,40 @@ static zend_always_inline bool zend_ffi_validate_api_restriction(zend_execute_da
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef PHP_WIN32
|
||||||
|
# ifndef DWORD_MAX
|
||||||
|
# define DWORD_MAX ULONG_MAX
|
||||||
|
# endif
|
||||||
|
# define NUM_MODULES 1024
|
||||||
|
/* A rough approximation of dlysm(RTLD_DEFAULT) */
|
||||||
|
static void *dlsym_loaded(char *symbol)
|
||||||
|
{
|
||||||
|
HMODULE modules_static[NUM_MODULES], *modules = modules_static;
|
||||||
|
DWORD num = NUM_MODULES, i;
|
||||||
|
void * addr;
|
||||||
|
if (!EnumProcessModules(GetCurrentProcess(), modules, num * sizeof(HMODULE), &num)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (num >= NUM_MODULES && num <= DWORD_MAX / sizeof(HMODULE)) {
|
||||||
|
modules = emalloc(num *sizeof(HMODULE));
|
||||||
|
if (!EnumProcessModules(GetCurrentProcess(), modules, num * sizeof(HMODULE), &num)) {
|
||||||
|
efree(modules);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
addr = GetProcAddress(modules[i], symbol);
|
||||||
|
if (addr != NULL) break;
|
||||||
|
}
|
||||||
|
if (modules != modules_static) {
|
||||||
|
efree(modules);
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
# undef DL_FETCH_SYMBOL
|
||||||
|
# define DL_FETCH_SYMBOL(h, s) (h == NULL ? dlsym_loaded(s) : GetProcAddress(h, s))
|
||||||
|
#endif
|
||||||
|
|
||||||
ZEND_METHOD(FFI, cdef) /* {{{ */
|
ZEND_METHOD(FFI, cdef) /* {{{ */
|
||||||
{
|
{
|
||||||
zend_string *code = NULL;
|
zend_string *code = NULL;
|
||||||
|
|
|
@ -3,10 +3,9 @@ FFI 100: PHP symbols
|
||||||
--EXTENSIONS--
|
--EXTENSIONS--
|
||||||
ffi
|
ffi
|
||||||
--SKIPIF--
|
--SKIPIF--
|
||||||
<?php require_once('utils.inc'); ?>
|
|
||||||
<?php
|
<?php
|
||||||
try {
|
try {
|
||||||
ffi_cdef("extern void *zend_printf;", ffi_get_php_dll_name());
|
FFI::cdef("extern void *zend_printf;");
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
die('skip PHP symbols not available');
|
die('skip PHP symbols not available');
|
||||||
}
|
}
|
||||||
|
@ -17,7 +16,7 @@ ffi.enable=1
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
require_once('utils.inc');
|
||||||
$fastcall = ffi_get_fastcall_specifier();
|
$fastcall = ffi_get_fastcall_specifier();
|
||||||
$zend = ffi_cdef("
|
$zend = FFI::cdef("
|
||||||
const char *get_zend_version(void);
|
const char *get_zend_version(void);
|
||||||
//char *get_zend_version(void);
|
//char *get_zend_version(void);
|
||||||
extern size_t (*zend_printf)(const char *format, ...);
|
extern size_t (*zend_printf)(const char *format, ...);
|
||||||
|
@ -26,7 +25,7 @@ $zend = ffi_cdef("
|
||||||
|
|
||||||
void $fastcall zend_str_tolower(char *str, size_t length);
|
void $fastcall zend_str_tolower(char *str, size_t length);
|
||||||
|
|
||||||
", ffi_get_php_dll_name());
|
");
|
||||||
var_dump(trim(explode("\n",$zend->get_zend_version())[0]));
|
var_dump(trim(explode("\n",$zend->get_zend_version())[0]));
|
||||||
//var_dump(trim(FFI::string($zend->get_zend_version())));
|
//var_dump(trim(FFI::string($zend->get_zend_version())));
|
||||||
var_dump($zend->zend_printf);
|
var_dump($zend->zend_printf);
|
||||||
|
|
|
@ -3,10 +3,9 @@ FFI 101: PHP symbols (function address)
|
||||||
--EXTENSIONS--
|
--EXTENSIONS--
|
||||||
ffi
|
ffi
|
||||||
--SKIPIF--
|
--SKIPIF--
|
||||||
<?php require_once('utils.inc'); ?>
|
|
||||||
<?php
|
<?php
|
||||||
try {
|
try {
|
||||||
ffi_cdef("extern void *zend_printf;", ffi_get_php_dll_name());
|
FFI::cdef("extern void *zend_printf;");
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
die('skip PHP symbols not available');
|
die('skip PHP symbols not available');
|
||||||
}
|
}
|
||||||
|
@ -17,7 +16,7 @@ ffi.enable=1
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
require_once('utils.inc');
|
||||||
$fastcall = ffi_get_fastcall_specifier();
|
$fastcall = ffi_get_fastcall_specifier();
|
||||||
$zend = ffi_cdef("
|
$zend = FFI::cdef("
|
||||||
const char *get_zend_version(void);
|
const char *get_zend_version(void);
|
||||||
//char *get_zend_version(void);
|
//char *get_zend_version(void);
|
||||||
extern size_t (*zend_printf)(const char *format, ...);
|
extern size_t (*zend_printf)(const char *format, ...);
|
||||||
|
@ -26,7 +25,7 @@ $zend = ffi_cdef("
|
||||||
|
|
||||||
void $fastcall zend_str_tolower(char *str, size_t length);
|
void $fastcall zend_str_tolower(char *str, size_t length);
|
||||||
|
|
||||||
", ffi_get_php_dll_name());
|
");
|
||||||
$f = $zend->get_zend_version;
|
$f = $zend->get_zend_version;
|
||||||
var_dump(trim(explode("\n",$f())[0]));
|
var_dump(trim(explode("\n",$f())[0]));
|
||||||
//var_dump(trim(FFI::string($zend->get_zend_version())));
|
//var_dump(trim(FFI::string($zend->get_zend_version())));
|
||||||
|
|
|
@ -6,7 +6,7 @@ ffi
|
||||||
<?php require_once('utils.inc'); ?>
|
<?php require_once('utils.inc'); ?>
|
||||||
<?php
|
<?php
|
||||||
try {
|
try {
|
||||||
FFI::cdef("void* zend_write;", ffi_get_php_dll_name());
|
FFI::cdef("void* zend_write;");
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
die('skip PHP symbols not available');
|
die('skip PHP symbols not available');
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ require_once('utils.inc');
|
||||||
$zend = FFI::cdef("
|
$zend = FFI::cdef("
|
||||||
typedef size_t (*zend_write_func_t)(const char *str, size_t str_length);
|
typedef size_t (*zend_write_func_t)(const char *str, size_t str_length);
|
||||||
extern zend_write_func_t zend_write;
|
extern zend_write_func_t zend_write;
|
||||||
", ffi_get_php_dll_name());
|
");
|
||||||
|
|
||||||
echo "Hello World!\n";
|
echo "Hello World!\n";
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
#define FFI_SCOPE "TEST_300_WIN32"
|
|
||||||
#define FFI_LIB "PHP_DLL_NAME"
|
|
||||||
|
|
||||||
size_t php_printf(const char *format, ...);
|
|
|
@ -1,29 +0,0 @@
|
||||||
--TEST--
|
|
||||||
FFI 301: FFI loading on Windows
|
|
||||||
--EXTENSIONS--
|
|
||||||
ffi
|
|
||||||
--SKIPIF--
|
|
||||||
<?php if (substr(PHP_OS, 0, 3) != 'WIN') die('skip for Windows only'); ?>
|
|
||||||
--INI--
|
|
||||||
ffi.enable=1
|
|
||||||
--FILE--
|
|
||||||
<?php
|
|
||||||
require_once('utils.inc');
|
|
||||||
$fn = __DIR__ . "/300-win32.h";
|
|
||||||
$cont = str_replace(
|
|
||||||
"PHP_DLL_NAME",
|
|
||||||
ffi_get_php_dll_name(),
|
|
||||||
file_get_contents("$fn.in")
|
|
||||||
);
|
|
||||||
file_put_contents($fn, $cont);
|
|
||||||
|
|
||||||
$ffi = FFI::load($fn);
|
|
||||||
$ffi->php_printf("Hello World from %s!\n", "PHP");
|
|
||||||
?>
|
|
||||||
--CLEAN--
|
|
||||||
<?php
|
|
||||||
$fn = __DIR__ . "/300-win32.h";
|
|
||||||
unlink($fn);
|
|
||||||
?>
|
|
||||||
--EXPECT--
|
|
||||||
Hello World from PHP!
|
|
|
@ -2,8 +2,6 @@
|
||||||
FFI 301: FFI loading
|
FFI 301: FFI loading
|
||||||
--EXTENSIONS--
|
--EXTENSIONS--
|
||||||
ffi
|
ffi
|
||||||
--SKIPIF--
|
|
||||||
<?php if (substr(PHP_OS, 0, 3) == 'WIN') die('skip not for Windows'); ?>
|
|
||||||
--INI--
|
--INI--
|
||||||
ffi.enable=1
|
ffi.enable=1
|
||||||
--FILE--
|
--FILE--
|
||||||
|
|
|
@ -4,9 +4,8 @@ Bug #77632 (FFI function pointers with variadics)
|
||||||
ffi
|
ffi
|
||||||
--SKIPIF--
|
--SKIPIF--
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
|
||||||
try {
|
try {
|
||||||
FFI::cdef("extern void *zend_printf;", ffi_get_php_dll_name());
|
FFI::cdef("extern void *zend_printf;");
|
||||||
} catch (Throwable $_) {
|
} catch (Throwable $_) {
|
||||||
die('skip PHP symbols not available');
|
die('skip PHP symbols not available');
|
||||||
}
|
}
|
||||||
|
@ -15,8 +14,7 @@ try {
|
||||||
ffi.enable=1
|
ffi.enable=1
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
$libc = FFI::cdef("extern size_t (*zend_printf)(const char *format, ...);");
|
||||||
$libc = FFI::cdef("extern size_t (*zend_printf)(const char *format, ...);", ffi_get_php_dll_name());
|
|
||||||
$args = ["test from zend_printf\n"];
|
$args = ["test from zend_printf\n"];
|
||||||
($libc->zend_printf)(...$args);
|
($libc->zend_printf)(...$args);
|
||||||
$args2 = ["Hello, %s from zend_printf\n", "world"];
|
$args2 = ["Hello, %s from zend_printf\n", "world"];
|
||||||
|
|
|
@ -6,9 +6,8 @@ ffi
|
||||||
ffi.enable=1
|
ffi.enable=1
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
|
||||||
$def = 'char * __cdecl get_zend_version(void);';
|
$def = 'char * __cdecl get_zend_version(void);';
|
||||||
$ffi = ffi_cdef($def, ffi_get_php_dll_name());
|
$ffi = FFI::cdef($def);
|
||||||
echo substr(FFI::string($ffi->get_zend_version()), 0, 4) . "\n";
|
echo substr(FFI::string($ffi->get_zend_version()), 0, 4) . "\n";
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
|
|
@ -5,7 +5,6 @@ ffi
|
||||||
zend_test
|
zend_test
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
|
||||||
$header = <<<HEADER
|
$header = <<<HEADER
|
||||||
struct bug79096 {
|
struct bug79096 {
|
||||||
uint64_t a;
|
uint64_t a;
|
||||||
|
@ -15,16 +14,7 @@ struct bug79096 {
|
||||||
struct bug79096 bug79096(void);
|
struct bug79096 bug79096(void);
|
||||||
HEADER;
|
HEADER;
|
||||||
|
|
||||||
if (PHP_OS_FAMILY !== 'Windows') {
|
|
||||||
$ffi = FFI::cdef($header);
|
$ffi = FFI::cdef($header);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$ffi = FFI::cdef($header, 'php_zend_test.dll');
|
|
||||||
} catch (FFI\Exception $ex) {
|
|
||||||
$ffi = FFI::cdef($header, ffi_get_php_dll_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$struct = $ffi->bug79096();
|
$struct = $ffi->bug79096();
|
||||||
var_dump($struct);
|
var_dump($struct);
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -5,22 +5,12 @@ ffi
|
||||||
zend_test
|
zend_test
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/utils.inc';
|
|
||||||
$header = <<<HEADER
|
$header = <<<HEADER
|
||||||
extern int *(*bug79177_cb)(void);
|
extern int *(*bug79177_cb)(void);
|
||||||
void bug79177(void);
|
void bug79177(void);
|
||||||
HEADER;
|
HEADER;
|
||||||
|
|
||||||
if (PHP_OS_FAMILY !== 'Windows') {
|
|
||||||
$ffi = FFI::cdef($header);
|
$ffi = FFI::cdef($header);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$ffi = FFI::cdef($header, 'php_zend_test.dll');
|
|
||||||
} catch (FFI\Exception $ex) {
|
|
||||||
$ffi = FFI::cdef($header, ffi_get_php_dll_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$ffi->bug79177_cb = function() {
|
$ffi->bug79177_cb = function() {
|
||||||
throw new \RuntimeException('Not allowed');
|
throw new \RuntimeException('Not allowed');
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,22 +5,11 @@ ffi
|
||||||
zend_test
|
zend_test
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
|
||||||
|
|
||||||
$header = <<<HEADER
|
$header = <<<HEADER
|
||||||
void bug79532(off_t *array, size_t elems);
|
void bug79532(off_t *array, size_t elems);
|
||||||
HEADER;
|
HEADER;
|
||||||
|
|
||||||
if (PHP_OS_FAMILY !== 'Windows') {
|
|
||||||
$ffi = FFI::cdef($header);
|
$ffi = FFI::cdef($header);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$ffi = FFI::cdef($header, 'php_zend_test.dll');
|
|
||||||
} catch (FFI\Exception $ex) {
|
|
||||||
$ffi = FFI::cdef($header, ffi_get_php_dll_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$array = FFI::cdef()->new("off_t[3]");
|
$array = FFI::cdef()->new("off_t[3]");
|
||||||
$ffi->bug79532($array, 3);
|
$ffi->bug79532($array, 3);
|
||||||
var_dump($array);
|
var_dump($array);
|
||||||
|
|
|
@ -9,7 +9,6 @@ if (PHP_OS_FAMILY == 'Windows' && ((1 << 31) > 0)) die('xfail libffi doesn\'t pr
|
||||||
?>
|
?>
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once('utils.inc');
|
|
||||||
$header = <<<HEADER
|
$header = <<<HEADER
|
||||||
typedef struct bug80847_01 {
|
typedef struct bug80847_01 {
|
||||||
uint64_t b;
|
uint64_t b;
|
||||||
|
@ -23,15 +22,7 @@ $header = <<<HEADER
|
||||||
bug80847_02 ffi_bug80847(bug80847_02 s);
|
bug80847_02 ffi_bug80847(bug80847_02 s);
|
||||||
HEADER;
|
HEADER;
|
||||||
|
|
||||||
if (PHP_OS_FAMILY !== 'Windows') {
|
|
||||||
$ffi = FFI::cdef($header);
|
$ffi = FFI::cdef($header);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$ffi = FFI::cdef($header, 'php_zend_test.dll');
|
|
||||||
} catch (FFI\Exception $ex) {
|
|
||||||
$ffi = FFI::cdef($header, ffi_get_php_dll_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$x = $ffi->new('bug80847_02');
|
$x = $ffi->new('bug80847_02');
|
||||||
$x->a->b = 42;
|
$x->a->b = 42;
|
||||||
$x->a->c = 42.5;
|
$x->a->c = 42.5;
|
||||||
|
|
|
@ -6,7 +6,6 @@ zend_test
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once 'utils.inc';
|
|
||||||
$h = <<<'EOD'
|
$h = <<<'EOD'
|
||||||
void (*bug_gh9090_void_none_ptr)();
|
void (*bug_gh9090_void_none_ptr)();
|
||||||
void (*bug_gh9090_void_int_char_ptr)(int, char *);
|
void (*bug_gh9090_void_int_char_ptr)(int, char *);
|
||||||
|
@ -19,15 +18,7 @@ void bug_gh9090_void_int_char(int i, char *s);
|
||||||
void bug_gh9090_void_int_char_var(int i, char *fmt, ...);
|
void bug_gh9090_void_int_char_var(int i, char *fmt, ...);
|
||||||
EOD;
|
EOD;
|
||||||
|
|
||||||
if (PHP_OS_FAMILY !== 'Windows') {
|
|
||||||
$ffi = FFI::cdef($h);
|
$ffi = FFI::cdef($h);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$ffi = FFI::cdef($h, 'php_zend_test.dll');
|
|
||||||
} catch (FFI\Exception $ex) {
|
|
||||||
$ffi = FFI::cdef($h, ffi_get_php_dll_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$func_ptrs = [
|
$func_ptrs = [
|
||||||
'bug_gh9090_void_none_ptr',
|
'bug_gh9090_void_none_ptr',
|
||||||
|
|
|
@ -5,21 +5,11 @@ ffi
|
||||||
zend_test
|
zend_test
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/utils.inc';
|
|
||||||
$header = <<<HEADER
|
$header = <<<HEADER
|
||||||
extern int gh11934b_ffi_var_test_cdata;
|
extern int gh11934b_ffi_var_test_cdata;
|
||||||
HEADER;
|
HEADER;
|
||||||
|
|
||||||
if (PHP_OS_FAMILY !== 'Windows') {
|
|
||||||
$ffi = FFI::cdef($header);
|
$ffi = FFI::cdef($header);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$ffi = FFI::cdef($header, 'php_zend_test.dll');
|
|
||||||
} catch (FFI\Exception $ex) {
|
|
||||||
$ffi = FFI::cdef($header, ffi_get_php_dll_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$ffi->gh11934b_ffi_var_test_cdata->cdata = 2;
|
$ffi->gh11934b_ffi_var_test_cdata->cdata = 2;
|
||||||
var_dump($ffi->gh11934b_ffi_var_test_cdata);
|
var_dump($ffi->gh11934b_ffi_var_test_cdata);
|
||||||
$source = $ffi->new('int');
|
$source = $ffi->new('int');
|
||||||
|
|
|
@ -1,28 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
function ffi_cdef($code, $lib)
|
|
||||||
{
|
|
||||||
if (isset($lib)) {
|
|
||||||
return FFI::cdef($code, $lib);
|
|
||||||
} else {
|
|
||||||
return FFI::cdef($code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ffi_get_php_dll_name()
|
|
||||||
{
|
|
||||||
if (PHP_OS_FAMILY === 'Windows') {
|
|
||||||
return "php" . PHP_MAJOR_VERSION . (PHP_ZTS ? "ts" : "") . (PHP_DEBUG ? "_debug" : "") . ".dll";
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ffi_get_fastcall_specifier()
|
function ffi_get_fastcall_specifier()
|
||||||
{
|
{
|
||||||
foreach (['__attribute__((fastcall))', '__fastcall', '__vectorcall'] as $spec) {
|
foreach (['__attribute__((fastcall))', '__fastcall', '__vectorcall'] as $spec) {
|
||||||
try {
|
try {
|
||||||
ffi_cdef("extern size_t $spec zend_list_insert(void *ptr, int type);", ffi_get_php_dll_name());
|
FFI::cdef("extern size_t $spec zend_list_insert(void *ptr, int type);");
|
||||||
return "$spec ";
|
return "$spec ";
|
||||||
} catch (Throwable $e) {}
|
} catch (Throwable $e) {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue