Skip to content

Commit 1417352

Browse files
committed
Allow loading FFI bindings through ffi.preload directive
1 parent 1c9bfcb commit 1417352

File tree

5 files changed

+124
-27
lines changed

5 files changed

+124
-27
lines changed

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,9 @@ END_EXTERN_C()
10941094
/* disable jumptable optimization for switch statements */
10951095
#define ZEND_COMPILE_NO_JUMPTABLES (1<<16)
10961096

1097+
/* this flag is set when compiler invoked during preloading in separate process */
1098+
#define ZEND_COMPILE_PRELOAD_IN_CHILD (1<<17)
1099+
10971100
/* The default value for CG(compiler_options) */
10981101
#define ZEND_COMPILE_DEFAULT ZEND_COMPILE_HANDLE_OP_ARRAY
10991102

ext/ffi/ffi.c

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,35 +3083,27 @@ static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type) /* {{{ */
30833083
}
30843084
/* }}} */
30853085

3086-
ZEND_METHOD(FFI, load) /* {{{ */
3086+
static zend_ffi *zend_ffi_load(const char *filename, zend_bool preload) /* {{{ */
30873087
{
3088-
zend_string *fn;
30893088
struct stat buf;
30903089
int fd;
3091-
char *filename, *code, *code_pos, *scope_name, *lib;
3090+
char *code, *code_pos, *scope_name, *lib;
30923091
size_t code_size, scope_name_len;
30933092
zend_ffi *ffi;
3094-
zend_bool preload = (CG(compiler_options) & ZEND_COMPILE_PRELOAD) != 0;
30953093
DL_HANDLE handle = NULL;
30963094
zend_ffi_scope *scope = NULL;
30973095
zend_string *name;
30983096
zend_ffi_symbol *sym;
30993097
zend_ffi_tag *tag;
31003098
void *addr;
31013099

3102-
ZEND_FFI_VALIDATE_API_RESTRICTION();
3103-
ZEND_PARSE_PARAMETERS_START(1, 1)
3104-
Z_PARAM_STR(fn)
3105-
ZEND_PARSE_PARAMETERS_END();
3106-
3107-
filename = ZSTR_VAL(fn);
31083100
if (stat(filename, &buf) != 0) {
31093101
if (preload) {
31103102
zend_error(E_WARNING, "FFI: failed pre-loading '%s', file doesn't exist", filename);
31113103
} else {
31123104
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', file doesn't exist", filename);
31133105
}
3114-
return;
3106+
return NULL;
31153107
}
31163108

31173109
if ((buf.st_mode & S_IFMT) != S_IFREG) {
@@ -3120,7 +3112,7 @@ ZEND_METHOD(FFI, load) /* {{{ */
31203112
} else {
31213113
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', not a regular file", filename);
31223114
}
3123-
return;
3115+
return NULL;
31243116
}
31253117

31263118
code_size = buf.st_size;
@@ -3134,7 +3126,7 @@ ZEND_METHOD(FFI, load) /* {{{ */
31343126
}
31353127
efree(code);
31363128
close(fd);
3137-
return;
3129+
return NULL;
31383130
}
31393131
close(fd);
31403132
code[code_size] = 0;
@@ -3153,7 +3145,7 @@ ZEND_METHOD(FFI, load) /* {{{ */
31533145
if (!code_pos) {
31543146
efree(code);
31553147
FFI_G(persistent) = 0;
3156-
return;
3148+
return NULL;
31573149
}
31583150
code_size -= code_pos - code;
31593151

@@ -3318,7 +3310,11 @@ ZEND_METHOD(FFI, load) /* {{{ */
33183310
}
33193311
}
33203312

3321-
ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
3313+
if (EG(objects_store).object_buckets) {
3314+
ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
3315+
} else {
3316+
ffi = ecalloc(1, sizeof(zend_ffi));
3317+
}
33223318
ffi->symbols = scope->symbols;
33233319
ffi->tags = scope->tags;
33243320
ffi->persistent = 1;
@@ -3333,7 +3329,7 @@ ZEND_METHOD(FFI, load) /* {{{ */
33333329
FFI_G(symbols) = NULL;
33343330
FFI_G(tags) = NULL;
33353331

3336-
RETURN_OBJ(&ffi->std);
3332+
return ffi;
33373333

33383334
cleanup:
33393335
efree(code);
@@ -3348,6 +3344,30 @@ ZEND_METHOD(FFI, load) /* {{{ */
33483344
FFI_G(tags) = NULL;
33493345
}
33503346
FFI_G(persistent) = 0;
3347+
return NULL;
3348+
}
3349+
/* }}} */
3350+
3351+
ZEND_METHOD(FFI, load) /* {{{ */
3352+
{
3353+
zend_string *fn;
3354+
zend_ffi *ffi;
3355+
3356+
ZEND_FFI_VALIDATE_API_RESTRICTION();
3357+
ZEND_PARSE_PARAMETERS_START(1, 1)
3358+
Z_PARAM_STR(fn)
3359+
ZEND_PARSE_PARAMETERS_END();
3360+
3361+
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD_IN_CHILD) {
3362+
zend_throw_error(zend_ffi_exception_ce, "FFI::load() doesn't work in conjunction with \"opcache.pelaod_user\". Use \"ffi.preload\" instead.");
3363+
return;
3364+
}
3365+
3366+
ffi = zend_ffi_load(ZSTR_VAL(fn), (CG(compiler_options) & ZEND_COMPILE_PRELOAD) != 0);
3367+
3368+
if (ffi) {
3369+
RETURN_OBJ(&ffi->std);
3370+
}
33513371
}
33523372
/* }}} */
33533373

@@ -4813,8 +4833,51 @@ static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */
48134833

48144834
ZEND_INI_BEGIN()
48154835
ZEND_INI_ENTRY3_EX("ffi.enable", "preload", ZEND_INI_SYSTEM, OnUpdateFFIEnable, NULL, NULL, NULL, zend_ffi_enable_displayer_cb)
4836+
STD_ZEND_INI_ENTRY("ffi.preload", NULL, ZEND_INI_SYSTEM, OnUpdateString, preload, zend_ffi_globals, ffi_globals)
48164837
ZEND_INI_END()
48174838

4839+
static int zend_ffi_preload(char *preload) /* {{{ */
4840+
{
4841+
zend_ffi *ffi;
4842+
char *s = NULL, *e, *filename;
4843+
4844+
e = preload;
4845+
while (*e) {
4846+
switch (*e) {
4847+
case ZEND_PATHS_SEPARATOR:
4848+
if (s) {
4849+
filename = estrndup(s, e-s);
4850+
ffi = zend_ffi_load(filename, 1);
4851+
efree(filename);
4852+
if (!ffi) {
4853+
return FAILURE;
4854+
}
4855+
efree(ffi);
4856+
s = NULL;
4857+
}
4858+
break;
4859+
default:
4860+
if (!s) {
4861+
s = e;
4862+
}
4863+
break;
4864+
}
4865+
e++;
4866+
}
4867+
if (s) {
4868+
filename = estrndup(s, e-s);
4869+
ffi = zend_ffi_load(filename, 1);
4870+
efree(filename);
4871+
if (!ffi) {
4872+
return FAILURE;
4873+
}
4874+
efree(ffi);
4875+
}
4876+
4877+
return SUCCESS;
4878+
}
4879+
/* }}} */
4880+
48184881
/* {{{ ZEND_MINIT_FUNCTION
48194882
*/
48204883
ZEND_MINIT_FUNCTION(ffi)
@@ -4976,6 +5039,12 @@ ZEND_MINIT_FUNCTION(ffi)
49765039
zend_ffi_ctype_handlers.get_properties = zend_fake_get_properties;
49775040
zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc;
49785041

5042+
if (FFI_G(preload)) {
5043+
if (zend_ffi_preload(FFI_G(preload)) != SUCCESS) {
5044+
return FAILURE;
5045+
}
5046+
}
5047+
49795048
return SUCCESS;
49805049
}
49815050
/* }}} */

ext/ffi/php_ffi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi)
3838
HashTable types;
3939

4040
/* preloading */
41+
char *preload;
4142
HashTable *scopes; /* list of preloaded scopes */
4243

4344
/* callbacks */

ext/ffi/tests/302.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
FFI 302: FFI preloading
3+
--SKIPIF--
4+
<?php require_once('skipif.inc'); ?>
5+
<?php if (substr(PHP_OS, 0, 3) == 'WIN') die('skip not for Windows'); ?>
6+
--INI--
7+
ffi.enable=1
8+
ffi.preload={PWD}/300.h
9+
--FILE--
10+
<?php
11+
$ffi = FFI::scope("TEST_300");
12+
$ffi->printf("Hello World from %s!\n", "PHP");
13+
?>
14+
--EXPECT--
15+
Hello World from PHP!

ext/opcache/ZendAccelerator.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4140,14 +4140,17 @@ static void preload_load(void)
41404140
if (EG(class_table)) {
41414141
EG(persistent_classes_count) = EG(class_table)->nNumUsed;
41424142
}
4143-
CG(map_ptr_last) = ZCSG(map_ptr_last);
4143+
if (CG(map_ptr_last) != ZCSG(map_ptr_last)) {
4144+
CG(map_ptr_last) = ZCSG(map_ptr_last);
4145+
CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096);
4146+
CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1);
4147+
}
41444148
}
41454149

41464150
static int accel_preload(const char *config)
41474151
{
41484152
zend_file_handle file_handle;
41494153
int ret;
4150-
uint32_t orig_compiler_options;
41514154
char *orig_open_basedir;
41524155
size_t orig_map_ptr_last;
41534156
zval *zv;
@@ -4159,14 +4162,6 @@ static int accel_preload(const char *config)
41594162
preload_orig_compile_file = accelerator_orig_compile_file;
41604163
accelerator_orig_compile_file = preload_compile_file;
41614164

4162-
orig_compiler_options = CG(compiler_options);
4163-
CG(compiler_options) |= ZEND_COMPILE_PRELOAD;
4164-
CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
4165-
// CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
4166-
CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
4167-
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
4168-
// CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
4169-
41704165
orig_map_ptr_last = CG(map_ptr_last);
41714166

41724167
/* Compile and execute proloading script */
@@ -4207,7 +4202,6 @@ static int accel_preload(const char *config)
42074202
ret = FAILURE;
42084203
} zend_end_try();
42094204

4210-
CG(compiler_options) = orig_compiler_options;
42114205
PG(open_basedir) = orig_open_basedir;
42124206
accelerator_orig_compile_file = preload_orig_compile_file;
42134207
ZCG(enabled) = 1;
@@ -4500,6 +4494,7 @@ static int accel_finish_startup(void)
45004494
char *(*orig_getenv)(char *name, size_t name_len TSRMLS_DC) = sapi_module.getenv;
45014495
size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
45024496
void (*orig_flush)(void *server_context) = sapi_module.flush;
4497+
uint32_t orig_compiler_options = CG(compiler_options);
45034498
#ifdef ZEND_SIGNALS
45044499
zend_bool old_reset_signals = SIGG(reset);
45054500
#endif
@@ -4595,6 +4590,18 @@ static int accel_finish_startup(void)
45954590
sapi_module.ub_write = preload_ub_write;
45964591
sapi_module.flush = preload_flush;
45974592

4593+
#ifndef ZEND_WIN32
4594+
if (in_child) {
4595+
CG(compiler_options) |= ZEND_COMPILE_PRELOAD_IN_CHILD;
4596+
}
4597+
#endif
4598+
CG(compiler_options) |= ZEND_COMPILE_PRELOAD;
4599+
CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
4600+
CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
4601+
CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
4602+
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
4603+
CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
4604+
45984605
zend_interned_strings_switch_storage(1);
45994606

46004607
#ifdef ZEND_SIGNALS
@@ -4648,6 +4655,8 @@ static int accel_finish_startup(void)
46484655
SIGG(reset) = old_reset_signals;
46494656
#endif
46504657

4658+
CG(compiler_options) = orig_compiler_options;
4659+
46514660
sapi_module.activate = orig_activate;
46524661
sapi_module.deactivate = orig_deactivate;
46534662
sapi_module.register_server_variables = orig_register_server_variables;

0 commit comments

Comments
 (0)