xref: /PHP-7.3/ext/opcache/zend_persist.c (revision 9afce019)
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(&copy->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