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