1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 #include "zend.h"
23 #include "ZendAccelerator.h"
24 #include "zend_persist.h"
25 #include "zend_extensions.h"
26 #include "zend_shared_alloc.h"
27 #include "zend_vm.h"
28 #include "zend_constants.h"
29 #include "zend_operators.h"
30
31 #define zend_accel_store(p, size) \
32 (p = _zend_shared_memdup((void*)p, size, 1))
33 #define zend_accel_memdup(p, size) \
34 _zend_shared_memdup((void*)p, size, 0)
35
36 #ifdef HAVE_OPCACHE_FILE_CACHE
37 #define zend_set_str_gc_flags(str) do { \
38 if (file_cache_only) { \
39 GC_TYPE_INFO(str) = IS_STRING | (IS_STR_INTERNED << GC_FLAGS_SHIFT); \
40 } else { \
41 GC_TYPE_INFO(str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \
42 } \
43 } while (0)
44 #else
45 #define zend_set_str_gc_flags(str) do {\
46 GC_TYPE_INFO(str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT); \
47 } while (0)
48 #endif
49
50 #define zend_accel_store_string(str) do { \
51 zend_string *new_str = zend_shared_alloc_get_xlat_entry(str); \
52 if (new_str) { \
53 zend_string_release_ex(str, 0); \
54 str = new_str; \
55 } else { \
56 new_str = zend_accel_memdup((void*)str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \
57 zend_string_release_ex(str, 0); \
58 str = new_str; \
59 zend_string_hash_val(str); \
60 zend_set_str_gc_flags(str); \
61 } \
62 } while (0)
63 #define zend_accel_memdup_string(str) do { \
64 str = zend_accel_memdup(str, _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); \
65 zend_string_hash_val(str); \
66 zend_set_str_gc_flags(str); \
67 } while (0)
68 #define zend_accel_store_interned_string(str) do { \
69 if (!IS_ACCEL_INTERNED(str)) { \
70 zend_accel_store_string(str); \
71 } \
72 } while (0)
73 #define zend_accel_memdup_interned_string(str) do { \
74 if (!IS_ACCEL_INTERNED(str)) { \
75 zend_accel_memdup_string(str); \
76 } \
77 } while (0)
78
79 typedef void (*zend_persist_func_t)(zval*);
80
81 static void zend_persist_zval(zval *z);
82
83 static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
84 {HT_INVALID_IDX, HT_INVALID_IDX};
85
zend_hash_persist(HashTable * ht,zend_persist_func_t pPersistElement)86 static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement)
87 {
88 uint32_t idx, nIndex;
89 Bucket *p;
90
91 HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS;
92 ht->pDestructor = NULL;
93
94 if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) {
95 if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) {
96 HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
97 } else {
98 HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
99 }
100 return;
101 }
102 if (ht->nNumUsed == 0) {
103 efree(HT_GET_DATA_ADDR(ht));
104 ht->nTableMask = HT_MIN_MASK;
105 if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) {
106 HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
107 } else {
108 HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
109 }
110 HT_FLAGS(ht) &= ~HASH_FLAG_INITIALIZED;
111 return;
112 }
113 if (HT_FLAGS(ht) & HASH_FLAG_PACKED) {
114 void *data = HT_GET_DATA_ADDR(ht);
115 zend_accel_store(data, HT_USED_SIZE(ht));
116 HT_SET_DATA_ADDR(ht, data);
117 } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
118 /* compact table */
119 void *old_data = HT_GET_DATA_ADDR(ht);
120 Bucket *old_buckets = ht->arData;
121 uint32_t hash_size;
122
123 if (ht->nNumUsed <= HT_MIN_SIZE) {
124 hash_size = HT_MIN_SIZE * 2;
125 } else {
126 hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
127 while (hash_size >> 2 > ht->nNumUsed) {
128 hash_size >>= 1;
129 }
130 }
131 ht->nTableMask = (uint32_t)(-(int32_t)hash_size);
132 ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
133 HT_SET_DATA_ADDR(ht, ZCG(mem));
134 ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket))));
135 HT_HASH_RESET(ht);
136 memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket));
137 efree(old_data);
138
139 for (idx = 0; idx < ht->nNumUsed; idx++) {
140 p = ht->arData + idx;
141 if (Z_TYPE(p->val) == IS_UNDEF) continue;
142
143 /* persist bucket and key */
144 if (p->key) {
145 zend_accel_store_interned_string(p->key);
146 }
147
148 /* persist the data itself */
149 pPersistElement(&p->val);
150
151 nIndex = p->h | ht->nTableMask;
152 Z_NEXT(p->val) = HT_HASH(ht, nIndex);
153 HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
154 }
155 return;
156 } else {
157 void *data = ZCG(mem);
158 void *old_data = HT_GET_DATA_ADDR(ht);
159
160 ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
161 ZCG(mem) = (void*)((char*)data + ZEND_ALIGNED_SIZE(HT_USED_SIZE(ht)));
162 memcpy(data, old_data, HT_USED_SIZE(ht));
163 efree(old_data);
164 HT_SET_DATA_ADDR(ht, data);
165 }
166
167 for (idx = 0; idx < ht->nNumUsed; idx++) {
168 p = ht->arData + idx;
169 if (Z_TYPE(p->val) == IS_UNDEF) continue;
170
171 /* persist bucket and key */
172 if (p->key) {
173 zend_accel_store_interned_string(p->key);
174 }
175
176 /* persist the data itself */
177 pPersistElement(&p->val);
178 }
179 }
180
zend_hash_persist_immutable(HashTable * ht)181 static void zend_hash_persist_immutable(HashTable *ht)
182 {
183 uint32_t idx, nIndex;
184 Bucket *p;
185
186 HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS;
187 ht->pDestructor = NULL;
188
189 if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) {
190 if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) {
191 HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
192 } else {
193 HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
194 }
195 return;
196 }
197 if (ht->nNumUsed == 0) {
198 efree(HT_GET_DATA_ADDR(ht));
199 ht->nTableMask = HT_MIN_MASK;
200 if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) {
201 HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
202 } else {
203 HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
204 }
205 HT_FLAGS(ht) &= ~HASH_FLAG_INITIALIZED;
206 return;
207 }
208 if (HT_FLAGS(ht) & HASH_FLAG_PACKED) {
209 HT_SET_DATA_ADDR(ht, zend_accel_memdup(HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)));
210 } else if (ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) {
211 /* compact table */
212 void *old_data = HT_GET_DATA_ADDR(ht);
213 Bucket *old_buckets = ht->arData;
214 uint32_t hash_size;
215
216 if (ht->nNumUsed <= HT_MIN_SIZE) {
217 hash_size = HT_MIN_SIZE * 2;
218 } else {
219 hash_size = (uint32_t)(-(int32_t)ht->nTableMask);
220 while (hash_size >> 2 > ht->nNumUsed) {
221 hash_size >>= 1;
222 }
223 }
224 ht->nTableMask = (uint32_t)(-(int32_t)hash_size);
225 ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
226 HT_SET_DATA_ADDR(ht, ZCG(mem));
227 ZCG(mem) = (void*)((char*)ZCG(mem) + (hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket)));
228 HT_HASH_RESET(ht);
229 memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket));
230 efree(old_data);
231
232 for (idx = 0; idx < ht->nNumUsed; idx++) {
233 p = ht->arData + idx;
234 if (Z_TYPE(p->val) == IS_UNDEF) continue;
235
236 /* persist bucket and key */
237 if (p->key) {
238 zend_accel_memdup_interned_string(p->key);
239 }
240
241 /* persist the data itself */
242 zend_persist_zval(&p->val);
243
244 nIndex = p->h | ht->nTableMask;
245 Z_NEXT(p->val) = HT_HASH(ht, nIndex);
246 HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
247 }
248 return;
249 } else {
250 void *data = ZCG(mem);
251
252 ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
253 ZCG(mem) = (void*)((char*)data + ZEND_ALIGNED_SIZE(HT_USED_SIZE(ht)));
254 memcpy(data, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
255 HT_SET_DATA_ADDR(ht, data);
256 }
257 for (idx = 0; idx < ht->nNumUsed; idx++) {
258 p = ht->arData + idx;
259 if (Z_TYPE(p->val) == IS_UNDEF) continue;
260
261 /* persist bucket and key */
262 if (p->key) {
263 zend_accel_memdup_interned_string(p->key);
264 }
265
266 /* persist the data itself */
267 zend_persist_zval(&p->val);
268 }
269 }
270
zend_persist_ast(zend_ast * ast)271 static zend_ast *zend_persist_ast(zend_ast *ast)
272 {
273 uint32_t i;
274 zend_ast *node;
275
276 if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
277 zend_ast_zval *copy = zend_accel_memdup(ast, sizeof(zend_ast_zval));
278 zend_persist_zval(©->val);
279 node = (zend_ast *) copy;
280 } else if (zend_ast_is_list(ast)) {
281 zend_ast_list *list = zend_ast_get_list(ast);
282 zend_ast_list *copy = zend_accel_memdup(ast,
283 sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children);
284 for (i = 0; i < list->children; i++) {
285 if (copy->child[i]) {
286 copy->child[i] = zend_persist_ast(copy->child[i]);
287 }
288 }
289 node = (zend_ast *) copy;
290 } else {
291 uint32_t children = zend_ast_get_num_children(ast);
292 node = zend_accel_memdup(ast, sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children);
293 for (i = 0; i < children; i++) {
294 if (node->child[i]) {
295 node->child[i] = zend_persist_ast(node->child[i]);
296 }
297 }
298 }
299
300 return node;
301 }
302
zend_persist_zval(zval * z)303 static void zend_persist_zval(zval *z)
304 {
305 void *new_ptr;
306
307 switch (Z_TYPE_P(z)) {
308 case IS_STRING:
309 zend_accel_store_interned_string(Z_STR_P(z));
310 Z_TYPE_FLAGS_P(z) = 0;
311 break;
312 case IS_ARRAY:
313 new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
314 if (new_ptr) {
315 Z_ARR_P(z) = new_ptr;
316 Z_TYPE_FLAGS_P(z) = 0;
317 } else {
318 if (!Z_REFCOUNTED_P(z)) {
319 Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array));
320 zend_hash_persist_immutable(Z_ARRVAL_P(z));
321 } else {
322 GC_REMOVE_FROM_BUFFER(Z_ARR_P(z));
323 zend_accel_store(Z_ARR_P(z), sizeof(zend_array));
324 zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval);
325 /* make immutable array */
326 Z_TYPE_FLAGS_P(z) = 0;
327 GC_SET_REFCOUNT(Z_COUNTED_P(z), 2);
328 GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE);
329 }
330 }
331 break;
332 case IS_REFERENCE:
333 new_ptr = zend_shared_alloc_get_xlat_entry(Z_REF_P(z));
334 if (new_ptr) {
335 Z_REF_P(z) = new_ptr;
336 } else {
337 zend_accel_store(Z_REF_P(z), sizeof(zend_reference));
338 zend_persist_zval(Z_REFVAL_P(z));
339 }
340 break;
341 case IS_CONSTANT_AST:
342 new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z));
343 if (new_ptr) {
344 Z_AST_P(z) = new_ptr;
345 Z_TYPE_FLAGS_P(z) = 0;
346 } else {
347 zend_ast_ref *old_ref = Z_AST_P(z);
348 Z_ARR_P(z) = zend_accel_memdup(Z_AST_P(z), sizeof(zend_ast_ref));
349 zend_persist_ast(GC_AST(old_ref));
350 Z_TYPE_FLAGS_P(z) = 0;
351 GC_SET_REFCOUNT(Z_COUNTED_P(z), 1);
352 efree(old_ref);
353 }
354 break;
355 }
356 }
357
zend_persist_op_array_ex(zend_op_array * op_array,zend_persistent_script * main_persistent_script)358 static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script)
359 {
360 int already_stored = 0;
361 zend_op *persist_ptr;
362 zval *orig_literals = NULL;
363
364 if (op_array->refcount && --(*op_array->refcount) == 0) {
365 efree(op_array->refcount);
366 }
367 op_array->refcount = NULL;
368
369 if (main_persistent_script) {
370 zend_execute_data *orig_execute_data = EG(current_execute_data);
371 zend_execute_data fake_execute_data;
372 zval *offset;
373
374 memset(&fake_execute_data, 0, sizeof(fake_execute_data));
375 fake_execute_data.func = (zend_function*)op_array;
376 EG(current_execute_data) = &fake_execute_data;
377 if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
378 main_persistent_script->compiler_halt_offset = Z_LVAL_P(offset);
379 }
380 EG(current_execute_data) = orig_execute_data;
381 }
382
383 if (op_array->static_variables) {
384 HashTable *stored = zend_shared_alloc_get_xlat_entry(op_array->static_variables);
385
386 if (stored) {
387 op_array->static_variables = stored;
388 } else {
389 zend_hash_persist(op_array->static_variables, zend_persist_zval);
390 zend_accel_store(op_array->static_variables, sizeof(HashTable));
391 /* make immutable array */
392 GC_SET_REFCOUNT(op_array->static_variables, 2);
393 GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << GC_FLAGS_SHIFT);
394 }
395 }
396
397 if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
398 already_stored = 1;
399 }
400
401 if (op_array->literals) {
402 if (already_stored) {
403 orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals);
404 ZEND_ASSERT(orig_literals != NULL);
405 op_array->literals = orig_literals;
406 } else {
407 zval *p = zend_accel_memdup(op_array->literals, sizeof(zval) * op_array->last_literal);
408 zval *end = p + op_array->last_literal;
409 orig_literals = op_array->literals;
410 op_array->literals = p;
411 while (p < end) {
412 zend_persist_zval(p);
413 p++;
414 }
415 #if ZEND_USE_ABS_CONST_ADDR
416 efree(orig_literals);
417 #endif
418 }
419 }
420
421 if (already_stored) {
422 persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes);
423 ZEND_ASSERT(persist_ptr != NULL);
424 op_array->opcodes = persist_ptr;
425 } else {
426 zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last);
427 zend_op *opline = new_opcodes;
428 zend_op *end = new_opcodes + op_array->last;
429 int offset = 0;
430
431 for (; opline < end ; opline++, offset++) {
432 #if ZEND_USE_ABS_CONST_ADDR
433 if (opline->op1_type == IS_CONST) {
434 opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals));
435 if (opline->opcode == ZEND_SEND_VAL
436 || opline->opcode == ZEND_SEND_VAL_EX
437 || opline->opcode == ZEND_QM_ASSIGN) {
438 /* Update handlers to eliminate REFCOUNTED check */
439 zend_vm_set_opcode_handler_ex(opline, 0, 0, 0);
440 }
441 }
442 if (opline->op2_type == IS_CONST) {
443 opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals));
444 }
445 #else
446 if (opline->op1_type == IS_CONST) {
447 opline->op1.constant =
448 (char*)(op_array->literals +
449 ((zval*)((char*)(op_array->opcodes + (opline - new_opcodes)) +
450 (int32_t)opline->op1.constant) - orig_literals)) -
451 (char*)opline;
452 if (opline->opcode == ZEND_SEND_VAL
453 || opline->opcode == ZEND_SEND_VAL_EX
454 || opline->opcode == ZEND_QM_ASSIGN) {
455 zend_vm_set_opcode_handler_ex(opline, 0, 0, 0);
456 }
457 }
458 if (opline->op2_type == IS_CONST) {
459 opline->op2.constant =
460 (char*)(op_array->literals +
461 ((zval*)((char*)(op_array->opcodes + (opline - new_opcodes)) +
462 (int32_t)opline->op2.constant) - orig_literals)) -
463 (char*)opline;
464 }
465 #endif
466 #if ZEND_USE_ABS_JMP_ADDR
467 if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
468 /* fix jumps to point to new array */
469 switch (opline->opcode) {
470 case ZEND_JMP:
471 case ZEND_FAST_CALL:
472 opline->op1.jmp_addr = &new_opcodes[opline->op1.jmp_addr - op_array->opcodes];
473 break;
474 case ZEND_JMPZNZ:
475 /* relative extended_value don't have to be changed */
476 /* break omitted intentionally */
477 case ZEND_JMPZ:
478 case ZEND_JMPNZ:
479 case ZEND_JMPZ_EX:
480 case ZEND_JMPNZ_EX:
481 case ZEND_JMP_SET:
482 case ZEND_COALESCE:
483 case ZEND_FE_RESET_R:
484 case ZEND_FE_RESET_RW:
485 case ZEND_ASSERT_CHECK:
486 opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes];
487 break;
488 case ZEND_CATCH:
489 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
490 opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes];
491 }
492 break;
493 case ZEND_DECLARE_ANON_CLASS:
494 case ZEND_DECLARE_ANON_INHERITED_CLASS:
495 case ZEND_FE_FETCH_R:
496 case ZEND_FE_FETCH_RW:
497 case ZEND_SWITCH_LONG:
498 case ZEND_SWITCH_STRING:
499 /* relative extended_value don't have to be changed */
500 break;
501 }
502 }
503 #endif
504 }
505
506 efree(op_array->opcodes);
507 op_array->opcodes = new_opcodes;
508
509 if (op_array->run_time_cache) {
510 efree(op_array->run_time_cache);
511 op_array->run_time_cache = NULL;
512 }
513 }
514
515 if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) {
516 zend_string *new_name;
517 if (already_stored) {
518 new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
519 ZEND_ASSERT(new_name != NULL);
520 op_array->function_name = new_name;
521 } else {
522 zend_accel_store_interned_string(op_array->function_name);
523 }
524 }
525
526 if (op_array->filename) {
527 /* do not free! PHP has centralized filename storage, compiler will free it */
528 zend_accel_memdup_string(op_array->filename);
529 }
530
531 if (op_array->arg_info) {
532 zend_arg_info *arg_info = op_array->arg_info;
533 uint32_t num_args = op_array->num_args;
534
535 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
536 arg_info--;
537 num_args++;
538 }
539 if (already_stored) {
540 arg_info = zend_shared_alloc_get_xlat_entry(arg_info);
541 ZEND_ASSERT(arg_info != NULL);
542 } else {
543 uint32_t i;
544
545 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
546 num_args++;
547 }
548 zend_accel_store(arg_info, sizeof(zend_arg_info) * num_args);
549 for (i = 0; i < num_args; i++) {
550 if (arg_info[i].name) {
551 zend_accel_store_interned_string(arg_info[i].name);
552 }
553 if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
554 zend_string *type_name = ZEND_TYPE_NAME(arg_info[i].type);
555 zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
556
557 zend_accel_store_interned_string(type_name);
558 arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
559 }
560 }
561 }
562 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
563 arg_info++;
564 }
565 op_array->arg_info = arg_info;
566 }
567
568 if (op_array->live_range) {
569 zend_accel_store(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
570 }
571
572 if (op_array->scope) {
573 op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
574 }
575
576 if (op_array->doc_comment) {
577 if (ZCG(accel_directives).save_comments) {
578 if (already_stored) {
579 op_array->doc_comment = zend_shared_alloc_get_xlat_entry(op_array->doc_comment);
580 ZEND_ASSERT(op_array->doc_comment != NULL);
581 } else {
582 zend_accel_store_interned_string(op_array->doc_comment);
583 }
584 } else {
585 if (!already_stored) {
586 zend_string_release_ex(op_array->doc_comment, 0);
587 }
588 op_array->doc_comment = NULL;
589 }
590 }
591
592 if (op_array->try_catch_array) {
593 zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
594 }
595
596 if (op_array->vars) {
597 if (already_stored) {
598 persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars);
599 ZEND_ASSERT(persist_ptr != NULL);
600 op_array->vars = (zend_string**)persist_ptr;
601 } else {
602 int i;
603 zend_accel_store(op_array->vars, sizeof(zend_string*) * op_array->last_var);
604 for (i = 0; i < op_array->last_var; i++) {
605 zend_accel_store_interned_string(op_array->vars[i]);
606 }
607 }
608 }
609
610 /* "prototype" may be undefined if "scope" isn't set */
611 if (op_array->scope && op_array->prototype) {
612 if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) {
613 op_array->prototype = (union _zend_function*)persist_ptr;
614 }
615 } else {
616 op_array->prototype = NULL;
617 }
618
619 ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem))));
620 }
621
zend_persist_op_array(zval * zv)622 static void zend_persist_op_array(zval *zv)
623 {
624 zend_op_array *op_array = Z_PTR_P(zv);
625
626 ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
627 memcpy(ZCG(mem), Z_PTR_P(zv), sizeof(zend_op_array));
628 Z_PTR_P(zv) = ZCG(mem);
629 ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(sizeof(zend_op_array)));
630 zend_persist_op_array_ex(Z_PTR_P(zv), NULL);
631 ((zend_op_array*)Z_PTR_P(zv))->fn_flags |= ZEND_ACC_IMMUTABLE;
632 }
633
zend_persist_class_method(zval * zv)634 static void zend_persist_class_method(zval *zv)
635 {
636 zend_op_array *op_array = Z_PTR_P(zv);
637 zend_op_array *old_op_array;
638
639 ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
640 old_op_array = zend_shared_alloc_get_xlat_entry(op_array);
641 if (old_op_array) {
642 Z_PTR_P(zv) = old_op_array;
643 if (op_array->refcount && --(*op_array->refcount) == 0) {
644 efree(op_array->refcount);
645 }
646 return;
647 }
648 memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_op_array));
649 zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem));
650 Z_PTR_P(zv) = ZCG(arena_mem);
651 ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_op_array)));
652 zend_persist_op_array_ex(Z_PTR_P(zv), NULL);
653 }
654
zend_persist_property_info(zval * zv)655 static void zend_persist_property_info(zval *zv)
656 {
657 zend_property_info *prop = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv));
658
659 if (prop) {
660 Z_PTR_P(zv) = prop;
661 return;
662 }
663 memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_property_info));
664 zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem));
665 prop = Z_PTR_P(zv) = ZCG(arena_mem);
666 ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_property_info)));
667 prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce);
668 zend_accel_store_interned_string(prop->name);
669 if (prop->doc_comment) {
670 if (ZCG(accel_directives).save_comments) {
671 zend_accel_store_interned_string(prop->doc_comment);
672 } else {
673 if (!zend_shared_alloc_get_xlat_entry(prop->doc_comment)) {
674 zend_shared_alloc_register_xlat_entry(prop->doc_comment, prop->doc_comment);
675 }
676 zend_string_release_ex(prop->doc_comment, 0);
677 prop->doc_comment = NULL;
678 }
679 }
680 }
681
zend_persist_class_constant(zval * zv)682 static void zend_persist_class_constant(zval *zv)
683 {
684 zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv));
685
686 if (c) {
687 Z_PTR_P(zv) = c;
688 return;
689 }
690 memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_constant));
691 zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem));
692 c = Z_PTR_P(zv) = ZCG(arena_mem);
693 ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_constant)));
694 zend_persist_zval(&c->value);
695 c->ce = zend_shared_alloc_get_xlat_entry(c->ce);
696 if (c->doc_comment) {
697 if (ZCG(accel_directives).save_comments) {
698 zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment);
699 if (doc_comment) {
700 c->doc_comment = doc_comment;
701 } else {
702 zend_accel_store_interned_string(c->doc_comment);
703 }
704 } else {
705 zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment);
706 if (!doc_comment) {
707 zend_shared_alloc_register_xlat_entry(c->doc_comment, c->doc_comment);
708 zend_string_release_ex(c->doc_comment, 0);
709 }
710 c->doc_comment = NULL;
711 }
712 }
713 }
714
zend_persist_class_entry(zval * zv)715 static void zend_persist_class_entry(zval *zv)
716 {
717 zend_class_entry *ce = Z_PTR_P(zv);
718
719 if (ce->type == ZEND_USER_CLASS) {
720 memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_entry));
721 zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem));
722 ce = Z_PTR_P(zv) = ZCG(arena_mem);
723 ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_entry)));
724 zend_accel_store_interned_string(ce->name);
725 zend_hash_persist(&ce->function_table, zend_persist_class_method);
726 if (ce->default_properties_table) {
727 int i;
728
729 zend_accel_store(ce->default_properties_table, sizeof(zval) * ce->default_properties_count);
730 for (i = 0; i < ce->default_properties_count; i++) {
731 zend_persist_zval(&ce->default_properties_table[i]);
732 }
733 }
734 if (ce->default_static_members_table) {
735 int i;
736 zend_accel_store(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count);
737
738 /* Persist only static properties in this class.
739 * Static properties from parent classes will be handled in class_copy_ctor */
740 i = ce->parent ? ce->parent->default_static_members_count : 0;
741 for (; i < ce->default_static_members_count; i++) {
742 zend_persist_zval(&ce->default_static_members_table[i]);
743 }
744 }
745 ce->static_members_table = NULL;
746
747 zend_hash_persist(&ce->constants_table, zend_persist_class_constant);
748
749 if (ce->info.user.filename) {
750 /* do not free! PHP has centralized filename storage, compiler will free it */
751 zend_accel_memdup_string(ce->info.user.filename);
752 }
753 if (ce->info.user.doc_comment) {
754 if (ZCG(accel_directives).save_comments) {
755 zend_accel_store_interned_string(ce->info.user.doc_comment);
756 } else {
757 if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) {
758 zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment);
759 zend_string_release_ex(ce->info.user.doc_comment, 0);
760 }
761 ce->info.user.doc_comment = NULL;
762 }
763 }
764 zend_hash_persist(&ce->properties_info, zend_persist_property_info);
765 if (ce->num_interfaces && ce->interfaces) {
766 efree(ce->interfaces);
767 }
768 ce->interfaces = NULL; /* will be filled in on fetch */
769
770 if (ce->num_traits && ce->traits) {
771 efree(ce->traits);
772 }
773 ce->traits = NULL;
774
775 if (ce->trait_aliases) {
776 int i = 0;
777 while (ce->trait_aliases[i]) {
778 if (ce->trait_aliases[i]->trait_method.method_name) {
779 zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.method_name);
780 }
781 if (ce->trait_aliases[i]->trait_method.class_name) {
782 zend_accel_store_interned_string(ce->trait_aliases[i]->trait_method.class_name);
783 }
784
785 if (ce->trait_aliases[i]->alias) {
786 zend_accel_store_interned_string(ce->trait_aliases[i]->alias);
787 }
788
789 zend_accel_store(ce->trait_aliases[i], sizeof(zend_trait_alias));
790 i++;
791 }
792
793 zend_accel_store(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1));
794 }
795
796 if (ce->trait_precedences) {
797 int i = 0;
798 int j;
799
800 while (ce->trait_precedences[i]) {
801 zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.method_name);
802 zend_accel_store_interned_string(ce->trait_precedences[i]->trait_method.class_name);
803
804 for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) {
805 zend_accel_store_interned_string(ce->trait_precedences[i]->exclude_class_names[j]);
806 }
807
808 zend_accel_store(ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
809 i++;
810 }
811 zend_accel_store(
812 ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1));
813 }
814 }
815 }
816
817 //static int zend_update_property_info_ce(zval *zv)
818 //{
819 // zend_property_info *prop = Z_PTR_P(zv);
820 //
821 // prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce);
822 // return 0;
823 //}
824
zend_update_parent_ce(zval * zv)825 static int zend_update_parent_ce(zval *zv)
826 {
827 zend_class_entry *ce = Z_PTR_P(zv);
828
829 if (ce->parent) {
830 ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent);
831 }
832
833 /* update methods */
834 if (ce->constructor) {
835 ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor);
836 }
837 if (ce->destructor) {
838 ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor);
839 }
840 if (ce->clone) {
841 ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone);
842 }
843 if (ce->__get) {
844 ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get);
845 }
846 if (ce->__set) {
847 ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set);
848 }
849 if (ce->__call) {
850 ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call);
851 }
852 if (ce->serialize_func) {
853 ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func);
854 }
855 if (ce->unserialize_func) {
856 ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func);
857 }
858 if (ce->__isset) {
859 ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset);
860 }
861 if (ce->__unset) {
862 ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset);
863 }
864 if (ce->__tostring) {
865 ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring);
866 }
867 if (ce->__callstatic) {
868 ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic);
869 }
870 if (ce->__debugInfo) {
871 ce->__debugInfo = zend_shared_alloc_get_xlat_entry(ce->__debugInfo);
872 }
873 // zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce);
874 return 0;
875 }
876
zend_accel_persist_class_table(HashTable * class_table)877 static void zend_accel_persist_class_table(HashTable *class_table)
878 {
879 zend_hash_persist(class_table, zend_persist_class_entry);
880 zend_hash_apply(class_table, (apply_func_t) zend_update_parent_ce);
881 }
882
zend_accel_script_persist(zend_persistent_script * script,const char ** key,unsigned int key_length,int for_shm)883 zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, const char **key, unsigned int key_length, int for_shm)
884 {
885 script->mem = ZCG(mem);
886
887 ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
888 zend_shared_alloc_clear_xlat_table();
889
890 zend_accel_store(script, sizeof(zend_persistent_script));
891 if (key && *key) {
892 *key = zend_accel_memdup(*key, key_length + 1);
893 }
894
895 script->corrupted = 0;
896 ZCG(current_persistent_script) = script;
897
898 if (!for_shm) {
899 /* script is not going to be saved in SHM */
900 script->corrupted = 1;
901 }
902
903 zend_accel_store_interned_string(script->script.filename);
904
905 #if defined(__AVX__) || defined(__SSE2__)
906 /* Align to 64-byte boundary */
907 ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
908 #else
909 ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */
910 #endif
911
912 script->arena_mem = ZCG(arena_mem) = ZCG(mem);
913 ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size);
914
915 zend_accel_persist_class_table(&script->script.class_table);
916 zend_hash_persist(&script->script.function_table, zend_persist_op_array);
917 zend_persist_op_array_ex(&script->script.main_op_array, script);
918
919 script->corrupted = 0;
920 ZCG(current_persistent_script) = NULL;
921
922 return script;
923 }
924
zend_accel_script_persistable(zend_persistent_script * script)925 int zend_accel_script_persistable(zend_persistent_script *script)
926 {
927 return 1;
928 }
929