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