1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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_API.h"
23 #include "zend_constants.h"
24 #include "zend_accelerator_util_funcs.h"
25 #include "zend_persist.h"
26 #include "zend_shared_alloc.h"
27
28 #if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG
29 /* If sizeof(void*) == sizeof(zend_ulong) we can use zend_hash index functions */
30 # define accel_xlat_set(old, new) zend_hash_index_add_new_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old), (new))
31 # define accel_xlat_get(old) zend_hash_index_find_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old))
32 #else
33 # define accel_xlat_set(old, new) zend_hash_str_add_new_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (new))
34 # define accel_xlat_get(old) zend_hash_str_find_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*))
35 #endif
36
37 #define IN_ARENA(ptr) \
38 ((void*)(ptr) >= ZCG(current_persistent_script)->arena_mem && \
39 (void*)(ptr) < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size))
40
41 #define ARENA_REALLOC(ptr) \
42 (void*)(((char*)(ptr)) + ((char*)ZCG(arena_mem) - (char*)ZCG(current_persistent_script)->arena_mem))
43
44 typedef int (*id_function_t)(void *, void *);
45 typedef void (*unique_copy_ctor_func_t)(void *pElement);
46
create_persistent_script(void)47 zend_persistent_script* create_persistent_script(void)
48 {
49 zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script));
50 memset(persistent_script, 0, sizeof(zend_persistent_script));
51
52 zend_hash_init(&persistent_script->script.function_table, 0, NULL, ZEND_FUNCTION_DTOR, 0);
53 /* class_table is usually destroyed by free_persistent_script() that
54 * overrides destructor. ZEND_CLASS_DTOR may be used by standard
55 * PHP compiler
56 */
57 zend_hash_init(&persistent_script->script.class_table, 0, NULL, ZEND_CLASS_DTOR, 0);
58
59 return persistent_script;
60 }
61
free_persistent_script(zend_persistent_script * persistent_script,int destroy_elements)62 void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements)
63 {
64 if (!destroy_elements) {
65 persistent_script->script.function_table.pDestructor = NULL;
66 persistent_script->script.class_table.pDestructor = NULL;
67 }
68
69 zend_hash_destroy(&persistent_script->script.function_table);
70 zend_hash_destroy(&persistent_script->script.class_table);
71
72 if (persistent_script->script.filename) {
73 zend_string_release_ex(persistent_script->script.filename, 0);
74 }
75
76 efree(persistent_script);
77 }
78
zend_accel_move_user_functions(HashTable * src,uint32_t count,zend_script * script)79 void zend_accel_move_user_functions(HashTable *src, uint32_t count, zend_script *script)
80 {
81 Bucket *p, *end;
82 HashTable *dst;
83 zend_string *filename;
84 dtor_func_t orig_dtor;
85 zend_function *function;
86
87 if (!count) {
88 return;
89 }
90
91 dst = &script->function_table;
92 filename = script->main_op_array.filename;
93 orig_dtor = src->pDestructor;
94 src->pDestructor = NULL;
95 zend_hash_extend(dst, count, 0);
96 end = src->arData + src->nNumUsed;
97 p = end - count;
98 for (; p != end; p++) {
99 if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
100 function = Z_PTR(p->val);
101 if (EXPECTED(function->type == ZEND_USER_FUNCTION)
102 && EXPECTED(function->op_array.filename == filename)) {
103 _zend_hash_append_ptr(dst, p->key, function);
104 zend_hash_del_bucket(src, p);
105 }
106 }
107 src->pDestructor = orig_dtor;
108 }
109
zend_accel_move_user_classes(HashTable * src,uint32_t count,zend_script * script)110 void zend_accel_move_user_classes(HashTable *src, uint32_t count, zend_script *script)
111 {
112 Bucket *p, *end;
113 HashTable *dst;
114 zend_string *filename;
115 dtor_func_t orig_dtor;
116 zend_class_entry *ce;
117
118 if (!count) {
119 return;
120 }
121
122 dst = &script->class_table;
123 filename = script->main_op_array.filename;
124 orig_dtor = src->pDestructor;
125 src->pDestructor = NULL;
126 zend_hash_extend(dst, count, 0);
127 end = src->arData + src->nNumUsed;
128 p = end - count;
129 for (; p != end; p++) {
130 if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
131 ce = Z_PTR(p->val);
132 if (EXPECTED(ce->type == ZEND_USER_CLASS)
133 && EXPECTED(ce->info.user.filename == filename)) {
134 _zend_hash_append_ptr(dst, p->key, ce);
135 zend_hash_del_bucket(src, p);
136 }
137 }
138 src->pDestructor = orig_dtor;
139 }
140
zend_hash_clone_constants(HashTable * ht)141 static void zend_hash_clone_constants(HashTable *ht)
142 {
143 Bucket *p, *end;
144 zend_class_constant *c;
145
146 if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
147 return;
148 }
149
150 p = emalloc(HT_SIZE(ht));
151 memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
152 HT_SET_DATA_ADDR(ht, p);
153
154 p = ht->arData;
155 end = p + ht->nNumUsed;
156 for (; p != end; p++) {
157 ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
158 c = Z_PTR(p->val);
159 if (IN_ARENA(c)) {
160 c = ARENA_REALLOC(c);
161 Z_PTR(p->val) = c;
162
163 if (IN_ARENA(c->ce)) {
164 c->ce = ARENA_REALLOC(c->ce);
165 }
166 }
167 }
168 }
169
zend_hash_clone_methods(HashTable * ht)170 static void zend_hash_clone_methods(HashTable *ht)
171 {
172 Bucket *p, *end;
173 zend_op_array *new_entry;
174
175 ht->pDestructor = ZEND_FUNCTION_DTOR;
176
177 if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
178 return;
179 }
180
181 p = emalloc(HT_SIZE(ht));
182 memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
183 HT_SET_DATA_ADDR(ht, p);
184
185 p = ht->arData;
186 end = p + ht->nNumUsed;
187 for (; p != end; p++) {
188 ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
189 new_entry = Z_PTR(p->val);
190 if (IN_ARENA(new_entry)) {
191 new_entry = ARENA_REALLOC(new_entry);
192 Z_PTR(p->val) = new_entry;
193
194 if (IN_ARENA(new_entry->scope)) {
195 new_entry->scope = ARENA_REALLOC(new_entry->scope);
196
197 /* update prototype */
198 if (IN_ARENA(new_entry->prototype)) {
199 new_entry->prototype = ARENA_REALLOC(new_entry->prototype);
200 }
201 }
202 if (IN_ARENA(ZEND_MAP_PTR(new_entry->run_time_cache))) {
203 ZEND_MAP_PTR_INIT(new_entry->run_time_cache, ARENA_REALLOC(ZEND_MAP_PTR(new_entry->run_time_cache)));
204 }
205 ZEND_MAP_PTR_INIT(new_entry->static_variables_ptr, &new_entry->static_variables);
206 }
207 }
208 }
209
zend_hash_clone_prop_info(HashTable * ht)210 static void zend_hash_clone_prop_info(HashTable *ht)
211 {
212 Bucket *p, *end;
213 zend_property_info *prop_info;
214
215 if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
216 return;
217 }
218
219 p = emalloc(HT_SIZE(ht));
220 memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht));
221 HT_SET_DATA_ADDR(ht, p);
222
223 p = ht->arData;
224 end = p + ht->nNumUsed;
225 for (; p != end; p++) {
226 ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
227 prop_info = Z_PTR(p->val);
228 if (IN_ARENA(prop_info)) {
229 prop_info = ARENA_REALLOC(prop_info);
230 Z_PTR(p->val) = prop_info;
231
232 if (IN_ARENA(prop_info->ce)) {
233 prop_info->ce = ARENA_REALLOC(prop_info->ce);
234 }
235
236 if (ZEND_TYPE_IS_CE(prop_info->type)) {
237 zend_class_entry *ce = ZEND_TYPE_CE(prop_info->type);
238 if (IN_ARENA(ce)) {
239 ce = ARENA_REALLOC(ce);
240 prop_info->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop_info->type));
241 }
242 }
243 }
244 }
245 }
246
247 #define zend_update_inherited_handler(handler) \
248 { \
249 if (ce->handler != NULL && IN_ARENA(ce->handler)) { \
250 ce->handler = ARENA_REALLOC(ce->handler); \
251 } \
252 }
253
254 /* Protects class' refcount, copies default properties, functions and class name */
zend_class_copy_ctor(zend_class_entry ** pce)255 static void zend_class_copy_ctor(zend_class_entry **pce)
256 {
257 zend_class_entry *ce = *pce;
258 zval *src, *dst, *end;
259
260 *pce = ce = ARENA_REALLOC(ce);
261 ce->refcount = 1;
262
263 if ((ce->ce_flags & ZEND_ACC_LINKED) && IN_ARENA(ce->parent)) {
264 ce->parent = ARENA_REALLOC(ce->parent);
265 }
266
267 if (ce->default_properties_table) {
268 dst = emalloc(sizeof(zval) * ce->default_properties_count);
269 src = ce->default_properties_table;
270 end = src + ce->default_properties_count;
271 ce->default_properties_table = dst;
272 for (; src != end; src++, dst++) {
273 ZVAL_COPY_VALUE_PROP(dst, src);
274 }
275 }
276
277 zend_hash_clone_methods(&ce->function_table);
278
279 /* static members */
280 if (ce->default_static_members_table) {
281 int i, end;
282 zend_class_entry *parent = !(ce->ce_flags & ZEND_ACC_LINKED) ? NULL : ce->parent;
283
284 dst = emalloc(sizeof(zval) * ce->default_static_members_count);
285 src = ce->default_static_members_table;
286 ce->default_static_members_table = dst;
287 i = ce->default_static_members_count - 1;
288
289 /* Copy static properties in this class */
290 end = parent ? parent->default_static_members_count : 0;
291 for (; i >= end; i--) {
292 zval *p = &dst[i];
293 ZVAL_COPY_VALUE(p, &src[i]);
294 }
295
296 /* Create indirections to static properties from parent classes */
297 while (parent && parent->default_static_members_table) {
298 end = parent->parent ? parent->parent->default_static_members_count : 0;
299 for (; i >= end; i--) {
300 zval *p = &dst[i];
301 ZVAL_INDIRECT(p, &parent->default_static_members_table[i]);
302 }
303
304 parent = parent->parent;
305 }
306 }
307 ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table);
308
309 /* properties_info */
310 zend_hash_clone_prop_info(&ce->properties_info);
311
312 /* constants table */
313 zend_hash_clone_constants(&ce->constants_table);
314
315 if (ce->properties_info_table) {
316 int i;
317 ce->properties_info_table = ARENA_REALLOC(ce->properties_info_table);
318 for (i = 0; i < ce->default_properties_count; i++) {
319 if (IN_ARENA(ce->properties_info_table[i])) {
320 ce->properties_info_table[i] = ARENA_REALLOC(ce->properties_info_table[i]);
321 }
322 }
323 }
324
325 if (ce->num_interfaces) {
326 zend_class_name *interface_names;
327
328 if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
329 interface_names = emalloc(sizeof(zend_class_name) * ce->num_interfaces);
330 memcpy(interface_names, ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
331 ce->interface_names = interface_names;
332 } else {
333 zend_class_entry **interfaces = emalloc(sizeof(zend_class_entry*) * ce->num_interfaces);
334 uint32_t i;
335
336 for (i = 0; i < ce->num_interfaces; i++) {
337 if (IN_ARENA(ce->interfaces[i])) {
338 interfaces[i] = ARENA_REALLOC(ce->interfaces[i]);
339 } else {
340 interfaces[i] = ce->interfaces[i];
341 }
342 }
343 ce->interfaces = interfaces;
344 }
345 }
346
347 zend_update_inherited_handler(constructor);
348 zend_update_inherited_handler(destructor);
349 zend_update_inherited_handler(clone);
350 zend_update_inherited_handler(__get);
351 zend_update_inherited_handler(__set);
352 zend_update_inherited_handler(__call);
353 /* 5.1 stuff */
354 zend_update_inherited_handler(serialize_func);
355 zend_update_inherited_handler(unserialize_func);
356 zend_update_inherited_handler(__isset);
357 zend_update_inherited_handler(__unset);
358 /* 5.2 stuff */
359 zend_update_inherited_handler(__tostring);
360
361 /* 5.3 stuff */
362 zend_update_inherited_handler(__callstatic);
363 zend_update_inherited_handler(__debugInfo);
364
365 /* 5.4 traits */
366 if (ce->num_traits) {
367 zend_class_name *trait_names = emalloc(sizeof(zend_class_name) * ce->num_traits);
368
369 memcpy(trait_names, ce->trait_names, sizeof(zend_class_name) * ce->num_traits);
370 ce->trait_names = trait_names;
371
372 if (ce->trait_aliases) {
373 zend_trait_alias **trait_aliases;
374 int i = 0;
375
376 while (ce->trait_aliases[i]) {
377 i++;
378 }
379 trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i + 1));
380 i = 0;
381 while (ce->trait_aliases[i]) {
382 trait_aliases[i] = emalloc(sizeof(zend_trait_alias));
383 memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias));
384 i++;
385 }
386 trait_aliases[i] = NULL;
387 ce->trait_aliases = trait_aliases;
388 }
389
390 if (ce->trait_precedences) {
391 zend_trait_precedence **trait_precedences;
392 int i = 0;
393
394 while (ce->trait_precedences[i]) {
395 i++;
396 }
397 trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i + 1));
398 i = 0;
399 while (ce->trait_precedences[i]) {
400 trait_precedences[i] = emalloc(sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
401 memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence) + (ce->trait_precedences[i]->num_excludes - 1) * sizeof(zend_string*));
402 i++;
403 }
404 trait_precedences[i] = NULL;
405 ce->trait_precedences = trait_precedences;
406 }
407 }
408 }
409
zend_accel_function_hash_copy(HashTable * target,HashTable * source)410 static void zend_accel_function_hash_copy(HashTable *target, HashTable *source)
411 {
412 zend_function *function1, *function2;
413 Bucket *p, *end;
414 zval *t;
415
416 zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
417 p = source->arData;
418 end = p + source->nNumUsed;
419 for (; p != end; p++) {
420 ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
421 ZEND_ASSERT(p->key);
422 t = zend_hash_find_ex(target, p->key, 1);
423 if (UNEXPECTED(t != NULL)) {
424 if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
425 /* Runtime definition key. There are two circumstances under which the key can
426 * already be defined:
427 * 1. The file has been re-included without being changed in the meantime. In
428 * this case we can keep the old value, because we know that the definition
429 * hasn't changed.
430 * 2. The file has been changed in the meantime, but the RTD key ends up colliding.
431 * This would be a bug.
432 * As we can't distinguish these cases, we assume that it is 1. and keep the old
433 * value. */
434 continue;
435 } else {
436 goto failure;
437 }
438 } else {
439 _zend_hash_append_ptr(target, p->key, Z_PTR(p->val));
440 }
441 }
442 target->nInternalPointer = 0;
443 return;
444
445 failure:
446 function1 = Z_PTR(p->val);
447 function2 = Z_PTR_P(t);
448 CG(in_compilation) = 1;
449 zend_set_compiled_filename(function1->op_array.filename);
450 CG(zend_lineno) = function1->op_array.opcodes[0].lineno;
451 if (function2->type == ZEND_USER_FUNCTION
452 && function2->op_array.last > 0) {
453 zend_error(E_ERROR, "Cannot redeclare %s() (previously declared in %s:%d)",
454 ZSTR_VAL(function1->common.function_name),
455 ZSTR_VAL(function2->op_array.filename),
456 (int)function2->op_array.opcodes[0].lineno);
457 } else {
458 zend_error(E_ERROR, "Cannot redeclare %s()", ZSTR_VAL(function1->common.function_name));
459 }
460 }
461
zend_accel_function_hash_copy_from_shm(HashTable * target,HashTable * source)462 static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable *source)
463 {
464 zend_function *function1, *function2;
465 Bucket *p, *end;
466 zval *t;
467
468 zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
469 p = source->arData;
470 end = p + source->nNumUsed;
471 for (; p != end; p++) {
472 ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
473 ZEND_ASSERT(p->key);
474 t = zend_hash_find_ex(target, p->key, 1);
475 if (UNEXPECTED(t != NULL)) {
476 if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
477 /* See comment in zend_accel_function_hash_copy(). */
478 continue;
479 } else {
480 goto failure;
481 }
482 } else {
483 _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
484 }
485 }
486 target->nInternalPointer = 0;
487 return;
488
489 failure:
490 function1 = Z_PTR(p->val);
491 function2 = Z_PTR_P(t);
492 CG(in_compilation) = 1;
493 zend_set_compiled_filename(function1->op_array.filename);
494 CG(zend_lineno) = function1->op_array.opcodes[0].lineno;
495 if (function2->type == ZEND_USER_FUNCTION
496 && function2->op_array.last > 0) {
497 zend_error(E_ERROR, "Cannot redeclare %s() (previously declared in %s:%d)",
498 ZSTR_VAL(function1->common.function_name),
499 ZSTR_VAL(function2->op_array.filename),
500 (int)function2->op_array.opcodes[0].lineno);
501 } else {
502 zend_error(E_ERROR, "Cannot redeclare %s()", ZSTR_VAL(function1->common.function_name));
503 }
504 }
505
zend_accel_class_hash_copy(HashTable * target,HashTable * source)506 static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
507 {
508 Bucket *p, *end;
509 zval *t;
510
511 zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
512 p = source->arData;
513 end = p + source->nNumUsed;
514 for (; p != end; p++) {
515 if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
516 ZEND_ASSERT(p->key);
517 t = zend_hash_find_ex(target, p->key, 1);
518 if (UNEXPECTED(t != NULL)) {
519 if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
520 /* See comment in zend_accel_function_hash_copy(). */
521 continue;
522 } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) {
523 zend_class_entry *ce1 = Z_PTR(p->val);
524 if (!(ce1->ce_flags & ZEND_ACC_ANON_CLASS)) {
525 CG(in_compilation) = 1;
526 zend_set_compiled_filename(ce1->info.user.filename);
527 CG(zend_lineno) = ce1->info.user.line_start;
528 zend_error(E_ERROR,
529 "Cannot declare %s %s, because the name is already in use",
530 zend_get_object_type(ce1), ZSTR_VAL(ce1->name));
531 return;
532 }
533 continue;
534 }
535 } else {
536 t = _zend_hash_append_ptr(target, p->key, Z_PTR(p->val));
537 }
538 }
539 target->nInternalPointer = 0;
540 return;
541 }
542
zend_accel_class_hash_copy_from_shm(HashTable * target,HashTable * source)543 static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *source)
544 {
545 Bucket *p, *end;
546 zval *t;
547
548 zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
549 p = source->arData;
550 end = p + source->nNumUsed;
551 for (; p != end; p++) {
552 ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF);
553 ZEND_ASSERT(p->key);
554 t = zend_hash_find_ex(target, p->key, 1);
555 if (UNEXPECTED(t != NULL)) {
556 if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) {
557 /* See comment in zend_accel_function_hash_copy(). */
558 continue;
559 } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) {
560 zend_class_entry *ce1 = Z_PTR(p->val);
561 if (!(ce1->ce_flags & ZEND_ACC_ANON_CLASS)) {
562 CG(in_compilation) = 1;
563 zend_set_compiled_filename(ce1->info.user.filename);
564 CG(zend_lineno) = ce1->info.user.line_start;
565 zend_error(E_ERROR,
566 "Cannot declare %s %s, because the name is already in use",
567 zend_get_object_type(ce1), ZSTR_VAL(ce1->name));
568 return;
569 }
570 continue;
571 }
572 } else {
573 t = _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
574 if (!(((zend_class_entry*)Z_PTR_P(t))->ce_flags & ZEND_ACC_IMMUTABLE)) {
575 zend_class_copy_ctor((zend_class_entry**)&Z_PTR_P(t));
576 }
577 }
578 }
579 target->nInternalPointer = 0;
580 return;
581 }
582
583 #if __has_feature(memory_sanitizer)
584 # define fast_memcpy memcpy
585 #elif defined(__AVX__)
586 # include <nmmintrin.h>
587 # if defined(__GNUC__) && defined(__i386__)
fast_memcpy(void * dest,const void * src,size_t size)588 static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
589 {
590 size_t delta = (char*)dest - (char*)src;
591
592 __asm__ volatile (
593 ".align 16\n\t"
594 ".LL0%=:\n\t"
595 "prefetchnta 0x40(%1)\n\t"
596 "vmovaps (%1), %%ymm0\n\t"
597 "vmovaps 0x20(%1), %%ymm1\n\t"
598 "vmovaps %%ymm0, (%1,%2)\n\t"
599 "vmovaps %%ymm1, 0x20(%1,%2)\n\t"
600 "addl $0x40, %1\n\t"
601 "subl $0x40, %0\n\t"
602 "ja .LL0%="
603 : "+r"(size),
604 "+r"(src)
605 : "r"(delta)
606 : "cc", "memory", "%ymm0", "%ymm1");
607 }
608 # elif defined(__GNUC__) && defined(__x86_64__)
fast_memcpy(void * dest,const void * src,size_t size)609 static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
610 {
611 size_t delta = (char*)dest - (char*)src;
612
613 __asm__ volatile (
614 ".align 16\n\t"
615 ".LL0%=:\n\t"
616 "prefetchnta 0x40(%1)\n\t"
617 "vmovaps (%1), %%ymm0\n\t"
618 "vmovaps 0x20(%1), %%ymm1\n\t"
619 "vmovaps %%ymm0, (%1,%2)\n\t"
620 "vmovaps %%ymm1, 0x20(%1,%2)\n\t"
621 "addq $0x40, %1\n\t"
622 "subq $0x40, %0\n\t"
623 "ja .LL0%="
624 : "+r"(size),
625 "+r"(src)
626 : "r"(delta)
627 : "cc", "memory", "%ymm0", "%ymm1");
628 }
629 # else
fast_memcpy(void * dest,const void * src,size_t size)630 static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
631 {
632 __m256 *dqdest = (__m256*)dest;
633 const __m256 *dqsrc = (const __m256*)src;
634 const __m256 *end = (const __m256*)((const char*)src + size);
635
636 do {
637 #ifdef PHP_WIN32
638 _mm_prefetch((const char *)(dqsrc + 2), _MM_HINT_NTA);
639 #else
640 _mm_prefetch(dqsrc + 2, _MM_HINT_NTA);
641 #endif
642
643 __m256 ymm0 = _mm256_load_ps((const float *)(dqsrc + 0));
644 __m256 ymm1 = _mm256_load_ps((const float *)(dqsrc + 1));
645 dqsrc += 2;
646 _mm256_store_ps((float *)(dqdest + 0), ymm0);
647 _mm256_store_ps((float *)(dqdest + 1), ymm1);
648 dqdest += 2;
649 } while (dqsrc != end);
650 }
651 # endif
652 #elif defined(__SSE2__)
653 # include <emmintrin.h>
654 # if defined(__GNUC__) && defined(__i386__)
fast_memcpy(void * dest,const void * src,size_t size)655 static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
656 {
657 size_t delta = (char*)dest - (char*)src;
658
659 __asm__ volatile (
660 ".align 16\n\t"
661 ".LL0%=:\n\t"
662 "prefetchnta 0x40(%1)\n\t"
663 "movdqa (%1), %%xmm0\n\t"
664 "movdqa 0x10(%1), %%xmm1\n\t"
665 "movdqa 0x20(%1), %%xmm2\n\t"
666 "movdqa 0x30(%1), %%xmm3\n\t"
667 "movdqa %%xmm0, (%1,%2)\n\t"
668 "movdqa %%xmm1, 0x10(%1,%2)\n\t"
669 "movdqa %%xmm2, 0x20(%1,%2)\n\t"
670 "movdqa %%xmm3, 0x30(%1,%2)\n\t"
671 "addl $0x40, %1\n\t"
672 "subl $0x40, %0\n\t"
673 "ja .LL0%="
674 : "+r"(size),
675 "+r"(src)
676 : "r"(delta)
677 : "cc", "memory", "%xmm0", "%xmm1", "%xmm1", "%xmm2");
678 }
679 # elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__)
fast_memcpy(void * dest,const void * src,size_t size)680 static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
681 {
682 size_t delta = (char*)dest - (char*)src;
683
684 __asm__ volatile (
685 ".align 16\n\t"
686 ".LL0%=:\n\t"
687 "prefetchnta 0x40(%1)\n\t"
688 "movdqa (%1), %%xmm0\n\t"
689 "movdqa 0x10(%1), %%xmm1\n\t"
690 "movdqa 0x20(%1), %%xmm2\n\t"
691 "movdqa 0x30(%1), %%xmm3\n\t"
692 "movdqa %%xmm0, (%1,%2)\n\t"
693 "movdqa %%xmm1, 0x10(%1,%2)\n\t"
694 "movdqa %%xmm2, 0x20(%1,%2)\n\t"
695 "movdqa %%xmm3, 0x30(%1,%2)\n\t"
696 "addq $0x40, %1\n\t"
697 "subq $0x40, %0\n\t"
698 "ja .LL0%="
699 : "+r"(size),
700 "+r"(src)
701 : "r"(delta)
702 : "cc", "memory", "%xmm0", "%xmm1", "%xmm1", "%xmm2");
703 }
704 # else
fast_memcpy(void * dest,const void * src,size_t size)705 static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size)
706 {
707 __m128i *dqdest = (__m128i*)dest;
708 const __m128i *dqsrc = (const __m128i*)src;
709 const __m128i *end = (const __m128i*)((const char*)src + size);
710
711 do {
712 #ifdef PHP_WIN32
713 _mm_prefetch((const char *)(dqsrc + 4), _MM_HINT_NTA);
714 #else
715 _mm_prefetch(dqsrc + 4, _MM_HINT_NTA);
716 #endif
717
718 __m128i xmm0 = _mm_load_si128(dqsrc + 0);
719 __m128i xmm1 = _mm_load_si128(dqsrc + 1);
720 __m128i xmm2 = _mm_load_si128(dqsrc + 2);
721 __m128i xmm3 = _mm_load_si128(dqsrc + 3);
722 dqsrc += 4;
723 _mm_store_si128(dqdest + 0, xmm0);
724 _mm_store_si128(dqdest + 1, xmm1);
725 _mm_store_si128(dqdest + 2, xmm2);
726 _mm_store_si128(dqdest + 3, xmm3);
727 dqdest += 4;
728 } while (dqsrc != end);
729 }
730 # endif
731 #endif
732
zend_accel_load_script(zend_persistent_script * persistent_script,int from_shared_memory)733 zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory)
734 {
735 zend_op_array *op_array;
736
737 op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
738 *op_array = persistent_script->script.main_op_array;
739 ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables);
740
741 if (EXPECTED(from_shared_memory)) {
742 zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);
743
744 ZCG(current_persistent_script) = persistent_script;
745 ZCG(arena_mem) = NULL;
746 if (EXPECTED(persistent_script->arena_size)) {
747 #if defined(__AVX__) || defined(__SSE2__)
748 /* Target address must be aligned to 64-byte boundary */
749 _mm_prefetch(persistent_script->arena_mem, _MM_HINT_NTA);
750 ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size + 64);
751 ZCG(arena_mem) = (void*)(((zend_uintptr_t)ZCG(arena_mem) + 63L) & ~63L);
752 fast_memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size);
753 #else
754 ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size);
755 memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size);
756 #endif
757 }
758
759 /* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
760 if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
761 zend_accel_class_hash_copy_from_shm(CG(class_table), &persistent_script->script.class_table);
762 }
763 /* we must first to copy all classes and then prepare functions, since functions may try to bind
764 classes - which depend on pre-bind class entries existent in the class table */
765 if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
766 zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->script.function_table);
767 }
768
769 /* Register __COMPILER_HALT_OFFSET__ constant */
770 if (persistent_script->compiler_halt_offset != 0 &&
771 persistent_script->script.filename) {
772 zend_string *name;
773 static const char haltoff[] = "__COMPILER_HALT_OFFSET__";
774
775 name = zend_mangle_property_name(haltoff, sizeof(haltoff) - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename), 0);
776 if (!zend_hash_exists(EG(zend_constants), name)) {
777 zend_register_long_constant(ZSTR_VAL(name), ZSTR_LEN(name), persistent_script->compiler_halt_offset, CONST_CS, 0);
778 }
779 zend_string_release_ex(name, 0);
780 }
781
782 zend_hash_destroy(&ZCG(bind_hash));
783 ZCG(current_persistent_script) = NULL;
784 zend_map_ptr_extend(ZCSG(map_ptr_last));
785 } else /* if (!from_shared_memory) */ {
786 if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
787 zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table);
788 }
789 if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
790 zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table);
791 }
792 }
793
794 if (persistent_script->script.first_early_binding_opline != (uint32_t)-1) {
795 zend_string *orig_compiled_filename = CG(compiled_filename);
796 CG(compiled_filename) = persistent_script->script.filename;
797 zend_do_delayed_early_binding(op_array, persistent_script->script.first_early_binding_opline);
798 CG(compiled_filename) = orig_compiled_filename;
799 }
800
801 if (UNEXPECTED(!from_shared_memory)) {
802 free_persistent_script(persistent_script, 0); /* free only hashes */
803 }
804
805 return op_array;
806 }
807
808 /*
809 * zend_adler32() is based on zlib implementation
810 * Computes the Adler-32 checksum of a data stream
811 *
812 * Copyright (C) 1995-2005 Mark Adler
813 * For conditions of distribution and use, see copyright notice in zlib.h
814 *
815 * Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
816 *
817 * This software is provided 'as-is', without any express or implied
818 * warranty. In no event will the authors be held liable for any damages
819 * arising from the use of this software.
820 *
821 * Permission is granted to anyone to use this software for any purpose,
822 * including commercial applications, and to alter it and redistribute it
823 * freely, subject to the following restrictions:
824 *
825 * 1. The origin of this software must not be misrepresented; you must not
826 * claim that you wrote the original software. If you use this software
827 * in a product, an acknowledgment in the product documentation would be
828 * appreciated but is not required.
829 * 2. Altered source versions must be plainly marked as such, and must not be
830 * misrepresented as being the original software.
831 * 3. This notice may not be removed or altered from any source distribution.
832 *
833 */
834
835 #define ADLER32_BASE 65521 /* largest prime smaller than 65536 */
836 #define ADLER32_NMAX 5552
837 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
838
839 #define ADLER32_DO1(buf) {s1 += *(buf); s2 += s1;}
840 #define ADLER32_DO2(buf, i) ADLER32_DO1(buf + i); ADLER32_DO1(buf + i + 1);
841 #define ADLER32_DO4(buf, i) ADLER32_DO2(buf, i); ADLER32_DO2(buf, i + 2);
842 #define ADLER32_DO8(buf, i) ADLER32_DO4(buf, i); ADLER32_DO4(buf, i + 4);
843 #define ADLER32_DO16(buf) ADLER32_DO8(buf, 0); ADLER32_DO8(buf, 8);
844
zend_adler32(unsigned int checksum,unsigned char * buf,uint32_t len)845 unsigned int zend_adler32(unsigned int checksum, unsigned char *buf, uint32_t len)
846 {
847 unsigned int s1 = checksum & 0xffff;
848 unsigned int s2 = (checksum >> 16) & 0xffff;
849 unsigned char *end;
850
851 while (len >= ADLER32_NMAX) {
852 len -= ADLER32_NMAX;
853 end = buf + ADLER32_NMAX;
854 do {
855 ADLER32_DO16(buf);
856 buf += 16;
857 } while (buf != end);
858 s1 %= ADLER32_BASE;
859 s2 %= ADLER32_BASE;
860 }
861
862 if (len) {
863 if (len >= 16) {
864 end = buf + (len & 0xfff0);
865 len &= 0xf;
866 do {
867 ADLER32_DO16(buf);
868 buf += 16;
869 } while (buf != end);
870 }
871 if (len) {
872 end = buf + len;
873 do {
874 ADLER32_DO1(buf);
875 buf++;
876 } while (buf != end);
877 }
878 s1 %= ADLER32_BASE;
879 s2 %= ADLER32_BASE;
880 }
881
882 return (s2 << 16) | s1;
883 }
884
zend_accel_script_checksum(zend_persistent_script * persistent_script)885 unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script)
886 {
887 unsigned char *mem = (unsigned char*)persistent_script->mem;
888 size_t size = persistent_script->size;
889 size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script;
890 unsigned int checksum = ADLER32_INIT;
891
892 if (mem < (unsigned char*)persistent_script) {
893 checksum = zend_adler32(checksum, mem, (unsigned char*)persistent_script - mem);
894 size -= (unsigned char*)persistent_script - mem;
895 mem += (unsigned char*)persistent_script - mem;
896 }
897
898 zend_adler32(checksum, mem, persistent_script_check_block_size);
899 mem += sizeof(*persistent_script);
900 size -= sizeof(*persistent_script);
901
902 if (size > 0) {
903 checksum = zend_adler32(checksum, mem, size);
904 }
905 return checksum;
906 }
907