/src/php-src/ext/opcache/zend_persist.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend OPcache | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright © The PHP Group and Contributors. | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to the Modified BSD License that is | |
8 | | | bundled with this package in the file LICENSE, and is available | |
9 | | | through the World Wide Web at <https://www.php.net/license/>. | |
10 | | | | |
11 | | | SPDX-License-Identifier: BSD-3-Clause | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Authors: Andi Gutmans <andi@php.net> | |
14 | | | Zeev Suraski <zeev@php.net> | |
15 | | | Stanislav Malyshev <stas@zend.com> | |
16 | | | Dmitry Stogov <dmitry@php.net> | |
17 | | +----------------------------------------------------------------------+ |
18 | | */ |
19 | | |
20 | | #include "zend.h" |
21 | | #include "ZendAccelerator.h" |
22 | | #include "zend_persist.h" |
23 | | #include "zend_extensions.h" |
24 | | #include "zend_shared_alloc.h" |
25 | | #include "zend_vm.h" |
26 | | #include "zend_constants.h" |
27 | | #include "zend_operators.h" |
28 | | #include "zend_interfaces.h" |
29 | | #include "zend_attributes.h" |
30 | | #include "zend_partial.h" |
31 | | |
32 | | #ifdef HAVE_JIT |
33 | | # include "Optimizer/zend_func_info.h" |
34 | | # include "jit/zend_jit.h" |
35 | | #endif |
36 | | |
37 | 25.3k | #define zend_set_str_gc_flags(str) do { \ |
38 | 25.3k | GC_SET_REFCOUNT(str, 2); \ |
39 | 25.3k | uint32_t flags = GC_STRING | (ZSTR_IS_VALID_UTF8(str) ? IS_STR_VALID_UTF8 : 0); \ |
40 | 25.3k | if (file_cache_only \ |
41 | 25.3k | || (ZCG(current_persistent_script) && ZCG(current_persistent_script)->corrupted)) { \ |
42 | 0 | GC_TYPE_INFO(str) = GC_STRING | (IS_STR_INTERNED << GC_FLAGS_SHIFT); \ |
43 | 0 | flags |= (IS_STR_INTERNED << GC_FLAGS_SHIFT); \ |
44 | 25.3k | } else { \ |
45 | 25.3k | flags |= ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \ |
46 | 25.3k | } \ |
47 | 25.3k | GC_TYPE_INFO(str) = flags; \ |
48 | 25.3k | } while (0) |
49 | | |
50 | 26.3k | #define zend_accel_store_string(str) do { \ |
51 | 26.3k | zend_string *new_str = zend_shared_alloc_get_xlat_entry(str); \ |
52 | 26.3k | if (new_str) { \ |
53 | 1.00k | zend_string_release_ex(str, 0); \ |
54 | 1.00k | str = new_str; \ |
55 | 25.3k | } else { \ |
56 | 25.3k | new_str = zend_shared_memdup_put((void*)str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \ |
57 | 25.3k | zend_string_release_ex(str, 0); \ |
58 | 25.3k | str = new_str; \ |
59 | 25.3k | zend_string_hash_val(str); \ |
60 | 25.3k | zend_set_str_gc_flags(str); \ |
61 | 25.3k | } \ |
62 | 26.3k | } while (0) |
63 | | #define zend_accel_memdup_string(str) do { \ |
64 | | zend_string *new_str = zend_shared_alloc_get_xlat_entry(str); \ |
65 | | if (new_str) { \ |
66 | | str = new_str; \ |
67 | | } else { \ |
68 | | new_str = zend_shared_memdup_put((void*)str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \ |
69 | | str = new_str; \ |
70 | | zend_string_hash_val(str); \ |
71 | | zend_set_str_gc_flags(str); \ |
72 | | } \ |
73 | | } while (0) |
74 | 107k | #define zend_accel_store_interned_string(str) do { \ |
75 | 107k | if (!IS_ACCEL_INTERNED(str)) { \ |
76 | 0 | zend_accel_store_string(str); \ |
77 | 0 | } \ |
78 | 107k | } while (0) |
79 | | #define zend_accel_memdup_interned_string(str) do { \ |
80 | | if (!IS_ACCEL_INTERNED(str)) { \ |
81 | | zend_accel_memdup_string(str); \ |
82 | | } \ |
83 | | } while (0) |
84 | | |
85 | | typedef void (*zend_persist_func_t)(zval*); |
86 | | |
87 | | static void zend_persist_zval(zval *z); |
88 | | static void zend_persist_op_array(zval *zv); |
89 | | |
90 | | static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = |
91 | | {HT_INVALID_IDX, HT_INVALID_IDX}; |
92 | | |
93 | | static void zend_hash_persist(HashTable *ht) |
94 | 52.3k | { |
95 | 52.3k | uint32_t idx, nIndex; |
96 | 52.3k | Bucket *p; |
97 | | |
98 | 52.3k | HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; |
99 | 52.3k | ht->pDestructor = NULL; |
100 | 52.3k | ht->nInternalPointer = 0; |
101 | | |
102 | 52.3k | if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { |
103 | 51.1k | if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { |
104 | 51.1k | HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); |
105 | 51.1k | } else { |
106 | 0 | HT_SET_DATA_ADDR(ht, &uninitialized_bucket); |
107 | 0 | } |
108 | 51.1k | return; |
109 | 51.1k | } |
110 | 1.23k | if (ht->nNumUsed == 0) { |
111 | 0 | efree(HT_GET_DATA_ADDR(ht)); |
112 | 0 | ht->nTableMask = HT_MIN_MASK; |
113 | 0 | if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { |
114 | 0 | HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket)); |
115 | 0 | } else { |
116 | 0 | HT_SET_DATA_ADDR(ht, &uninitialized_bucket); |
117 | 0 | } |
118 | 0 | HT_FLAGS(ht) |= HASH_FLAG_UNINITIALIZED; |
119 | 0 | return; |
120 | 0 | } |
121 | 1.23k | if (HT_IS_PACKED(ht)) { |
122 | 289 | void *data = HT_GET_DATA_ADDR(ht); |
123 | 289 | if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) { |
124 | 0 | data = zend_shared_memdup(data, HT_PACKED_USED_SIZE(ht)); |
125 | 289 | } else { |
126 | 289 | data = zend_shared_memdup_free(data, HT_PACKED_USED_SIZE(ht)); |
127 | 289 | } |
128 | 289 | HT_SET_DATA_ADDR(ht, data); |
129 | 948 | } else if (ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { |
130 | | /* compact table */ |
131 | 0 | void *old_data = HT_GET_DATA_ADDR(ht); |
132 | 0 | Bucket *old_buckets = ht->arData; |
133 | 0 | uint32_t hash_size; |
134 | |
|
135 | 0 | hash_size = (uint32_t)(-(int32_t)ht->nTableMask); |
136 | 0 | while (hash_size >> 2 > ht->nNumUsed) { |
137 | 0 | hash_size >>= 1; |
138 | 0 | } |
139 | 0 | ht->nTableMask = (uint32_t)(-(int32_t)hash_size); |
140 | 0 | ZEND_ASSERT(((uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ |
141 | 0 | HT_SET_DATA_ADDR(ht, ZCG(mem)); |
142 | 0 | ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket)))); |
143 | 0 | HT_HASH_RESET(ht); |
144 | 0 | memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket)); |
145 | 0 | if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { |
146 | 0 | efree(old_data); |
147 | 0 | } |
148 | | |
149 | | /* rehash */ |
150 | 0 | for (idx = 0; idx < ht->nNumUsed; idx++) { |
151 | 0 | p = ht->arData + idx; |
152 | 0 | if (Z_TYPE(p->val) == IS_UNDEF) continue; |
153 | 0 | nIndex = p->h | ht->nTableMask; |
154 | 0 | Z_NEXT(p->val) = HT_HASH(ht, nIndex); |
155 | 0 | HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); |
156 | 0 | } |
157 | 948 | } else { |
158 | 948 | void *data = ZCG(mem); |
159 | 948 | void *old_data = HT_GET_DATA_ADDR(ht); |
160 | | |
161 | 948 | ZEND_ASSERT(((uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ |
162 | 948 | ZCG(mem) = (void*)((char*)data + ZEND_ALIGNED_SIZE(HT_USED_SIZE(ht))); |
163 | 948 | memcpy(data, old_data, HT_USED_SIZE(ht)); |
164 | 948 | if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { |
165 | 948 | efree(old_data); |
166 | 948 | } |
167 | 948 | HT_SET_DATA_ADDR(ht, data); |
168 | 948 | } |
169 | 1.23k | } |
170 | | |
171 | | static zend_ast *zend_persist_ast(zend_ast *ast) |
172 | 714 | { |
173 | 714 | uint32_t i; |
174 | 714 | zend_ast *node; |
175 | | |
176 | 714 | if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) { |
177 | 376 | zend_ast_zval *copy = zend_shared_memdup(ast, sizeof(zend_ast_zval)); |
178 | 376 | zend_persist_zval(©->val); |
179 | 376 | node = (zend_ast *) copy; |
180 | 376 | } else if (zend_ast_is_list(ast)) { |
181 | 42 | zend_ast_list *list = zend_ast_get_list(ast); |
182 | 42 | zend_ast_list *copy = zend_shared_memdup(ast, |
183 | 42 | sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children); |
184 | 76 | for (i = 0; i < list->children; i++) { |
185 | 34 | if (copy->child[i]) { |
186 | 34 | copy->child[i] = zend_persist_ast(copy->child[i]); |
187 | 34 | } |
188 | 34 | } |
189 | 42 | node = (zend_ast *) copy; |
190 | 296 | } else if (ast->kind == ZEND_AST_OP_ARRAY) { |
191 | 0 | zend_ast_op_array *copy = zend_shared_memdup(ast, sizeof(zend_ast_op_array)); |
192 | 0 | zval z; |
193 | 0 | ZVAL_PTR(&z, copy->op_array); |
194 | 0 | zend_persist_op_array(&z); |
195 | 0 | copy->op_array = Z_PTR(z); |
196 | 0 | node = (zend_ast *) copy; |
197 | 296 | } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { |
198 | 22 | zend_ast_fcc *copy = zend_shared_memdup(ast, sizeof(zend_ast_fcc)); |
199 | 22 | if (!ZCG(current_persistent_script)->corrupted) { |
200 | 22 | ZEND_MAP_PTR_NEW(copy->fptr); |
201 | 22 | copy->attr |= ZEND_PARTIAL_CACHEABLE_IN_SHM; |
202 | 22 | } |
203 | 22 | if (copy->filename) { |
204 | 0 | zend_accel_store_interned_string(copy->filename); |
205 | 0 | } |
206 | 22 | if (copy->name) { |
207 | 0 | zend_accel_store_interned_string(copy->name); |
208 | 0 | } |
209 | 22 | copy->args = zend_persist_ast(copy->args); |
210 | 22 | node = (zend_ast *) copy; |
211 | 274 | } else if (zend_ast_is_decl(ast)) { |
212 | | /* Not implemented. */ |
213 | 0 | ZEND_UNREACHABLE(); |
214 | 274 | } else { |
215 | 274 | uint32_t children = zend_ast_get_num_children(ast); |
216 | 274 | node = zend_shared_memdup(ast, zend_ast_size(children)); |
217 | 732 | for (i = 0; i < children; i++) { |
218 | 458 | if (node->child[i]) { |
219 | 448 | node->child[i] = zend_persist_ast(node->child[i]); |
220 | 448 | } |
221 | 458 | } |
222 | 274 | } |
223 | | |
224 | 714 | return node; |
225 | 714 | } |
226 | | |
227 | | static void zend_persist_zval(zval *z) |
228 | 114k | { |
229 | 114k | void *new_ptr; |
230 | | |
231 | 114k | switch (Z_TYPE_P(z)) { |
232 | 68.9k | case IS_STRING: |
233 | 68.9k | zend_accel_store_interned_string(Z_STR_P(z)); |
234 | 68.9k | Z_TYPE_FLAGS_P(z) = 0; |
235 | 68.9k | break; |
236 | 487 | case IS_ARRAY: |
237 | 487 | new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z)); |
238 | 487 | if (new_ptr) { |
239 | 4 | Z_ARR_P(z) = new_ptr; |
240 | 4 | Z_TYPE_FLAGS_P(z) = 0; |
241 | 483 | } else if (!ZCG(current_persistent_script)->corrupted |
242 | 483 | && zend_accel_in_shm(Z_ARR_P(z))) { |
243 | | /* pass */ |
244 | 483 | } else { |
245 | 483 | HashTable *ht; |
246 | | |
247 | 483 | if (!Z_REFCOUNTED_P(z)) { |
248 | 168 | ht = zend_shared_memdup_put(Z_ARR_P(z), sizeof(zend_array)); |
249 | 315 | } else { |
250 | 315 | GC_REMOVE_FROM_BUFFER(Z_ARR_P(z)); |
251 | 315 | ht = zend_shared_memdup_put_free(Z_ARR_P(z), sizeof(zend_array)); |
252 | 315 | } |
253 | 483 | Z_ARR_P(z) = ht; |
254 | 483 | zend_hash_persist(ht); |
255 | 483 | if (HT_IS_PACKED(ht)) { |
256 | 257 | zval *zv; |
257 | | |
258 | 1.23k | ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) { |
259 | 1.23k | zend_persist_zval(zv); |
260 | 1.23k | } ZEND_HASH_FOREACH_END(); |
261 | 257 | } else { |
262 | 226 | Bucket *p; |
263 | | |
264 | 644 | ZEND_HASH_MAP_FOREACH_BUCKET(ht, p) { |
265 | 644 | if (p->key) { |
266 | 44 | zend_accel_store_interned_string(p->key); |
267 | 44 | } |
268 | 644 | zend_persist_zval(&p->val); |
269 | 644 | } ZEND_HASH_FOREACH_END(); |
270 | 226 | } |
271 | | /* make immutable array */ |
272 | 483 | Z_TYPE_FLAGS_P(z) = 0; |
273 | 483 | GC_SET_REFCOUNT(Z_COUNTED_P(z), 2); |
274 | 483 | GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE); |
275 | 483 | } |
276 | 487 | break; |
277 | 487 | case IS_CONSTANT_AST: |
278 | 210 | new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z)); |
279 | 210 | if (new_ptr) { |
280 | 0 | Z_AST_P(z) = new_ptr; |
281 | 0 | Z_TYPE_FLAGS_P(z) = 0; |
282 | 210 | } else if (ZCG(current_persistent_script)->corrupted |
283 | 210 | || !zend_accel_in_shm(Z_AST_P(z))) { |
284 | 210 | zend_ast_ref *old_ref = Z_AST_P(z); |
285 | 210 | Z_AST_P(z) = zend_shared_memdup_put(Z_AST_P(z), sizeof(zend_ast_ref)); |
286 | 210 | zend_persist_ast(GC_AST(old_ref)); |
287 | 210 | Z_TYPE_FLAGS_P(z) = 0; |
288 | 210 | GC_SET_REFCOUNT(Z_COUNTED_P(z), 1); |
289 | 210 | GC_ADD_FLAGS(Z_COUNTED_P(z), GC_IMMUTABLE); |
290 | 210 | efree(old_ref); |
291 | 210 | } |
292 | 210 | break; |
293 | 0 | case IS_PTR: |
294 | 0 | break; |
295 | 45.2k | default: |
296 | 45.2k | ZEND_ASSERT(Z_TYPE_P(z) < IS_STRING); |
297 | 45.2k | break; |
298 | 114k | } |
299 | 114k | } |
300 | | |
301 | | static HashTable *zend_persist_attributes(HashTable *attributes) |
302 | 32 | { |
303 | 32 | uint32_t i; |
304 | 32 | zval *v; |
305 | | |
306 | 32 | if (!ZCG(current_persistent_script)->corrupted && zend_accel_in_shm(attributes)) { |
307 | 0 | return attributes; |
308 | 0 | } |
309 | | |
310 | | /* Attributes for trait properties may be shared if preloading is used. */ |
311 | 32 | HashTable *xlat = zend_shared_alloc_get_xlat_entry(attributes); |
312 | 32 | if (xlat) { |
313 | 0 | return xlat; |
314 | 0 | } |
315 | | |
316 | 32 | zend_hash_persist(attributes); |
317 | | |
318 | 136 | ZEND_HASH_PACKED_FOREACH_VAL(attributes, v) { |
319 | 136 | zend_attribute *attr = Z_PTR_P(v); |
320 | 136 | zend_attribute *copy = zend_shared_memdup_put_free(attr, ZEND_ATTRIBUTE_SIZE(attr->argc)); |
321 | | |
322 | 136 | zend_accel_store_interned_string(copy->name); |
323 | 136 | zend_accel_store_interned_string(copy->lcname); |
324 | 136 | if (copy->validation_error) { |
325 | 0 | zend_accel_store_interned_string(copy->validation_error); |
326 | 0 | } |
327 | | |
328 | 136 | for (i = 0; i < copy->argc; i++) { |
329 | 2 | if (copy->args[i].name) { |
330 | 0 | zend_accel_store_interned_string(copy->args[i].name); |
331 | 0 | } |
332 | 2 | zend_persist_zval(©->args[i].value); |
333 | 2 | } |
334 | | |
335 | 136 | ZVAL_PTR(v, copy); |
336 | 136 | } ZEND_HASH_FOREACH_END(); |
337 | | |
338 | 32 | HashTable *ptr = zend_shared_memdup_put_free(attributes, sizeof(HashTable)); |
339 | 32 | GC_SET_REFCOUNT(ptr, 2); |
340 | 32 | GC_TYPE_INFO(ptr) = GC_ARRAY | ((IS_ARRAY_IMMUTABLE|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT); |
341 | | |
342 | 32 | return ptr; |
343 | 32 | } |
344 | | |
345 | | /* We delay the publishing of CE cache slots until after ZCSG(map_ptr_last) is |
346 | | * published. |
347 | | * CE cache slots stored in interned strings are immediately visible to other |
348 | | * processes, so if we fail to update ZCSG(map_ptr_last) after publishing a |
349 | | * slot (due to a bailout/exit), that slot may be re-allocated for something |
350 | | * else by other processes. */ |
351 | | void zend_accel_pending_ce_cache_begin(void) |
352 | 25.3k | { |
353 | 25.3k | ZEND_ASSERT(ZCG(locked)); |
354 | | |
355 | | /* A persist window left through a bailout caught by the caller (e.g. |
356 | | * opcache_compile_file()) never published its slots. Drop them. */ |
357 | 25.3k | zend_accel_pending_ce_cache_discard(); |
358 | | |
359 | 25.3k | ZCG(pending_ce_cache) = emalloc(sizeof(HashTable)); |
360 | 25.3k | zend_hash_init(ZCG(pending_ce_cache), 8, NULL, NULL, 0); |
361 | 25.3k | } |
362 | | |
363 | | void zend_accel_pending_ce_cache_commit(void) |
364 | 25.3k | { |
365 | 25.3k | ZEND_ASSERT(ZCG(pending_ce_cache)); |
366 | | /* Every pending slot must be covered by ZCSG(map_ptr_last) */ |
367 | 25.3k | ZEND_ASSERT(ZCSG(map_ptr_last) == CG(map_ptr_last)); |
368 | | |
369 | 25.6k | ZEND_HASH_FOREACH_NUM_KEY_VAL(ZCG(pending_ce_cache), zend_ulong h, zval *zv) { |
370 | 25.6k | zend_string *type_name = (zend_string*)(uintptr_t)h; |
371 | | |
372 | 25.6k | ZEND_ASSERT(!ZSTR_HAS_CE_CACHE(type_name)); |
373 | 25.6k | GC_SET_REFCOUNT(type_name, (uint32_t)Z_LVAL_P(zv)); |
374 | 139 | atomic_thread_fence(memory_order_release); |
375 | 139 | GC_ADD_FLAGS(type_name, IS_STR_CLASS_NAME_MAP_PTR); |
376 | 139 | } ZEND_HASH_FOREACH_END(); |
377 | | |
378 | 25.3k | zend_accel_pending_ce_cache_discard(); |
379 | 25.3k | } |
380 | | |
381 | | void zend_accel_pending_ce_cache_discard(void) |
382 | 357k | { |
383 | 357k | if (ZCG(pending_ce_cache)) { |
384 | 25.3k | zend_hash_destroy(ZCG(pending_ce_cache)); |
385 | 25.3k | efree(ZCG(pending_ce_cache)); |
386 | 25.3k | ZCG(pending_ce_cache) = NULL; |
387 | 25.3k | } |
388 | 357k | } |
389 | | |
390 | | void zend_accel_get_class_name_map_ptr(zend_string *type_name) |
391 | 502 | { |
392 | 502 | uint32_t ret; |
393 | 502 | zval *slot; |
394 | | |
395 | 502 | if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_SELF)) || |
396 | 502 | zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_PARENT))) { |
397 | 0 | return; |
398 | 0 | } |
399 | | |
400 | | /* We use type.name.gc.refcount to keep map_ptr of corresponding type */ |
401 | 502 | if (ZSTR_HAS_CE_CACHE(type_name)) { |
402 | 363 | return; |
403 | 363 | } |
404 | | |
405 | 139 | if ((GC_FLAGS(type_name) & GC_IMMUTABLE) |
406 | 139 | && (GC_FLAGS(type_name) & IS_STR_PERMANENT)) { |
407 | 139 | ZEND_ASSERT(ZCG(pending_ce_cache) && "Must be called inside a persist window"); |
408 | 139 | slot = zend_hash_index_lookup(ZCG(pending_ce_cache), (zend_ulong)(uintptr_t)type_name); |
409 | 139 | if (Z_TYPE_P(slot) != IS_NULL) { |
410 | | /* Already allocated earlier in this window */ |
411 | 0 | return; |
412 | 0 | } |
413 | 139 | do { |
414 | 139 | ret = ZEND_MAP_PTR_NEW_OFFSET(); |
415 | 139 | } while (ret <= 2); |
416 | 139 | ZVAL_LONG(slot, ret); |
417 | 139 | } |
418 | 139 | } |
419 | | |
420 | 368 | static void zend_persist_type(zend_type *type) { |
421 | 368 | if (ZEND_TYPE_HAS_LIST(*type)) { |
422 | 28 | zend_type_list *list = ZEND_TYPE_LIST(*type); |
423 | 28 | if (ZEND_TYPE_USES_ARENA(*type) || zend_accel_in_shm(list)) { |
424 | 28 | list = zend_shared_memdup_put(list, ZEND_TYPE_LIST_SIZE(list->num_types)); |
425 | 28 | ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT; |
426 | 28 | } else { |
427 | 0 | list = zend_shared_memdup_put_free(list, ZEND_TYPE_LIST_SIZE(list->num_types)); |
428 | 0 | } |
429 | 28 | ZEND_TYPE_SET_PTR(*type, list); |
430 | 28 | } |
431 | | |
432 | 368 | zend_type *single_type; |
433 | 776 | ZEND_TYPE_FOREACH_MUTABLE(*type, single_type) { |
434 | 776 | if (ZEND_TYPE_HAS_LIST(*single_type)) { |
435 | 0 | zend_persist_type(single_type); |
436 | 0 | continue; |
437 | 0 | } |
438 | 408 | if (ZEND_TYPE_HAS_NAME(*single_type)) { |
439 | 160 | zend_string *type_name = ZEND_TYPE_NAME(*single_type); |
440 | 160 | zend_accel_store_interned_string(type_name); |
441 | 160 | ZEND_TYPE_SET_PTR(*single_type, type_name); |
442 | 160 | if (!ZCG(current_persistent_script)->corrupted) { |
443 | 160 | zend_accel_get_class_name_map_ptr(type_name); |
444 | 160 | } |
445 | 160 | } |
446 | 408 | } ZEND_TYPE_FOREACH_END(); |
447 | 368 | } |
448 | | |
449 | | static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script) |
450 | 25.9k | { |
451 | 25.9k | zend_op *persist_ptr; |
452 | 25.9k | zval *orig_literals = NULL; |
453 | | |
454 | 25.9k | if (op_array->refcount && --(*op_array->refcount) == 0) { |
455 | 25.9k | efree(op_array->refcount); |
456 | 25.9k | } |
457 | 25.9k | op_array->refcount = NULL; |
458 | | |
459 | 25.9k | if (main_persistent_script) { |
460 | 25.3k | zend_execute_data *orig_execute_data = EG(current_execute_data); |
461 | 25.3k | zend_execute_data fake_execute_data; |
462 | 25.3k | zval *offset; |
463 | | |
464 | 25.3k | memset(&fake_execute_data, 0, sizeof(fake_execute_data)); |
465 | 25.3k | fake_execute_data.func = (zend_function*)op_array; |
466 | 25.3k | EG(current_execute_data) = &fake_execute_data; |
467 | 25.3k | if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) { |
468 | 0 | main_persistent_script->compiler_halt_offset = Z_LVAL_P(offset); |
469 | 0 | } |
470 | 25.3k | EG(current_execute_data) = orig_execute_data; |
471 | 25.3k | } |
472 | | |
473 | 25.9k | if (op_array->function_name) { |
474 | 630 | zend_string *old_name = op_array->function_name; |
475 | 630 | zend_accel_store_interned_string(op_array->function_name); |
476 | | /* Remember old function name, so it can be released multiple times if shared. */ |
477 | 630 | if (op_array->function_name != old_name |
478 | 0 | && !zend_shared_alloc_get_xlat_entry(&op_array->function_name)) { |
479 | 0 | zend_shared_alloc_register_xlat_entry(&op_array->function_name, old_name); |
480 | 0 | } |
481 | 630 | } |
482 | | |
483 | 25.9k | if (op_array->scope) { |
484 | 0 | zend_class_entry *scope = zend_shared_alloc_get_xlat_entry(op_array->scope); |
485 | |
|
486 | 0 | if (scope) { |
487 | 0 | op_array->scope = scope; |
488 | 0 | } |
489 | |
|
490 | 0 | if (op_array->prototype) { |
491 | 0 | zend_function *ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype); |
492 | |
|
493 | 0 | if (ptr) { |
494 | 0 | op_array->prototype = ptr; |
495 | 0 | } |
496 | 0 | } |
497 | |
|
498 | 0 | persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes); |
499 | 0 | if (persist_ptr) { |
500 | 0 | op_array->opcodes = persist_ptr; |
501 | 0 | if (op_array->static_variables) { |
502 | 0 | op_array->static_variables = zend_shared_alloc_get_xlat_entry(op_array->static_variables); |
503 | 0 | ZEND_ASSERT(op_array->static_variables != NULL); |
504 | 0 | } |
505 | 0 | if (op_array->literals) { |
506 | 0 | op_array->literals = zend_shared_alloc_get_xlat_entry(op_array->literals); |
507 | 0 | ZEND_ASSERT(op_array->literals != NULL); |
508 | 0 | } |
509 | 0 | if (op_array->filename) { |
510 | 0 | op_array->filename = zend_shared_alloc_get_xlat_entry(op_array->filename); |
511 | 0 | ZEND_ASSERT(op_array->filename != NULL); |
512 | 0 | } |
513 | 0 | if (op_array->arg_info) { |
514 | 0 | zend_arg_info *arg_info = op_array->arg_info; |
515 | 0 | if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
516 | 0 | arg_info--; |
517 | 0 | } |
518 | 0 | arg_info = zend_shared_alloc_get_xlat_entry(arg_info); |
519 | 0 | ZEND_ASSERT(arg_info != NULL); |
520 | 0 | if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
521 | 0 | arg_info++; |
522 | 0 | } |
523 | 0 | op_array->arg_info = arg_info; |
524 | 0 | } |
525 | 0 | if (op_array->live_range) { |
526 | 0 | op_array->live_range = zend_shared_alloc_get_xlat_entry(op_array->live_range); |
527 | 0 | ZEND_ASSERT(op_array->live_range != NULL); |
528 | 0 | } |
529 | 0 | if (op_array->doc_comment) { |
530 | 0 | if (ZCG(accel_directives).save_comments) { |
531 | 0 | op_array->doc_comment = zend_shared_alloc_get_xlat_entry(op_array->doc_comment); |
532 | 0 | ZEND_ASSERT(op_array->doc_comment != NULL); |
533 | 0 | } else { |
534 | 0 | op_array->doc_comment = NULL; |
535 | 0 | } |
536 | 0 | } |
537 | 0 | if (op_array->attributes) { |
538 | 0 | op_array->attributes = zend_shared_alloc_get_xlat_entry(op_array->attributes); |
539 | 0 | ZEND_ASSERT(op_array->attributes != NULL); |
540 | 0 | } |
541 | |
|
542 | 0 | if (op_array->try_catch_array) { |
543 | 0 | op_array->try_catch_array = zend_shared_alloc_get_xlat_entry(op_array->try_catch_array); |
544 | 0 | ZEND_ASSERT(op_array->try_catch_array != NULL); |
545 | 0 | } |
546 | 0 | if (op_array->vars) { |
547 | 0 | op_array->vars = zend_shared_alloc_get_xlat_entry(op_array->vars); |
548 | 0 | ZEND_ASSERT(op_array->vars != NULL); |
549 | 0 | } |
550 | 0 | if (op_array->dynamic_func_defs) { |
551 | 0 | op_array->dynamic_func_defs = zend_shared_alloc_get_xlat_entry(op_array->dynamic_func_defs); |
552 | 0 | ZEND_ASSERT(op_array->dynamic_func_defs != NULL); |
553 | 0 | } |
554 | 0 | ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem)))); |
555 | 0 | return; |
556 | 0 | } |
557 | 25.9k | } else { |
558 | | /* "prototype" may be undefined if "scope" isn't set */ |
559 | 25.9k | op_array->prototype = NULL; |
560 | 25.9k | } |
561 | | |
562 | 25.9k | if (op_array->scope |
563 | 0 | && !(op_array->fn_flags & ZEND_ACC_CLOSURE) |
564 | 0 | && (op_array->scope->ce_flags & ZEND_ACC_CACHED)) { |
565 | 0 | return; |
566 | 0 | } |
567 | | |
568 | 25.9k | if (op_array->static_variables && !zend_accel_in_shm(op_array->static_variables)) { |
569 | 32 | Bucket *p; |
570 | | |
571 | 32 | zend_hash_persist(op_array->static_variables); |
572 | 136 | ZEND_HASH_MAP_FOREACH_BUCKET(op_array->static_variables, p) { |
573 | 136 | ZEND_ASSERT(p->key != NULL); |
574 | 136 | zend_accel_store_interned_string(p->key); |
575 | 36 | zend_persist_zval(&p->val); |
576 | 36 | } ZEND_HASH_FOREACH_END(); |
577 | 32 | op_array->static_variables = zend_shared_memdup_put_free(op_array->static_variables, sizeof(HashTable)); |
578 | | /* make immutable array */ |
579 | 32 | GC_SET_REFCOUNT(op_array->static_variables, 2); |
580 | 32 | GC_TYPE_INFO(op_array->static_variables) = GC_ARRAY | ((IS_ARRAY_IMMUTABLE|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT); |
581 | 32 | } |
582 | | |
583 | 25.9k | if (op_array->literals) { |
584 | 25.7k | zval *p, *end; |
585 | | |
586 | 25.7k | orig_literals = op_array->literals; |
587 | | #if ZEND_USE_ABS_CONST_ADDR |
588 | | p = zend_shared_memdup_put_free(op_array->literals, sizeof(zval) * op_array->last_literal); |
589 | | #else |
590 | 25.7k | p = zend_shared_memdup_put(op_array->literals, sizeof(zval) * op_array->last_literal); |
591 | 25.7k | #endif |
592 | 25.7k | end = p + op_array->last_literal; |
593 | 25.7k | op_array->literals = p; |
594 | 139k | while (p < end) { |
595 | 113k | zend_persist_zval(p); |
596 | 113k | p++; |
597 | 113k | } |
598 | 25.7k | } |
599 | | |
600 | 25.9k | { |
601 | 25.9k | zend_op *new_opcodes = zend_shared_memdup_put(op_array->opcodes, sizeof(zend_op) * op_array->last); |
602 | 25.9k | zend_op *opline = new_opcodes; |
603 | 25.9k | zend_op *end = new_opcodes + op_array->last; |
604 | | |
605 | 175k | for (; opline < end ; opline++) { |
606 | | #if ZEND_USE_ABS_CONST_ADDR |
607 | | if (opline->op1_type == IS_CONST) { |
608 | | opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals)); |
609 | | if (opline->opcode == ZEND_SEND_VAL |
610 | | || opline->opcode == ZEND_SEND_VAL_EX |
611 | | || opline->opcode == ZEND_QM_ASSIGN) { |
612 | | /* Update handlers to eliminate REFCOUNTED check */ |
613 | | zend_vm_set_opcode_handler_ex(opline, 1 << Z_TYPE_P(opline->op1.zv), 0, 0); |
614 | | } |
615 | | } |
616 | | if (opline->op2_type == IS_CONST) { |
617 | | opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals)); |
618 | | } |
619 | | #else |
620 | 149k | if (opline->op1_type == IS_CONST) { |
621 | 59.7k | opline->op1.constant = |
622 | 59.7k | (char*)(op_array->literals + |
623 | 59.7k | ((zval*)((char*)(op_array->opcodes + (opline - new_opcodes)) + |
624 | 59.7k | (int32_t)opline->op1.constant) - orig_literals)) - |
625 | 59.7k | (char*)opline; |
626 | 59.7k | if (opline->opcode == ZEND_SEND_VAL |
627 | 53.1k | || opline->opcode == ZEND_SEND_VAL_EX |
628 | 52.6k | || opline->opcode == ZEND_QM_ASSIGN) { |
629 | 7.32k | zend_vm_set_opcode_handler_ex(opline, 0, 0, 0); |
630 | 7.32k | } |
631 | 59.7k | } |
632 | 149k | if (opline->op2_type == IS_CONST) { |
633 | 39.5k | opline->op2.constant = |
634 | 39.5k | (char*)(op_array->literals + |
635 | 39.5k | ((zval*)((char*)(op_array->opcodes + (opline - new_opcodes)) + |
636 | 39.5k | (int32_t)opline->op2.constant) - orig_literals)) - |
637 | 39.5k | (char*)opline; |
638 | 39.5k | } |
639 | 149k | #endif |
640 | | #if ZEND_USE_ABS_JMP_ADDR |
641 | | if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) { |
642 | | /* fix jumps to point to new array */ |
643 | | switch (opline->opcode) { |
644 | | case ZEND_JMP: |
645 | | case ZEND_FAST_CALL: |
646 | | opline->op1.jmp_addr = &new_opcodes[opline->op1.jmp_addr - op_array->opcodes]; |
647 | | break; |
648 | | case ZEND_JMPZ: |
649 | | case ZEND_JMPNZ: |
650 | | case ZEND_JMPZ_EX: |
651 | | case ZEND_JMPNZ_EX: |
652 | | case ZEND_JMP_SET: |
653 | | case ZEND_COALESCE: |
654 | | case ZEND_FE_RESET_R: |
655 | | case ZEND_FE_RESET_RW: |
656 | | case ZEND_ASSERT_CHECK: |
657 | | case ZEND_JMP_NULL: |
658 | | case ZEND_BIND_INIT_STATIC_OR_JMP: |
659 | | case ZEND_JMP_FRAMELESS: |
660 | | opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes]; |
661 | | break; |
662 | | case ZEND_CATCH: |
663 | | if (!(opline->extended_value & ZEND_LAST_CATCH)) { |
664 | | opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes]; |
665 | | } |
666 | | break; |
667 | | case ZEND_FE_FETCH_R: |
668 | | case ZEND_FE_FETCH_RW: |
669 | | case ZEND_SWITCH_LONG: |
670 | | case ZEND_SWITCH_STRING: |
671 | | case ZEND_MATCH: |
672 | | /* relative extended_value don't have to be changed */ |
673 | | break; |
674 | | } |
675 | | } |
676 | | #endif |
677 | 149k | if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) { |
678 | 0 | zval *literal = RT_CONSTANT(opline, opline->op1); |
679 | 0 | HashTable *attributes = Z_PTR_P(literal); |
680 | 0 | attributes = zend_persist_attributes(attributes); |
681 | 0 | ZVAL_PTR(literal, attributes); |
682 | 0 | } |
683 | | |
684 | 149k | if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { |
685 | 58 | if (!ZCG(current_persistent_script)->corrupted) { |
686 | 58 | opline->extended_value |= ZEND_PARTIAL_CACHEABLE_IN_SHM; |
687 | 58 | } |
688 | 58 | } |
689 | 149k | } |
690 | | |
691 | 25.9k | efree(op_array->opcodes); |
692 | 25.9k | op_array->opcodes = new_opcodes; |
693 | 25.9k | } |
694 | | |
695 | 25.9k | if (op_array->filename) { |
696 | 25.9k | zend_accel_store_string(op_array->filename); |
697 | 25.9k | } |
698 | | |
699 | 25.9k | if (op_array->arg_info) { |
700 | 200 | zend_arg_info *arg_info = op_array->arg_info; |
701 | 200 | uint32_t num_args = op_array->num_args; |
702 | 200 | uint32_t i; |
703 | | |
704 | 200 | if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
705 | 84 | arg_info--; |
706 | 84 | num_args++; |
707 | 84 | } |
708 | 200 | if (op_array->fn_flags & ZEND_ACC_VARIADIC) { |
709 | 0 | num_args++; |
710 | 0 | } |
711 | 200 | arg_info = zend_shared_memdup_put_free(arg_info, sizeof(zend_arg_info) * num_args); |
712 | 448 | for (i = 0; i < num_args; i++) { |
713 | 248 | if (arg_info[i].name) { |
714 | 164 | zend_accel_store_interned_string(arg_info[i].name); |
715 | 164 | } |
716 | 248 | zend_persist_type(&arg_info[i].type); |
717 | 248 | if (arg_info[i].doc_comment) { |
718 | 0 | zend_accel_store_interned_string(arg_info[i].doc_comment); |
719 | 0 | } |
720 | 248 | } |
721 | 200 | if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
722 | 84 | arg_info++; |
723 | 84 | } |
724 | 200 | op_array->arg_info = arg_info; |
725 | 200 | } |
726 | | |
727 | 25.9k | if (op_array->live_range) { |
728 | 10.1k | op_array->live_range = zend_shared_memdup_put_free(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range); |
729 | 10.1k | } |
730 | | |
731 | 25.9k | if (op_array->doc_comment) { |
732 | 0 | if (ZCG(accel_directives).save_comments) { |
733 | 0 | zend_accel_store_interned_string(op_array->doc_comment); |
734 | 0 | } else { |
735 | 0 | zend_string_release_ex(op_array->doc_comment, 0); |
736 | 0 | op_array->doc_comment = NULL; |
737 | 0 | } |
738 | 0 | } |
739 | | |
740 | 25.9k | if (op_array->attributes) { |
741 | 6 | op_array->attributes = zend_persist_attributes(op_array->attributes); |
742 | 6 | } |
743 | | |
744 | 25.9k | if (op_array->try_catch_array) { |
745 | 0 | op_array->try_catch_array = zend_shared_memdup_put_free(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); |
746 | 0 | } |
747 | | |
748 | 25.9k | if (op_array->vars) { |
749 | 6.30k | int i; |
750 | 6.30k | op_array->vars = zend_shared_memdup_put_free(op_array->vars, sizeof(zend_string*) * op_array->last_var); |
751 | 16.7k | for (i = 0; i < op_array->last_var; i++) { |
752 | 10.4k | zend_accel_store_interned_string(op_array->vars[i]); |
753 | 10.4k | } |
754 | 6.30k | } |
755 | | |
756 | 25.9k | if (op_array->num_dynamic_func_defs) { |
757 | 214 | op_array->dynamic_func_defs = zend_shared_memdup_put_free( |
758 | 214 | op_array->dynamic_func_defs, sizeof(zend_function *) * op_array->num_dynamic_func_defs); |
759 | 428 | for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { |
760 | 214 | zval tmp; |
761 | 214 | ZVAL_PTR(&tmp, op_array->dynamic_func_defs[i]); |
762 | 214 | zend_persist_op_array(&tmp); |
763 | 214 | op_array->dynamic_func_defs[i] = Z_PTR(tmp); |
764 | 214 | } |
765 | 214 | } |
766 | | |
767 | 25.9k | ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem)))); |
768 | 25.9k | } |
769 | | |
770 | | static void zend_persist_op_array(zval *zv) |
771 | 630 | { |
772 | 630 | zend_op_array *op_array = Z_PTR_P(zv); |
773 | 630 | zend_op_array *old_op_array; |
774 | 630 | ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); |
775 | | |
776 | 630 | old_op_array = zend_shared_alloc_get_xlat_entry(op_array); |
777 | 630 | if (!old_op_array) { |
778 | 630 | op_array = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_op_array)); |
779 | 630 | zend_persist_op_array_ex(op_array, NULL); |
780 | 630 | if (!ZCG(current_persistent_script)->corrupted) { |
781 | 630 | op_array->fn_flags |= ZEND_ACC_IMMUTABLE; |
782 | 630 | ZEND_MAP_PTR_NEW(op_array->run_time_cache); |
783 | 630 | if (op_array->static_variables) { |
784 | 14 | ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); |
785 | 14 | } |
786 | 630 | } |
787 | 630 | #ifdef HAVE_JIT |
788 | 630 | if (JIT_G(on) |
789 | 312 | && JIT_G(opt_level) <= ZEND_JIT_LEVEL_OPT_FUNCS |
790 | 161 | && (!ZCG(current_persistent_script) |
791 | 161 | || !ZCG(current_persistent_script)->corrupted)) { |
792 | 161 | zend_jit_op_array(op_array, ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); |
793 | 161 | } |
794 | 630 | #endif |
795 | 630 | } else { |
796 | | /* This can happen during preloading, if a dynamic function definition is declared. */ |
797 | 0 | Z_PTR_P(zv) = old_op_array; |
798 | 0 | } |
799 | 630 | } |
800 | | |
801 | | static zend_op_array *zend_persist_class_method(zend_op_array *op_array, const zend_class_entry *ce) |
802 | 0 | { |
803 | 0 | zend_op_array *old_op_array; |
804 | |
|
805 | 0 | if (op_array->type != ZEND_USER_FUNCTION) { |
806 | 0 | ZEND_ASSERT(op_array->type == ZEND_INTERNAL_FUNCTION); |
807 | 0 | if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) { |
808 | 0 | old_op_array = zend_shared_alloc_get_xlat_entry(op_array); |
809 | 0 | if (old_op_array) { |
810 | 0 | return old_op_array; |
811 | 0 | } else { |
812 | 0 | op_array = zend_shared_memdup_put(op_array, sizeof(zend_internal_function)); |
813 | 0 | if (op_array->scope) { |
814 | 0 | void *persist_ptr; |
815 | |
|
816 | 0 | if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->scope))) { |
817 | 0 | op_array->scope = (zend_class_entry*)persist_ptr; |
818 | 0 | } |
819 | 0 | if (op_array->prototype) { |
820 | 0 | if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { |
821 | 0 | op_array->prototype = (zend_function*)persist_ptr; |
822 | 0 | } |
823 | 0 | } |
824 | 0 | } |
825 | | // Real dynamically created internal functions like enum methods must have their own run_time_cache pointer. They're always on the same scope as their defining class. |
826 | | // However, copies - as caused by inheritance of internal methods - must retain the original run_time_cache pointer, shared with the source function. |
827 | 0 | if (!op_array->scope || (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE))) { |
828 | 0 | if (op_array->fn_flags & ZEND_ACC_PRELOADED) { |
829 | 0 | ZEND_MAP_PTR_NEW_STATIC(op_array->run_time_cache); |
830 | 0 | } else { |
831 | 0 | ZEND_MAP_PTR_NEW(op_array->run_time_cache); |
832 | 0 | } |
833 | 0 | } |
834 | 0 | } |
835 | 0 | } |
836 | 0 | return op_array; |
837 | 0 | } |
838 | | |
839 | 0 | if ((op_array->fn_flags & ZEND_ACC_IMMUTABLE) |
840 | 0 | && !ZCG(current_persistent_script)->corrupted |
841 | 0 | && zend_accel_in_shm(op_array)) { |
842 | 0 | zend_shared_alloc_register_xlat_entry(op_array, op_array); |
843 | 0 | return op_array; |
844 | 0 | } |
845 | | |
846 | 0 | old_op_array = zend_shared_alloc_get_xlat_entry(op_array); |
847 | 0 | if (old_op_array) { |
848 | 0 | if (op_array->refcount && --(*op_array->refcount) == 0) { |
849 | 0 | efree(op_array->refcount); |
850 | 0 | } |
851 | | |
852 | | /* If op_array is shared, the function name refcount is still incremented for each use, |
853 | | * so we need to release it here. We remembered the original function name in xlat. */ |
854 | 0 | zend_string *old_function_name = |
855 | 0 | zend_shared_alloc_get_xlat_entry(&old_op_array->function_name); |
856 | 0 | if (old_function_name) { |
857 | 0 | zend_string_release_ex(old_function_name, 0); |
858 | 0 | } |
859 | 0 | return old_op_array; |
860 | 0 | } |
861 | | |
862 | 0 | op_array = zend_shared_memdup_put(op_array, sizeof(zend_op_array)); |
863 | 0 | zend_persist_op_array_ex(op_array, NULL); |
864 | 0 | if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { |
865 | 0 | op_array->fn_flags |= ZEND_ACC_IMMUTABLE; |
866 | 0 | if (ce->ce_flags & ZEND_ACC_LINKED) { |
867 | 0 | ZEND_MAP_PTR_NEW(op_array->run_time_cache); |
868 | 0 | if (op_array->static_variables) { |
869 | 0 | ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); |
870 | 0 | } |
871 | 0 | } else { |
872 | 0 | ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); |
873 | 0 | ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL); |
874 | 0 | } |
875 | 0 | } |
876 | 0 | return op_array; |
877 | 0 | } |
878 | | |
879 | | static zend_property_info *zend_persist_property_info(zend_property_info *prop) |
880 | 118 | { |
881 | 118 | zend_class_entry *ce; |
882 | 118 | prop = zend_shared_memdup_put(prop, sizeof(zend_property_info)); |
883 | 118 | ce = zend_shared_alloc_get_xlat_entry(prop->ce); |
884 | 118 | if (ce) { |
885 | 118 | prop->ce = ce; |
886 | 118 | } |
887 | 118 | zend_accel_store_interned_string(prop->name); |
888 | 118 | if (prop->doc_comment) { |
889 | 0 | if (ZCG(accel_directives).save_comments) { |
890 | 0 | zend_accel_store_interned_string(prop->doc_comment); |
891 | 0 | } else { |
892 | 0 | if (!zend_shared_alloc_get_xlat_entry(prop->doc_comment)) { |
893 | 0 | zend_shared_alloc_register_xlat_entry(prop->doc_comment, prop->doc_comment); |
894 | 0 | } |
895 | 0 | zend_string_release_ex(prop->doc_comment, 0); |
896 | 0 | prop->doc_comment = NULL; |
897 | 0 | } |
898 | 0 | } |
899 | 118 | if (prop->attributes) { |
900 | 0 | prop->attributes = zend_persist_attributes(prop->attributes); |
901 | 0 | } |
902 | 118 | if (prop->prototype) { |
903 | 118 | const zend_property_info *new_prototype = (const zend_property_info *) zend_shared_alloc_get_xlat_entry(prop->prototype); |
904 | 118 | if (new_prototype) { |
905 | 118 | prop->prototype = new_prototype; |
906 | 118 | } |
907 | 118 | } |
908 | 118 | if (prop->hooks) { |
909 | 0 | prop->hooks = zend_shared_memdup_put(prop->hooks, ZEND_PROPERTY_HOOK_STRUCT_SIZE); |
910 | 0 | for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { |
911 | 0 | if (prop->hooks[i]) { |
912 | 0 | zend_op_array *hook = zend_persist_class_method(&prop->hooks[i]->op_array, ce); |
913 | 0 | #ifdef HAVE_JIT |
914 | 0 | if (JIT_G(on) |
915 | 0 | && JIT_G(opt_level) <= ZEND_JIT_LEVEL_OPT_FUNCS |
916 | 0 | && (!ZCG(current_persistent_script) |
917 | 0 | || !ZCG(current_persistent_script)->corrupted)) { |
918 | 0 | if (hook->scope == ce && !(hook->fn_flags & ZEND_ACC_TRAIT_CLONE)) { |
919 | 0 | zend_jit_op_array(hook, ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); |
920 | 0 | } |
921 | 0 | } |
922 | 0 | #endif |
923 | 0 | const zend_property_info *new_prop_info = (const zend_property_info *) zend_shared_alloc_get_xlat_entry(hook->prop_info); |
924 | 0 | if (new_prop_info) { |
925 | 0 | hook->prop_info = new_prop_info; |
926 | 0 | } |
927 | 0 | prop->hooks[i] = (zend_function *) hook; |
928 | 0 | } |
929 | 0 | } |
930 | 0 | } |
931 | 118 | zend_persist_type(&prop->type); |
932 | 118 | return prop; |
933 | 118 | } |
934 | | |
935 | | static void zend_persist_class_constant(zval *zv) |
936 | 2 | { |
937 | 2 | const zend_class_constant *orig_c = Z_PTR_P(zv); |
938 | 2 | zend_class_constant *c = zend_shared_alloc_get_xlat_entry(orig_c); |
939 | 2 | zend_class_entry *ce; |
940 | | |
941 | 2 | if (c) { |
942 | 0 | Z_PTR_P(zv) = c; |
943 | 0 | return; |
944 | 2 | } else if (((orig_c->ce->ce_flags & ZEND_ACC_IMMUTABLE) && !(Z_CONSTANT_FLAGS(orig_c->value) & CONST_OWNED)) |
945 | 2 | || orig_c->ce->type == ZEND_INTERNAL_CLASS) { |
946 | | /* Class constant comes from a different file in shm or internal class, keep existing pointer. */ |
947 | 0 | return; |
948 | 2 | } else if (!ZCG(current_persistent_script)->corrupted |
949 | 2 | && zend_accel_in_shm(Z_PTR_P(zv))) { |
950 | 0 | return; |
951 | 0 | } |
952 | 2 | c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant)); |
953 | 2 | zend_persist_zval(&c->value); |
954 | 2 | ce = zend_shared_alloc_get_xlat_entry(c->ce); |
955 | 2 | if (ce) { |
956 | 2 | c->ce = ce; |
957 | 2 | } |
958 | 2 | if (c->doc_comment) { |
959 | 0 | if (ZCG(accel_directives).save_comments) { |
960 | 0 | zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment); |
961 | 0 | if (doc_comment) { |
962 | 0 | c->doc_comment = doc_comment; |
963 | 0 | } else { |
964 | 0 | zend_accel_store_interned_string(c->doc_comment); |
965 | 0 | } |
966 | 0 | } else { |
967 | 0 | zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment); |
968 | 0 | if (!doc_comment) { |
969 | 0 | zend_shared_alloc_register_xlat_entry(c->doc_comment, c->doc_comment); |
970 | 0 | zend_string_release_ex(c->doc_comment, 0); |
971 | 0 | } |
972 | 0 | c->doc_comment = NULL; |
973 | 0 | } |
974 | 0 | } |
975 | 2 | if (c->attributes) { |
976 | 0 | c->attributes = zend_persist_attributes(c->attributes); |
977 | 0 | } |
978 | 2 | zend_persist_type(&c->type); |
979 | 2 | } |
980 | | |
981 | | zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) |
982 | 374 | { |
983 | 374 | Bucket *p; |
984 | 374 | zend_class_entry *ce = orig_ce; |
985 | | |
986 | 374 | if (ce->type == ZEND_USER_CLASS) { |
987 | | /* The same zend_class_entry may be reused by class_alias */ |
988 | 374 | zend_class_entry *new_ce = zend_shared_alloc_get_xlat_entry(ce); |
989 | 374 | if (new_ce) { |
990 | 0 | return new_ce; |
991 | 0 | } |
992 | 374 | ce = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); |
993 | 374 | if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { |
994 | 374 | ce->ce_flags |= ZEND_ACC_IMMUTABLE; |
995 | 374 | if ((ce->ce_flags & ZEND_ACC_LINKED) |
996 | 216 | && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { |
997 | 4 | ZEND_MAP_PTR_NEW(ce->mutable_data); |
998 | 370 | } else { |
999 | 370 | ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); |
1000 | 370 | } |
1001 | 374 | } else { |
1002 | 0 | ce->ce_flags |= ZEND_ACC_FILE_CACHED; |
1003 | 0 | } |
1004 | 374 | ce->inheritance_cache = NULL; |
1005 | | |
1006 | 374 | if (!(ce->ce_flags & ZEND_ACC_CACHED)) { |
1007 | 374 | if (ZSTR_HAS_CE_CACHE(ce->name)) { |
1008 | 264 | ZSTR_SET_CE_CACHE_EX(ce->name, NULL, 0); |
1009 | 264 | } |
1010 | 374 | zend_accel_store_interned_string(ce->name); |
1011 | 374 | if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS) |
1012 | 342 | && !ZCG(current_persistent_script)->corrupted) { |
1013 | 342 | zend_accel_get_class_name_map_ptr(ce->name); |
1014 | 342 | } |
1015 | 374 | if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { |
1016 | 30 | zend_accel_store_interned_string(ce->parent_name); |
1017 | 30 | } |
1018 | 374 | } |
1019 | | |
1020 | 374 | zend_hash_persist(&ce->function_table); |
1021 | 748 | ZEND_HASH_MAP_FOREACH_BUCKET(&ce->function_table, p) { |
1022 | 748 | ZEND_ASSERT(p->key != NULL); |
1023 | 748 | zend_accel_store_interned_string(p->key); |
1024 | 0 | Z_PTR(p->val) = zend_persist_class_method(Z_PTR(p->val), ce); |
1025 | 0 | } ZEND_HASH_FOREACH_END(); |
1026 | 374 | HT_FLAGS(&ce->function_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); |
1027 | 374 | if (ce->default_properties_table) { |
1028 | 98 | int i; |
1029 | | |
1030 | 98 | ce->default_properties_table = zend_shared_memdup_free(ce->default_properties_table, sizeof(zval) * ce->default_properties_count); |
1031 | 204 | for (i = 0; i < ce->default_properties_count; i++) { |
1032 | 106 | zend_persist_zval(&ce->default_properties_table[i]); |
1033 | 106 | } |
1034 | 98 | } |
1035 | 374 | if (ce->default_static_members_table) { |
1036 | 12 | ce->default_static_members_table = zend_shared_memdup_free(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count); |
1037 | | |
1038 | | /* Persist only static properties in this class. |
1039 | | * Static properties from parent classes will be handled in class_copy_ctor and are marked with IS_INDIRECT */ |
1040 | 24 | for (uint32_t i = 0; i < ce->default_static_members_count; i++) { |
1041 | 12 | if (Z_TYPE(ce->default_static_members_table[i]) != IS_INDIRECT) { |
1042 | 12 | zend_persist_zval(&ce->default_static_members_table[i]); |
1043 | 12 | } |
1044 | 12 | } |
1045 | 12 | if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { |
1046 | 12 | if (ce->ce_flags & ZEND_ACC_LINKED) { |
1047 | 12 | ZEND_MAP_PTR_NEW(ce->static_members_table); |
1048 | 12 | } else { |
1049 | 0 | ZEND_MAP_PTR_INIT(ce->static_members_table, NULL); |
1050 | 0 | } |
1051 | 12 | } |
1052 | 12 | } |
1053 | | |
1054 | 374 | zend_hash_persist(&ce->constants_table); |
1055 | 752 | ZEND_HASH_MAP_FOREACH_BUCKET(&ce->constants_table, p) { |
1056 | 752 | ZEND_ASSERT(p->key != NULL); |
1057 | 752 | zend_accel_store_interned_string(p->key); |
1058 | 2 | zend_persist_class_constant(&p->val); |
1059 | 2 | } ZEND_HASH_FOREACH_END(); |
1060 | 374 | HT_FLAGS(&ce->constants_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); |
1061 | | |
1062 | 374 | zend_hash_persist(&ce->properties_info); |
1063 | 984 | ZEND_HASH_MAP_FOREACH_BUCKET(&ce->properties_info, p) { |
1064 | 984 | zend_property_info *prop = Z_PTR(p->val); |
1065 | 984 | ZEND_ASSERT(p->key != NULL); |
1066 | 984 | zend_accel_store_interned_string(p->key); |
1067 | 118 | if (prop->ce == orig_ce) { |
1068 | 118 | Z_PTR(p->val) = zend_persist_property_info(prop); |
1069 | 118 | } else { |
1070 | 0 | prop = zend_shared_alloc_get_xlat_entry(prop); |
1071 | 0 | if (prop) { |
1072 | 0 | Z_PTR(p->val) = prop; |
1073 | 0 | } else { |
1074 | | /* This can happen if preloading is used and we inherit a property from an |
1075 | | * internal class. In that case we should keep pointing to the internal |
1076 | | * property, without any adjustments. */ |
1077 | 0 | } |
1078 | 0 | } |
1079 | 118 | } ZEND_HASH_FOREACH_END(); |
1080 | 374 | HT_FLAGS(&ce->properties_info) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); |
1081 | | |
1082 | 374 | if (ce->properties_info_table) { |
1083 | 4 | int i; |
1084 | | |
1085 | 4 | size_t size = sizeof(zend_property_info *) * ce->default_properties_count; |
1086 | 4 | ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED); |
1087 | 4 | ce->properties_info_table = zend_shared_memdup( |
1088 | 4 | ce->properties_info_table, size); |
1089 | | |
1090 | 8 | for (i = 0; i < ce->default_properties_count; i++) { |
1091 | 4 | if (ce->properties_info_table[i]) { |
1092 | 4 | zend_property_info *prop_info = zend_shared_alloc_get_xlat_entry( |
1093 | 4 | ce->properties_info_table[i]); |
1094 | 4 | if (prop_info) { |
1095 | 4 | ce->properties_info_table[i] = prop_info; |
1096 | 4 | } |
1097 | 4 | } |
1098 | 4 | } |
1099 | 4 | } |
1100 | | |
1101 | 374 | if (ce->iterator_funcs_ptr) { |
1102 | 0 | ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs)); |
1103 | 0 | } |
1104 | 374 | if (ce->arrayaccess_funcs_ptr) { |
1105 | 0 | ce->arrayaccess_funcs_ptr = zend_shared_memdup(ce->arrayaccess_funcs_ptr, sizeof(zend_class_arrayaccess_funcs)); |
1106 | 0 | } |
1107 | | |
1108 | 374 | if (ce->ce_flags & ZEND_ACC_CACHED) { |
1109 | 0 | return ce; |
1110 | 0 | } |
1111 | | |
1112 | 374 | ce->ce_flags |= ZEND_ACC_CACHED; |
1113 | | |
1114 | 374 | if (ce->info.user.filename) { |
1115 | 374 | zend_accel_store_string(ce->info.user.filename); |
1116 | 374 | } |
1117 | | |
1118 | 374 | if (ce->doc_comment) { |
1119 | 0 | if (ZCG(accel_directives).save_comments) { |
1120 | 0 | zend_accel_store_interned_string(ce->doc_comment); |
1121 | 0 | } else { |
1122 | 0 | if (!zend_shared_alloc_get_xlat_entry(ce->doc_comment)) { |
1123 | 0 | zend_shared_alloc_register_xlat_entry(ce->doc_comment, ce->doc_comment); |
1124 | 0 | zend_string_release_ex(ce->doc_comment, 0); |
1125 | 0 | } |
1126 | 0 | ce->doc_comment = NULL; |
1127 | 0 | } |
1128 | 0 | } |
1129 | | |
1130 | 374 | if (ce->attributes) { |
1131 | 26 | ce->attributes = zend_persist_attributes(ce->attributes); |
1132 | 26 | } |
1133 | | |
1134 | 374 | if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) { |
1135 | 120 | uint32_t i = 0; |
1136 | | |
1137 | 260 | for (i = 0; i < ce->num_interfaces; i++) { |
1138 | 140 | zend_accel_store_interned_string(ce->interface_names[i].name); |
1139 | 140 | zend_accel_store_interned_string(ce->interface_names[i].lc_name); |
1140 | 140 | } |
1141 | 120 | ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces); |
1142 | 120 | } |
1143 | | |
1144 | 374 | if (ce->num_traits) { |
1145 | 8 | uint32_t i = 0; |
1146 | | |
1147 | 16 | for (i = 0; i < ce->num_traits; i++) { |
1148 | 8 | zend_accel_store_interned_string(ce->trait_names[i].name); |
1149 | 8 | zend_accel_store_interned_string(ce->trait_names[i].lc_name); |
1150 | 8 | } |
1151 | 8 | ce->trait_names = zend_shared_memdup_free(ce->trait_names, sizeof(zend_class_name) * ce->num_traits); |
1152 | | |
1153 | 8 | i = 0; |
1154 | 8 | if (ce->trait_aliases) { |
1155 | 0 | while (ce->trait_aliases[i]) { |
1156 | 0 | if (ce->trait_aliases[i]->trait_method.method_name) { |
1157 | 0 | zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.method_name); |
1158 | 0 | } |
1159 | 0 | if (ce->trait_aliases[i]->trait_method.class_name) { |
1160 | 0 | zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.class_name); |
1161 | 0 | } |
1162 | |
|
1163 | 0 | if (ce->trait_aliases[i]->alias) { |
1164 | 0 | zend_accel_store_interned_string(ce->trait_aliases[i]->alias); |
1165 | 0 | } |
1166 | |
|
1167 | 0 | ce->trait_aliases[i] = zend_shared_memdup_free(ce->trait_aliases[i], sizeof(zend_trait_alias)); |
1168 | 0 | i++; |
1169 | 0 | } |
1170 | |
|
1171 | 0 | ce->trait_aliases = zend_shared_memdup_free(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1)); |
1172 | 0 | } |
1173 | | |
1174 | 8 | if (ce->trait_precedences) { |
1175 | 0 | uint32_t j; |
1176 | |
|
1177 | 0 | i = 0; |
1178 | 0 | while (ce->trait_precedences[i]) { |
1179 | 0 | zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.method_name); |
1180 | 0 | zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.class_name); |
1181 | |
|
1182 | 0 | for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) { |
1183 | 0 | zend_accel_store_interned_string(ce->trait_precedences[i]->exclude_class_names[j]); |
1184 | 0 | } |
1185 | |
|
1186 | 0 | ce->trait_precedences[i] = zend_shared_memdup_free(ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*)); |
1187 | 0 | i++; |
1188 | 0 | } |
1189 | 0 | ce->trait_precedences = zend_shared_memdup_free( |
1190 | 0 | ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1)); |
1191 | 0 | } |
1192 | 8 | } |
1193 | | |
1194 | 374 | ZEND_ASSERT(ce->backed_enum_table == NULL); |
1195 | 374 | } |
1196 | | |
1197 | 374 | return ce; |
1198 | 374 | } |
1199 | | |
1200 | | void zend_update_parent_ce(zend_class_entry *ce) |
1201 | 374 | { |
1202 | 374 | if (ce->ce_flags & ZEND_ACC_LINKED) { |
1203 | 216 | if (ce->parent) { |
1204 | 0 | int i, end; |
1205 | 0 | zend_class_entry *parent = ce->parent; |
1206 | |
|
1207 | 0 | if (parent->type == ZEND_USER_CLASS) { |
1208 | 0 | zend_class_entry *p = zend_shared_alloc_get_xlat_entry(parent); |
1209 | |
|
1210 | 0 | if (p) { |
1211 | 0 | ce->parent = parent = p; |
1212 | 0 | } |
1213 | 0 | } |
1214 | | |
1215 | | /* Create indirections to static properties from parent classes */ |
1216 | 0 | i = parent->default_static_members_count - 1; |
1217 | 0 | while (parent && parent->default_static_members_table) { |
1218 | 0 | end = parent->parent ? parent->parent->default_static_members_count : 0; |
1219 | 0 | for (; i >= end; i--) { |
1220 | 0 | zval *p = &ce->default_static_members_table[i]; |
1221 | | /* The static property may have been overridden by a trait |
1222 | | * during inheritance. In that case, the property default |
1223 | | * value is replaced by zend_declare_typed_property() at the |
1224 | | * property index of the parent property. Make sure we only |
1225 | | * point to the parent property value if the child value was |
1226 | | * already indirect. */ |
1227 | 0 | if (Z_TYPE_P(p) == IS_INDIRECT) { |
1228 | 0 | ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); |
1229 | 0 | } |
1230 | 0 | } |
1231 | |
|
1232 | 0 | parent = parent->parent; |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | 216 | if (ce->num_interfaces) { |
1237 | 0 | uint32_t i = 0; |
1238 | |
|
1239 | 0 | ce->interfaces = zend_shared_memdup_free(ce->interfaces, sizeof(zend_class_entry*) * ce->num_interfaces); |
1240 | 0 | for (i = 0; i < ce->num_interfaces; i++) { |
1241 | 0 | if (ce->interfaces[i]->type == ZEND_USER_CLASS) { |
1242 | 0 | zend_class_entry *tmp = zend_shared_alloc_get_xlat_entry(ce->interfaces[i]); |
1243 | 0 | if (tmp != NULL) { |
1244 | 0 | ce->interfaces[i] = tmp; |
1245 | 0 | } |
1246 | 0 | } |
1247 | 0 | } |
1248 | 0 | } |
1249 | | |
1250 | 216 | if (ce->iterator_funcs_ptr) { |
1251 | 0 | memset(ce->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs)); |
1252 | 0 | if (zend_class_implements_interface(ce, zend_ce_aggregate)) { |
1253 | 0 | ce->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&ce->function_table, "getiterator", sizeof("getiterator") - 1); |
1254 | 0 | } |
1255 | 0 | if (zend_class_implements_interface(ce, zend_ce_iterator)) { |
1256 | 0 | ce->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&ce->function_table, "rewind", sizeof("rewind") - 1); |
1257 | 0 | ce->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&ce->function_table, "valid", sizeof("valid") - 1); |
1258 | 0 | ce->iterator_funcs_ptr->zf_key = zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_KEY)); |
1259 | 0 | ce->iterator_funcs_ptr->zf_current = zend_hash_str_find_ptr(&ce->function_table, "current", sizeof("current") - 1); |
1260 | 0 | ce->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&ce->function_table, "next", sizeof("next") - 1); |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | 216 | if (ce->arrayaccess_funcs_ptr) { |
1265 | 0 | ZEND_ASSERT(zend_class_implements_interface(ce, zend_ce_arrayaccess)); |
1266 | 0 | ce->arrayaccess_funcs_ptr->zf_offsetget = zend_hash_str_find_ptr(&ce->function_table, "offsetget", sizeof("offsetget") - 1); |
1267 | 0 | ce->arrayaccess_funcs_ptr->zf_offsetexists = zend_hash_str_find_ptr(&ce->function_table, "offsetexists", sizeof("offsetexists") - 1); |
1268 | 0 | ce->arrayaccess_funcs_ptr->zf_offsetset = zend_hash_str_find_ptr(&ce->function_table, "offsetset", sizeof("offsetset") - 1); |
1269 | 0 | ce->arrayaccess_funcs_ptr->zf_offsetunset = zend_hash_str_find_ptr(&ce->function_table, "offsetunset", sizeof("offsetunset") - 1); |
1270 | 0 | } |
1271 | 216 | } |
1272 | | |
1273 | | /* update methods */ |
1274 | 374 | if (ce->constructor) { |
1275 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->constructor); |
1276 | 0 | if (tmp != NULL) { |
1277 | 0 | ce->constructor = tmp; |
1278 | 0 | } |
1279 | 0 | } |
1280 | 374 | if (ce->destructor) { |
1281 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->destructor); |
1282 | 0 | if (tmp != NULL) { |
1283 | 0 | ce->destructor = tmp; |
1284 | 0 | } |
1285 | 0 | } |
1286 | 374 | if (ce->clone) { |
1287 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->clone); |
1288 | 0 | if (tmp != NULL) { |
1289 | 0 | ce->clone = tmp; |
1290 | 0 | } |
1291 | 0 | } |
1292 | 374 | if (ce->__get) { |
1293 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__get); |
1294 | 0 | if (tmp != NULL) { |
1295 | 0 | ce->__get = tmp; |
1296 | 0 | } |
1297 | 0 | } |
1298 | 374 | if (ce->__set) { |
1299 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__set); |
1300 | 0 | if (tmp != NULL) { |
1301 | 0 | ce->__set = tmp; |
1302 | 0 | } |
1303 | 0 | } |
1304 | 374 | if (ce->__call) { |
1305 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__call); |
1306 | 0 | if (tmp != NULL) { |
1307 | 0 | ce->__call = tmp; |
1308 | 0 | } |
1309 | 0 | } |
1310 | 374 | if (ce->__serialize) { |
1311 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__serialize); |
1312 | 0 | if (tmp != NULL) { |
1313 | 0 | ce->__serialize = tmp; |
1314 | 0 | } |
1315 | 0 | } |
1316 | 374 | if (ce->__unserialize) { |
1317 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__unserialize); |
1318 | 0 | if (tmp != NULL) { |
1319 | 0 | ce->__unserialize = tmp; |
1320 | 0 | } |
1321 | 0 | } |
1322 | 374 | if (ce->__isset) { |
1323 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__isset); |
1324 | 0 | if (tmp != NULL) { |
1325 | 0 | ce->__isset = tmp; |
1326 | 0 | } |
1327 | 0 | } |
1328 | 374 | if (ce->__unset) { |
1329 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__unset); |
1330 | 0 | if (tmp != NULL) { |
1331 | 0 | ce->__unset = tmp; |
1332 | 0 | } |
1333 | 0 | } |
1334 | 374 | if (ce->__tostring) { |
1335 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__tostring); |
1336 | 0 | if (tmp != NULL) { |
1337 | 0 | ce->__tostring = tmp; |
1338 | 0 | } |
1339 | 0 | } |
1340 | 374 | if (ce->__callstatic) { |
1341 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__callstatic); |
1342 | 0 | if (tmp != NULL) { |
1343 | 0 | ce->__callstatic = tmp; |
1344 | 0 | } |
1345 | 0 | } |
1346 | 374 | if (ce->__debugInfo) { |
1347 | 0 | zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->__debugInfo); |
1348 | 0 | if (tmp != NULL) { |
1349 | 0 | ce->__debugInfo = tmp; |
1350 | 0 | } |
1351 | 0 | } |
1352 | 374 | } |
1353 | | |
1354 | | #ifdef HAVE_JIT |
1355 | | static void zend_accel_persist_jit_op_array(zend_op_array *op_array, const zend_class_entry *ce) |
1356 | 0 | { |
1357 | 0 | if (op_array->type == ZEND_USER_FUNCTION) { |
1358 | 0 | if (op_array->scope == ce |
1359 | 0 | && !(op_array->fn_flags & ZEND_ACC_ABSTRACT) |
1360 | 0 | && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { |
1361 | 0 | zend_jit_op_array(op_array, ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); |
1362 | 0 | for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { |
1363 | 0 | zend_jit_op_array(op_array->dynamic_func_defs[i], ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); |
1364 | 0 | } |
1365 | 0 | } |
1366 | 0 | } |
1367 | 0 | } |
1368 | | |
1369 | | static void zend_accel_persist_link_func_info(zend_op_array *op_array, const zend_class_entry *ce) |
1370 | 0 | { |
1371 | 0 | if (op_array->type == ZEND_USER_FUNCTION |
1372 | 0 | && !(op_array->fn_flags & ZEND_ACC_ABSTRACT)) { |
1373 | 0 | if ((op_array->scope != ce |
1374 | 0 | || (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) |
1375 | 0 | && (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC |
1376 | 0 | || JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST |
1377 | 0 | || JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS |
1378 | 0 | || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE)) { |
1379 | 0 | void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); |
1380 | |
|
1381 | 0 | if (jit_extension) { |
1382 | 0 | ZEND_SET_FUNC_INFO(op_array, jit_extension); |
1383 | 0 | } |
1384 | 0 | } |
1385 | 0 | } |
1386 | 0 | } |
1387 | | #endif |
1388 | | |
1389 | | static void zend_accel_persist_class_table(HashTable *class_table) |
1390 | 25.3k | { |
1391 | 25.3k | Bucket *p; |
1392 | 25.3k | zend_class_entry *ce; |
1393 | 25.3k | #ifdef HAVE_JIT |
1394 | 25.3k | bool orig_jit_on = JIT_G(on); |
1395 | | |
1396 | 25.3k | JIT_G(on) = 0; |
1397 | 25.3k | #endif |
1398 | 25.3k | zend_hash_persist(class_table); |
1399 | 51.4k | ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { |
1400 | 51.4k | ZEND_ASSERT(p->key != NULL); |
1401 | 51.4k | zend_accel_store_interned_string(p->key); |
1402 | 374 | Z_CE(p->val) = zend_persist_class_entry(Z_CE(p->val)); |
1403 | 374 | } ZEND_HASH_FOREACH_END(); |
1404 | 51.4k | ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { |
1405 | 51.4k | if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { |
1406 | 374 | ce = Z_PTR(p->val); |
1407 | 374 | zend_update_parent_ce(ce); |
1408 | 374 | } |
1409 | 51.4k | } ZEND_HASH_FOREACH_END(); |
1410 | 25.3k | #ifdef HAVE_JIT |
1411 | 25.3k | JIT_G(on) = orig_jit_on; |
1412 | 25.3k | if (JIT_G(on) && JIT_G(opt_level) <= ZEND_JIT_LEVEL_OPT_FUNCS && |
1413 | 5.10k | !ZCG(current_persistent_script)->corrupted) { |
1414 | 5.10k | zend_op_array *op_array; |
1415 | 5.10k | zend_property_info *prop; |
1416 | | |
1417 | 10.4k | ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { |
1418 | 10.4k | if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { |
1419 | 116 | ce = Z_PTR(p->val); |
1420 | 232 | ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { |
1421 | 232 | zend_accel_persist_jit_op_array(op_array, ce); |
1422 | 232 | } ZEND_HASH_FOREACH_END(); |
1423 | | |
1424 | 116 | if (ce->num_hooked_props > 0) { |
1425 | 0 | ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { |
1426 | 0 | if (prop->hooks) { |
1427 | 0 | for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { |
1428 | 0 | if (prop->hooks[i]) { |
1429 | 0 | op_array = &prop->hooks[i]->op_array; |
1430 | 0 | zend_accel_persist_jit_op_array(op_array, ce); |
1431 | 0 | } |
1432 | 0 | } |
1433 | 0 | } |
1434 | 0 | } ZEND_HASH_FOREACH_END(); |
1435 | 0 | } |
1436 | 116 | } |
1437 | 10.4k | } ZEND_HASH_FOREACH_END(); |
1438 | 10.4k | ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { |
1439 | 10.4k | if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { |
1440 | 116 | ce = Z_PTR(p->val); |
1441 | 232 | ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { |
1442 | 232 | zend_accel_persist_link_func_info(op_array, ce); |
1443 | 232 | } ZEND_HASH_FOREACH_END(); |
1444 | | |
1445 | 116 | if (ce->num_hooked_props > 0) { |
1446 | 0 | ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { |
1447 | 0 | if (prop->hooks) { |
1448 | 0 | for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { |
1449 | 0 | if (prop->hooks[i]) { |
1450 | 0 | op_array = &prop->hooks[i]->op_array; |
1451 | 0 | zend_accel_persist_link_func_info(op_array, ce); |
1452 | 0 | } |
1453 | 0 | } |
1454 | 0 | } |
1455 | 0 | } ZEND_HASH_FOREACH_END(); |
1456 | 0 | } |
1457 | 116 | } |
1458 | 10.4k | } ZEND_HASH_FOREACH_END(); |
1459 | 5.10k | } |
1460 | 25.3k | #endif |
1461 | 25.3k | } |
1462 | | |
1463 | 25.3k | zend_error_info **zend_persist_warnings(uint32_t num_warnings, zend_error_info **warnings) { |
1464 | 25.3k | if (warnings) { |
1465 | 0 | warnings = zend_shared_memdup(warnings, num_warnings * sizeof(zend_error_info *)); |
1466 | 0 | for (uint32_t i = 0; i < num_warnings; i++) { |
1467 | 0 | zend_accel_store_string(warnings[i]->filename); |
1468 | 0 | zend_accel_store_string(warnings[i]->message); |
1469 | 0 | warnings[i] = zend_shared_memdup(warnings[i], sizeof(zend_error_info)); |
1470 | 0 | } |
1471 | 0 | } |
1472 | 25.3k | return warnings; |
1473 | 25.3k | } |
1474 | | |
1475 | | static zend_early_binding *zend_persist_early_bindings( |
1476 | 25.3k | uint32_t num_early_bindings, zend_early_binding *early_bindings) { |
1477 | 25.3k | if (early_bindings) { |
1478 | 58 | early_bindings = zend_shared_memdup_free( |
1479 | 58 | early_bindings, num_early_bindings * sizeof(zend_early_binding)); |
1480 | 116 | for (uint32_t i = 0; i < num_early_bindings; i++) { |
1481 | 58 | zend_accel_store_interned_string(early_bindings[i].lcname); |
1482 | 58 | zend_accel_store_interned_string(early_bindings[i].rtd_key); |
1483 | 58 | zend_accel_store_interned_string(early_bindings[i].lc_parent_name); |
1484 | 58 | } |
1485 | 58 | } |
1486 | 25.3k | return early_bindings; |
1487 | 25.3k | } |
1488 | | |
1489 | | zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, bool for_shm) |
1490 | 25.3k | { |
1491 | 25.3k | Bucket *p; |
1492 | | |
1493 | 25.3k | script->mem = ZCG(mem); |
1494 | | |
1495 | 25.3k | ZEND_ASSERT(((uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ |
1496 | | |
1497 | 25.3k | script = zend_shared_memdup_free(script, sizeof(zend_persistent_script)); |
1498 | 25.3k | script->corrupted = false; |
1499 | 25.3k | ZCG(current_persistent_script) = script; |
1500 | | |
1501 | 25.3k | if (!for_shm) { |
1502 | | /* script is not going to be saved in SHM */ |
1503 | 0 | script->corrupted = true; |
1504 | 0 | } |
1505 | | |
1506 | 25.3k | zend_accel_store_interned_string(script->script.filename); |
1507 | | |
1508 | 25.3k | #if defined(__AVX__) || defined(__SSE2__) |
1509 | | /* Align to 64-byte boundary */ |
1510 | 25.3k | ZCG(mem) = (void*)(((uintptr_t)ZCG(mem) + 63L) & ~63L); |
1511 | | #else |
1512 | | ZEND_ASSERT(((uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ |
1513 | | #endif |
1514 | | |
1515 | 25.3k | #ifdef HAVE_JIT |
1516 | 25.3k | if (JIT_G(on) && for_shm) { |
1517 | 12.2k | zend_jit_unprotect(); |
1518 | 12.2k | } |
1519 | 25.3k | #endif |
1520 | | |
1521 | 25.3k | zend_map_ptr_extend(ZCSG(map_ptr_last)); |
1522 | 25.3k | if (for_shm) { |
1523 | 25.3k | zend_accel_pending_ce_cache_begin(); |
1524 | 25.3k | } |
1525 | | |
1526 | 25.3k | zend_accel_persist_class_table(&script->script.class_table); |
1527 | 25.3k | zend_hash_persist(&script->script.function_table); |
1528 | 51.5k | ZEND_HASH_MAP_FOREACH_BUCKET(&script->script.function_table, p) { |
1529 | 51.5k | ZEND_ASSERT(p->key != NULL); |
1530 | 51.5k | zend_accel_store_interned_string(p->key); |
1531 | 416 | zend_persist_op_array(&p->val); |
1532 | 416 | } ZEND_HASH_FOREACH_END(); |
1533 | 25.3k | zend_persist_op_array_ex(&script->script.main_op_array, script); |
1534 | 25.3k | if (!script->corrupted) { |
1535 | 25.3k | ZEND_MAP_PTR_INIT(script->script.main_op_array.run_time_cache, NULL); |
1536 | 25.3k | if (script->script.main_op_array.static_variables) { |
1537 | 18 | ZEND_MAP_PTR_NEW(script->script.main_op_array.static_variables_ptr); |
1538 | 18 | } |
1539 | 25.3k | #ifdef HAVE_JIT |
1540 | 25.3k | if (JIT_G(on) && JIT_G(opt_level) <= ZEND_JIT_LEVEL_OPT_FUNCS) { |
1541 | 5.10k | zend_jit_op_array(&script->script.main_op_array, &script->script); |
1542 | 5.10k | } |
1543 | 25.3k | #endif |
1544 | 25.3k | } |
1545 | 25.3k | script->warnings = zend_persist_warnings(script->num_warnings, script->warnings); |
1546 | 25.3k | script->early_bindings = zend_persist_early_bindings( |
1547 | 25.3k | script->num_early_bindings, script->early_bindings); |
1548 | | |
1549 | 25.3k | if (for_shm) { |
1550 | 25.3k | ZCSG(map_ptr_last) = CG(map_ptr_last); |
1551 | 25.3k | ZCSG(map_ptr_static_last) = zend_map_ptr_static_last; |
1552 | 25.3k | zend_accel_pending_ce_cache_commit(); |
1553 | 25.3k | } |
1554 | | |
1555 | 25.3k | #ifdef HAVE_JIT |
1556 | 25.3k | if (JIT_G(on) && for_shm) { |
1557 | 12.2k | if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_SCRIPT) { |
1558 | 7.17k | zend_jit_script(&script->script); |
1559 | 7.17k | } |
1560 | 12.2k | zend_jit_protect(); |
1561 | 12.2k | } |
1562 | 25.3k | #endif |
1563 | | |
1564 | 25.3k | script->corrupted = false; |
1565 | 25.3k | ZCG(current_persistent_script) = NULL; |
1566 | | |
1567 | 25.3k | return script; |
1568 | 25.3k | } |