Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, bool copy)
{
const zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
#ifdef ZTS
if (!c) {
c = zend_hash_find_ptr(zend_global_constants_table, name);
}
#endif
if (c) {
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
Expand Down
83 changes: 71 additions & 12 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,12 @@ ZEND_API size_t executor_globals_offset;
static void *language_scanner_globals_tls_addr(void) { return &language_scanner_globals; }
static HashTable *global_function_table = NULL;
static HashTable *global_class_table = NULL;
static HashTable *global_constants_table = NULL;
static HashTable *global_auto_globals_table = NULL;
static HashTable *global_persistent_list = NULL;
TSRMLS_MAIN_CACHE_DEFINE()
# define GLOBAL_FUNCTION_TABLE global_function_table
# define GLOBAL_CLASS_TABLE global_class_table
# define GLOBAL_CONSTANTS_TABLE global_constants_table
# define GLOBAL_CONSTANTS_TABLE zend_global_constants_table
# define GLOBAL_AUTO_GLOBALS_TABLE global_auto_globals_table
#else
# define GLOBAL_FUNCTION_TABLE CG(function_table)
Expand Down Expand Up @@ -715,19 +714,34 @@ static void auto_global_copy_ctor(zval *zv) /* {{{ */
}
/* }}} */

/* Copy the source's bucket array as-is instead of copying entries one by one.
* The source must be frozen and contiguous. Entries remain owned by the
* source table and must not be destroyed with the copy, so
* compiler_globals_dtor() undefs them first. */
static HashTable *zend_hash_clone_persistent(const HashTable *source)
{
HashTable *ht = (HashTable *) malloc(sizeof(HashTable));

if (source->nNumUsed == 0) {
zend_hash_init(ht, source->nTableSize, NULL, source->pDestructor, 1);
} else {
*ht = *source;
HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), 1));
memcpy(HT_GET_DATA_ADDR(ht), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source));
}
return ht;
}

static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{{ */
{
compiler_globals->compiled_filename = NULL;
compiler_globals->zend_lineno = 0;

compiler_globals->function_table = (HashTable *) malloc(sizeof(HashTable));
zend_hash_init(compiler_globals->function_table, 1024, NULL, ZEND_FUNCTION_DTOR, 1);
zend_hash_copy(compiler_globals->function_table, global_function_table, NULL);
compiler_globals->function_table = zend_hash_clone_persistent(global_function_table);
compiler_globals->copied_functions_count = zend_hash_num_elements(compiler_globals->function_table);

compiler_globals->class_table = (HashTable *) malloc(sizeof(HashTable));
zend_hash_init(compiler_globals->class_table, 64, NULL, ZEND_CLASS_DTOR, 1);
zend_hash_copy(compiler_globals->class_table, global_class_table, zend_class_add_ref);
compiler_globals->class_table = zend_hash_clone_persistent(global_class_table);
compiler_globals->copied_classes_count = zend_hash_num_elements(compiler_globals->class_table);

zend_set_default_compile_time_values();

Expand Down Expand Up @@ -778,6 +792,21 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{
free(compiler_globals->function_table);
}
if (compiler_globals->class_table != GLOBAL_CLASS_TABLE) {
uint32_t n = compiler_globals->copied_classes_count;

/* Prevent destruction of classes copied from the main process context */
if (zend_hash_num_elements(compiler_globals->class_table) <= n) {
compiler_globals->class_table->nNumUsed = 0;
} else {
Bucket *p = compiler_globals->class_table->arData;

compiler_globals->class_table->nNumOfElements -= n;
while (n != 0) {
ZVAL_UNDEF(&p->val);
p++;
n--;
}
}
/* Child classes may reuse structures from parent classes, so destroy in reverse order. */
zend_hash_graceful_reverse_destroy(compiler_globals->class_table);
free(compiler_globals->class_table);
Expand Down Expand Up @@ -805,7 +834,6 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{
static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{{ */
{
zend_startup_constants();
zend_copy_constants(executor_globals->zend_constants, GLOBAL_CONSTANTS_TABLE);
zend_init_rsrc_plist();
zend_init_exception_op();
zend_init_call_trampoline_op();
Expand Down Expand Up @@ -1041,7 +1069,8 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
compiler_globals->auto_globals = GLOBAL_AUTO_GLOBALS_TABLE;

zend_hash_destroy(executor_globals->zend_constants);
*executor_globals->zend_constants = *GLOBAL_CONSTANTS_TABLE;
free(executor_globals->zend_constants);
executor_globals->zend_constants = GLOBAL_CONSTANTS_TABLE;
#else
ini_scanner_globals_ctor(&ini_scanner_globals);
php_scanner_globals_ctor(&language_scanner_globals);
Expand Down Expand Up @@ -1096,6 +1125,31 @@ void zend_register_standard_ini_entries(void) /* {{{ */
/* }}} */


#ifdef ZTS
/* All threads read constants from GLOBAL_CONSTANTS_TABLE without copying or
* refcounting, so any string still not interned at this point must be made
* immortal. In practice registration interns everything and this is a no-op. */
static void zend_share_persistent_constants(void)
{
zend_constant *c;

ZEND_HASH_MAP_FOREACH_PTR(GLOBAL_CONSTANTS_TABLE, c) {
if (!(ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)) {
continue;
}
if (!ZSTR_IS_INTERNED(c->name)) {
GC_ADD_FLAGS(c->name, IS_STR_INTERNED | IS_STR_PERMANENT);
}
if (c->filename && !ZSTR_IS_INTERNED(c->filename)) {
GC_ADD_FLAGS(c->filename, IS_STR_INTERNED | IS_STR_PERMANENT);
}
if (Z_TYPE(c->value) == IS_STRING && !ZSTR_IS_INTERNED(Z_STR(c->value))) {
GC_ADD_FLAGS(Z_STR(c->value), IS_STR_INTERNED | IS_STR_PERMANENT);
}
} ZEND_HASH_FOREACH_END();
}
#endif

/* Unlink the global (r/o) copies of the class, function and constant tables,
* and use a fresh r/w copy for the startup thread
*/
Expand All @@ -1122,7 +1176,11 @@ zend_result zend_post_startup(void) /* {{{ */
#ifdef ZTS
*GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table;
*GLOBAL_CLASS_TABLE = *compiler_globals->class_table;
*GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants;
/* zend_hash_clone_persistent() requires hole-free sources; disable_functions
* etc. may have deleted entries. The tables are frozen from here on. */
zend_hash_rehash(GLOBAL_FUNCTION_TABLE);
zend_hash_rehash(GLOBAL_CLASS_TABLE);
zend_share_persistent_constants();
global_map_ptr_last = compiler_globals->map_ptr_last;

short_tags_default = CG(short_tags);
Expand All @@ -1148,7 +1206,8 @@ zend_result zend_post_startup(void) /* {{{ */
} else {
compiler_globals_ctor(compiler_globals);
}
free(EG(zend_constants));
/* Aliases GLOBAL_CONSTANTS_TABLE; the ctor allocates this thread's own
* (empty) table for runtime constants. */
EG(zend_constants) = NULL;

executor_globals_ctor(executor_globals);
Expand Down
63 changes: 35 additions & 28 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,9 @@ ZEND_FUNCTION(get_defined_constants)

array_init(return_value);

HashTable *const_tables[2];
unsigned int num_const_tables = zend_get_constants_tables(const_tables);

if (categorize) {
zend_constant *val;
int module_number;
Expand All @@ -1650,44 +1653,48 @@ ZEND_FUNCTION(get_defined_constants)
} ZEND_HASH_FOREACH_END();
module_names[i] = "user";

ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), val) {
if (!val->name) {
/* skip special constants */
continue;
}
for (unsigned int t = 0; t < num_const_tables; t++) {
ZEND_HASH_MAP_FOREACH_PTR(const_tables[t], val) {
if (!val->name) {
/* skip special constants */
continue;
}

if (ZEND_CONSTANT_MODULE_NUMBER(val) == PHP_USER_CONSTANT) {
module_number = i;
} else if (ZEND_CONSTANT_MODULE_NUMBER(val) > i) {
/* should not happen */
continue;
} else {
module_number = ZEND_CONSTANT_MODULE_NUMBER(val);
}
if (ZEND_CONSTANT_MODULE_NUMBER(val) == PHP_USER_CONSTANT) {
module_number = i;
} else if (ZEND_CONSTANT_MODULE_NUMBER(val) > i) {
/* should not happen */
continue;
} else {
module_number = ZEND_CONSTANT_MODULE_NUMBER(val);
}

if (Z_TYPE(modules[module_number]) == IS_UNDEF) {
array_init(&modules[module_number]);
add_assoc_zval(return_value, module_names[module_number], &modules[module_number]);
}
if (Z_TYPE(modules[module_number]) == IS_UNDEF) {
array_init(&modules[module_number]);
add_assoc_zval(return_value, module_names[module_number], &modules[module_number]);
}

ZVAL_COPY_OR_DUP(&const_val, &val->value);
zend_hash_add_new(Z_ARRVAL(modules[module_number]), val->name, &const_val);
} ZEND_HASH_FOREACH_END();
ZVAL_COPY_OR_DUP(&const_val, &val->value);
zend_hash_add_new(Z_ARRVAL(modules[module_number]), val->name, &const_val);
} ZEND_HASH_FOREACH_END();
}

efree(module_names);
efree(modules);
} else {
zend_constant *constant;
zval const_val;

ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) {
if (!constant->name) {
/* skip special constants */
continue;
}
ZVAL_COPY_OR_DUP(&const_val, &constant->value);
zend_hash_add_new(Z_ARRVAL_P(return_value), constant->name, &const_val);
} ZEND_HASH_FOREACH_END();
for (unsigned int t = 0; t < num_const_tables; t++) {
ZEND_HASH_MAP_FOREACH_PTR(const_tables[t], constant) {
if (!constant->name) {
/* skip special constants */
continue;
}
ZVAL_COPY_OR_DUP(&const_val, &constant->value);
zend_hash_add_new(Z_ARRVAL_P(return_value), constant->name, &const_val);
} ZEND_HASH_FOREACH_END();
}
}
}
/* }}} */
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,11 @@ static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qu
return true;
}
c = zend_hash_find_ptr(EG(zend_constants), name);
#ifdef ZTS
if (!c) {
c = zend_hash_find_ptr(zend_global_constants_table, name);
}
#endif
if (c && can_ct_eval_const(c)) {
ZVAL_COPY_OR_DUP(zv, &c->value);
return true;
Expand Down
67 changes: 40 additions & 27 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,33 +70,10 @@ void free_zend_constant(zval *zv)


#ifdef ZTS
static void copy_zend_constant(zval *zv)
{
zend_constant *c = Z_PTR_P(zv);

ZEND_ASSERT(ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);
Z_PTR_P(zv) = pemalloc(sizeof(zend_constant), 1);
memcpy(Z_PTR_P(zv), c, sizeof(zend_constant));

c = Z_PTR_P(zv);
c->name = zend_string_copy(c->name);
if (c->filename != NULL) {
c->filename = zend_string_copy(c->filename);
}
if (c->attributes != NULL) {
// Use the same attributes table
GC_ADDREF(c->attributes);
}
if (Z_TYPE(c->value) == IS_STRING) {
Z_STR(c->value) = zend_string_dup(Z_STR(c->value), 1);
}
}


void zend_copy_constants(HashTable *target, HashTable *source)
{
zend_hash_copy(target, source, copy_zend_constant);
}
/* Persistent constants live only in this shared table. Per-thread
* EG(zend_constants) holds runtime-defined constants. During startup the
* main thread's EG(zend_constants) aliases this table. */
ZEND_API HashTable *zend_global_constants_table = NULL;
#endif


Expand Down Expand Up @@ -265,10 +242,25 @@ ZEND_API bool zend_verify_const_access(const zend_class_constant *c, const zend_

static zend_constant *zend_get_constant_str_impl(const char *name, size_t name_len)
{
#ifdef ZTS
/* Skip the per-thread table while no run-time constant has been
* defined (the common case); persistent constants live only in the
* shared global table. */
zend_constant *c = zend_hash_num_elements(EG(zend_constants))
? zend_hash_str_find_ptr(EG(zend_constants), name, name_len) : NULL;
if (c) {
return c;
}
c = zend_hash_str_find_ptr(zend_global_constants_table, name, name_len);
if (c) {
return c;
}
#else
zend_constant *c = zend_hash_str_find_ptr(EG(zend_constants), name, name_len);
if (c) {
return c;
}
#endif

c = zend_get_halt_offset_constant(name, name_len);
if (c) {
Expand All @@ -289,10 +281,22 @@ ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)

ZEND_API zend_constant *zend_get_constant_ptr(zend_string *name)
{
#ifdef ZTS
zend_constant *c = zend_hash_num_elements(EG(zend_constants))
? zend_hash_find_ptr(EG(zend_constants), name) : NULL;
if (c) {
return c;
}
c = zend_hash_find_ptr(zend_global_constants_table, name);
if (c) {
return c;
}
#else
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
if (c) {
return c;
}
#endif

c = zend_get_halt_offset_constant(ZSTR_VAL(name), ZSTR_LEN(name));
if (c) {
Expand Down Expand Up @@ -458,6 +462,11 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, const zend_class_entry *
memcpy(lcname + prefix_len + 1, constant_name, const_name_len + 1);

c = zend_hash_str_find_ptr(EG(zend_constants), lcname, lcname_len);
#ifdef ZTS
if (!c) {
c = zend_hash_str_find_ptr(zend_global_constants_table, lcname, lcname_len);
}
#endif
free_alloca(lcname, use_heap);

if (!c) {
Expand Down Expand Up @@ -541,6 +550,10 @@ ZEND_API zend_constant *zend_register_constant(zend_constant *c)
/* Check if the user is trying to define any special constant */
if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__")
|| (!persistent && zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name)))
#ifdef ZTS
|| (EG(zend_constants) != zend_global_constants_table
&& zend_hash_exists(zend_global_constants_table, name))
#endif
|| (ret = zend_hash_add_constant(EG(zend_constants), name, c)) == NULL
) {
zend_error(E_WARNING, "Constant %s already defined, this will be an error in PHP 9", ZSTR_VAL(name));
Expand Down
Loading
Loading