XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

549 lines
15 KiB

  1. dnl {{{ === program start ========================================
  2. divert(0)
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include "php.h"
  6. #include "zend_extensions.h"
  7. #include "zend_compile.h"
  8. #include "zend_API.h"
  9. #include "zend_ini.h"
  10. /* export: #include "xcache.h" :export */
  11. #include "xcache.h"
  12. /* export: #include "mod_cacher/xc_cache.h" :export */
  13. #include "mod_cacher/xc_cache.h"
  14. #include "util/xc_align.h"
  15. #include "xcache/xc_const_string.h"
  16. #include "xcache/xc_utils.h"
  17. #include "xc_processor.h"
  18. #include "xcache_globals.h"
  19. #if defined(HARDENING_PATCH_HASH_PROTECT) && HARDENING_PATCH_HASH_PROTECT
  20. extern unsigned int zend_hash_canary;
  21. #endif
  22. define(`SIZEOF_zend_uint', `sizeof(zend_uint)')
  23. define(`COUNTOF_zend_uint', `1')
  24. define(`SIZEOF_int', `sizeof(int)')
  25. define(`COUNTOF_int', `1')
  26. define(`SIZEOF_zend_function', `sizeof(zend_function)')
  27. define(`COUNTOF_zend_function', `1')
  28. define(`SIZEOF_zval_ptr', `sizeof(zval_ptr)')
  29. define(`COUNTOF_zval_ptr', `1')
  30. define(`SIZEOF_zval_ptr_nullable', `sizeof(zval_ptr_nullable)')
  31. define(`COUNTOF_zval_ptr_nullable', `1')
  32. define(`SIZEOF_zend_trait_alias_ptr', `sizeof(zend_trait_alias)')
  33. define(`COUNTOF_zend_trait_alias_ptr', `1')
  34. define(`SIZEOF_zend_trait_precedence_ptr', `sizeof(zend_trait_precedence)')
  35. define(`COUNTOF_zend_trait_precedence_ptr', `1')
  36. define(`SIZEOF_xc_entry_name_t', `sizeof(xc_entry_name_t)')
  37. define(`COUNTOF_xc_entry_name_t', `1')
  38. define(`SIZEOF_xc_ztstring', `sizeof(xc_ztstring)')
  39. define(`COUNTOF_xc_ztstring', `1')
  40. ifdef(`XCACHE_ENABLE_TEST', `
  41. #undef NDEBUG
  42. #include <assert.h>
  43. m4_errprint(`AUTOCHECK INFO: runtime autocheck Enabled (debug build)')
  44. ', `
  45. m4_errprint(`AUTOCHECK INFO: runtime autocheck Disabled (optimized build)')
  46. ')
  47. ifdef(`DEBUG_SIZE', `static int xc_totalsize = 0;')
  48. sinclude(builddir`/structinfo.m4')
  49. #ifndef NDEBUG
  50. # undef inline
  51. #define inline
  52. #endif
  53. typedef zval *zval_ptr;
  54. typedef zval *zval_ptr_nullable;
  55. typedef char *xc_ztstring;
  56. #ifdef ZEND_ENGINE_2_4
  57. typedef zend_trait_alias *zend_trait_alias_ptr;
  58. typedef zend_trait_precedence *zend_trait_precedence_ptr;
  59. #endif
  60. #ifdef ZEND_ENGINE_2_3
  61. typedef int last_brk_cont_t;
  62. #else
  63. typedef zend_uint last_brk_cont_t;
  64. #endif
  65. typedef zend_uchar xc_zval_type_t;
  66. typedef int xc_op_type;
  67. typedef zend_uchar xc_opcode;
  68. #ifdef IS_UNICODE
  69. typedef UChar zstr_uchar;
  70. #endif
  71. typedef char zstr_char;
  72. #define MAX_DUP_STR_LEN 256
  73. dnl }}}
  74. /* export: typedef struct _xc_processor_t xc_processor_t; :export {{{ */
  75. struct _xc_processor_t {
  76. char *p;
  77. zend_uint size;
  78. HashTable strings;
  79. HashTable zvalptrs;
  80. zend_bool reference; /* enable if to deal with reference */
  81. zend_bool have_references;
  82. const xc_entry_php_t *entry_php_src;
  83. const xc_entry_php_t *entry_php_dst;
  84. const xc_entry_data_php_t *php_src;
  85. const xc_entry_data_php_t *php_dst;
  86. const xc_cache_t *cache;
  87. const zend_class_entry *cache_ce;
  88. zend_uint cache_class_index;
  89. const zend_op_array *active_op_array_src;
  90. zend_op_array *active_op_array_dst;
  91. const zend_class_entry *active_class_entry_src;
  92. zend_class_entry *active_class_entry_dst;
  93. zend_uint active_class_index;
  94. zend_uint active_op_array_index;
  95. const xc_op_array_info_t *active_op_array_infos_src;
  96. zend_bool readonly_protection; /* wheather it's present */
  97. IFAUTOCHECK(xc_stack_t allocsizes;)
  98. };
  99. /* }}} */
  100. /* export: typedef struct _xc_dasm_t { const zend_op_array *active_op_array_src; } xc_dasm_t; :export {{{ */
  101. /* }}} */
  102. /* {{{ memsetptr */
  103. IFAUTOCHECK(`dnl
  104. static void *memsetptr(void *mem, void *content, size_t n)
  105. {
  106. void **p = (void **) mem;
  107. void **end = (void **) ((char *) mem + n);
  108. while (p < end - sizeof(content)) {
  109. *p = content;
  110. p += sizeof(content);
  111. }
  112. if (p < end) {
  113. memset(p, -1, end - p);
  114. }
  115. return mem;
  116. }
  117. ')
  118. /* }}} */
  119. #ifdef HAVE_XCACHE_DPRINT
  120. static void xc_dprint_indent(int indent) /* {{{ */
  121. {
  122. int i;
  123. for (i = 0; i < indent; i ++) {
  124. fprintf(stderr, " ");
  125. }
  126. }
  127. /* }}} */
  128. static void xc_dprint_str_len(const char *str, int len) /* {{{ */
  129. {
  130. const unsigned char *p = (const unsigned char *) str;
  131. int i;
  132. for (i = 0; i < len; i ++) {
  133. if (p[i] < 32 || p[i] == 127) {
  134. fprintf(stderr, "\\%03o", (unsigned int) p[i]);
  135. }
  136. else {
  137. fputc(p[i], stderr);
  138. }
  139. }
  140. }
  141. /* }}} */
  142. #endif
  143. /* {{{ xc_zstrlen_char */
  144. static inline int xc_zstrlen_char(const_zstr s)
  145. {
  146. return strlen(ZSTR_S(s));
  147. }
  148. /* }}} */
  149. #ifdef IS_UNICODE
  150. /* {{{ xc_zstrlen_uchar */
  151. static inline int xc_zstrlen_uchar(zstr s)
  152. {
  153. return u_strlen(ZSTR_U(s));
  154. }
  155. /* }}} */
  156. /* {{{ xc_zstrlen */
  157. static inline int xc_zstrlen(int type, const_zstr s)
  158. {
  159. return type == IS_UNICODE ? xc_zstrlen_uchar(s) : xc_zstrlen_char(s);
  160. }
  161. /* }}} */
  162. #else
  163. /* {{{ xc_zstrlen */
  164. #define xc_zstrlen(dummy, s) xc_zstrlen_char(s)
  165. /* }}} */
  166. #endif
  167. /* {{{ xc_calc_string_n */
  168. REDEF(`PROCESSOR_TYPE', `calc')
  169. #undef C_RELAYLINE
  170. #define C_RELAYLINE
  171. IFAUTOCHECK(`
  172. #undef C_RELAYLINE
  173. #define C_RELAYLINE , __LINE__
  174. ')
  175. static inline void xc_calc_string_n(xc_processor_t *processor, zend_uchar type, const_zstr str, long size IFAUTOCHECK(`, int relayline')) {
  176. pushdef(`__LINE__', `relayline')
  177. int realsize = UNISW(size, (type == IS_UNICODE) ? UBYTES(size) : size);
  178. long dummy = 1;
  179. if (realsize > MAX_DUP_STR_LEN) {
  180. ALLOC(, char, realsize)
  181. }
  182. else if (zend_u_hash_add(&processor->strings, type, str, size, (void *) &dummy, sizeof(dummy), NULL) == SUCCESS) {
  183. /* new string */
  184. ALLOC(, char, realsize)
  185. }
  186. IFAUTOCHECK(`
  187. else {
  188. dnl fprintf(stderr, "dupstr %s\n", ZSTR_S(str));
  189. }
  190. ')
  191. popdef(`__LINE__')
  192. }
  193. /* }}} */
  194. /* {{{ xc_store_string_n */
  195. REDEF(`PROCESSOR_TYPE', `store')
  196. static inline zstr xc_store_string_n(xc_processor_t *processor, zend_uchar type, const_zstr str, long size IFAUTOCHECK(`, int relayline')) {
  197. pushdef(`__LINE__', `relayline')
  198. int realsize = UNISW(size, (type == IS_UNICODE) ? UBYTES(size) : size);
  199. zstr ret, *pret;
  200. if (realsize > MAX_DUP_STR_LEN) {
  201. ALLOC(ZSTR_V(ret), char, realsize)
  202. memcpy(ZSTR_V(ret), ZSTR_V(str), realsize);
  203. return ret;
  204. }
  205. if (zend_u_hash_find(&processor->strings, type, str, size, (void **) &pret) == SUCCESS) {
  206. return *pret;
  207. }
  208. /* new string */
  209. ALLOC(ZSTR_V(ret), char, realsize)
  210. memcpy(ZSTR_V(ret), ZSTR_V(str), realsize);
  211. zend_u_hash_add(&processor->strings, type, str, size, (void *) &ret, sizeof(zstr), NULL);
  212. return ret;
  213. popdef(`__LINE__')
  214. }
  215. /* }}} */
  216. /* {{{ xc_get_class_num
  217. * return class_index + 1
  218. */
  219. static zend_ulong xc_get_class_num(xc_processor_t *processor, zend_class_entry *ce) {
  220. zend_ulong i;
  221. const xc_entry_data_php_t *php = processor->php_src;
  222. zend_class_entry *ceptr;
  223. if (processor->cache_ce == ce) {
  224. return processor->cache_class_index + 1;
  225. }
  226. for (i = 0; i < php->classinfo_cnt; i ++) {
  227. ceptr = CestToCePtr(php->classinfos[i].cest);
  228. if (ZCEP_REFCOUNT_PTR(ceptr) == ZCEP_REFCOUNT_PTR(ce)) {
  229. processor->cache_ce = ceptr;
  230. processor->cache_class_index = i;
  231. return i + 1;
  232. }
  233. }
  234. assert(0);
  235. return (zend_ulong) -1;
  236. }
  237. define(`xc_get_class_num', `xc_get_class_numNOTDEFINED')
  238. /* }}} */
  239. /* {{{ xc_get_class */
  240. #ifdef ZEND_ENGINE_2
  241. static zend_class_entry *xc_get_class(xc_processor_t *processor, zend_ulong class_num) {
  242. /* must be parent or currrent class */
  243. assert(class_num <= processor->active_class_index + 1);
  244. return CestToCePtr(processor->php_dst->classinfos[class_num - 1].cest);
  245. }
  246. #endif
  247. define(`xc_get_class', `xc_get_classNOTDEFINED')
  248. /* }}} */
  249. #ifdef ZEND_ENGINE_2
  250. /* fix method on store */
  251. static void xc_fix_method(xc_processor_t *processor, zend_op_array *dst TSRMLS_DC) /* {{{ */
  252. {
  253. zend_function *zf = (zend_function *) dst;
  254. zend_class_entry *ce = processor->active_class_entry_dst;
  255. const zend_class_entry *srcce = processor->active_class_entry_src;
  256. /* Fixing up the default functions for objects here since
  257. * we need to compare with the newly allocated functions
  258. *
  259. * caveat: a sub-class method can have the same name as the
  260. * parent~s constructor and create problems.
  261. */
  262. if (zf->common.fn_flags & ZEND_ACC_CTOR) {
  263. if (!ce->constructor) {
  264. ce->constructor = zf;
  265. }
  266. }
  267. else if (zf->common.fn_flags & ZEND_ACC_DTOR) {
  268. ce->destructor = zf;
  269. }
  270. else if (zf->common.fn_flags & ZEND_ACC_CLONE) {
  271. ce->clone = zf;
  272. }
  273. else {
  274. pushdef(`SET_IF_SAME_NAMEs', `
  275. SET_IF_SAME_NAME(__get);
  276. SET_IF_SAME_NAME(__set);
  277. #ifdef ZEND_ENGINE_2_1
  278. SET_IF_SAME_NAME(__unset);
  279. SET_IF_SAME_NAME(__isset);
  280. #endif
  281. SET_IF_SAME_NAME(__call);
  282. #ifdef ZEND_CALLSTATIC_FUNC_NAME
  283. SET_IF_SAME_NAME(__callstatic);
  284. #endif
  285. #if defined(ZEND_ENGINE_2_2) || PHP_MAJOR_VERSION >= 6
  286. SET_IF_SAME_NAME(__tostring);
  287. #endif
  288. ')
  289. #ifdef IS_UNICODE
  290. if (UG(unicode)) {
  291. #define SET_IF_SAME_NAME(member) \
  292. do { \
  293. if (srcce->member && u_strcmp(ZSTR_U(zf->common.function_name), ZSTR_U(srcce->member->common.function_name)) == 0) { \
  294. ce->member = zf; \
  295. } \
  296. } \
  297. while(0)
  298. SET_IF_SAME_NAMEs()
  299. #undef SET_IF_SAME_NAME
  300. }
  301. else
  302. #endif
  303. do {
  304. #define SET_IF_SAME_NAME(member) \
  305. do { \
  306. if (srcce->member && strcmp(ZSTR_S(zf->common.function_name), ZSTR_S(srcce->member->common.function_name)) == 0) { \
  307. ce->member = zf; \
  308. } \
  309. } \
  310. while(0)
  311. SET_IF_SAME_NAMEs()
  312. #undef SET_IF_SAME_NAME
  313. } while (0);
  314. popdef(`SET_IF_SAME_NAMEs')
  315. }
  316. }
  317. /* }}} */
  318. #endif
  319. /* {{{ call op_array ctor handler */
  320. extern zend_bool xc_have_op_array_ctor;
  321. static void xc_zend_extension_op_array_ctor_handler(zend_extension *extension, zend_op_array *op_array TSRMLS_DC)
  322. {
  323. if (extension->op_array_ctor) {
  324. extension->op_array_ctor(op_array);
  325. }
  326. }
  327. /* }}} */
  328. /* {{{ field name checker */
  329. IFAUTOCHECK(`dnl
  330. static int xc_check_names(const char *file, int line, const char *functionName, const char **assert_names, int assert_names_count, HashTable *done_names)
  331. {
  332. int errors = 0;
  333. if (assert_names_count) {
  334. int i;
  335. Bucket *b;
  336. for (i = 0; i < assert_names_count; ++i) {
  337. if (!zend_u_hash_exists(done_names, IS_STRING, assert_names[i], strlen(assert_names[i]) + 1)) {
  338. fprintf(stderr
  339. , "Error: missing field at %s `#'%d %s`' : %s\n"
  340. , file, line, functionName
  341. , assert_names[i]
  342. );
  343. ++errors;
  344. }
  345. }
  346. for (b = done_names->pListHead; b != NULL; b = b->pListNext) {
  347. int known = 0;
  348. for (i = 0; i < assert_names_count; ++i) {
  349. if (strcmp(assert_names[i], BUCKET_KEY_S(b)) == 0) {
  350. known = 1;
  351. break;
  352. }
  353. }
  354. if (!known) {
  355. fprintf(stderr
  356. , "Error: unknown field at %s `#'%d %s`' : %s\n"
  357. , file, line, functionName
  358. , BUCKET_KEY_S(b)
  359. );
  360. ++errors;
  361. }
  362. }
  363. }
  364. return errors;
  365. }
  366. ')
  367. /* }}} */
  368. dnl ================ export API
  369. define(`DEFINE_STORE_API', `
  370. /* export: $1 *xc_processor_store_$1(xc_cache_t *cache, $1 *src TSRMLS_DC); :export {{{ */
  371. $1 *xc_processor_store_$1(xc_cache_t *cache, $1 *src TSRMLS_DC) {
  372. $1 *dst;
  373. xc_processor_t processor;
  374. memset(&processor, 0, sizeof(processor));
  375. processor.reference = 1;
  376. processor.cache = cache;
  377. IFAUTOCHECK(`xc_stack_init(&processor.allocsizes);')
  378. /* calc size */ {
  379. zend_hash_init(&processor.strings, 0, NULL, NULL, 0);
  380. if (processor.reference) {
  381. zend_hash_init(&processor.zvalptrs, 0, NULL, NULL, 0);
  382. }
  383. processor.size = 0;
  384. /* allocate */
  385. processor.size = ALIGN(processor.size + sizeof(src[0]));
  386. xc_calc_$1(&processor, src TSRMLS_CC);
  387. if (processor.reference) {
  388. zend_hash_destroy(&processor.zvalptrs);
  389. }
  390. zend_hash_destroy(&processor.strings);
  391. }
  392. src->ifelse(
  393. `$1', `xc_entry_data_php_t', `',
  394. `', `', entry.)size = processor.size;
  395. ifelse(
  396. `$1', `xc_entry_var_t', `src->have_references = processor.have_references;',
  397. `$1', `xc_entry_data_php_t', `src->have_references = processor.have_references;'
  398. )
  399. IFAUTOCHECK(`xc_stack_reverse(&processor.allocsizes);')
  400. /* store {{{ */
  401. {
  402. IFAUTOCHECK(`char *oldp;')
  403. zend_hash_init(&processor.strings, 0, NULL, NULL, 0);
  404. if (processor.reference) {
  405. zend_hash_init(&processor.zvalptrs, 0, NULL, NULL, 0);
  406. }
  407. /* mem :) */
  408. processor.p = (char *) processor.cache->mem->handlers->malloc(processor.cache->mem, processor.size);
  409. if (processor.p == NULL) {
  410. dst = NULL;
  411. goto err_alloc;
  412. }
  413. IFAUTOCHECK(`oldp = processor.p;')
  414. assert(processor.p == (char *) ALIGN(processor.p));
  415. /* allocate */
  416. dst = ($1 *) processor.p;
  417. processor.p = (char *) ALIGN(processor.p + sizeof(dst[0]));
  418. xc_store_$1(&processor, dst, src TSRMLS_CC);
  419. IFAUTOCHECK(` {
  420. size_t unexpected = processor.p - oldp;
  421. size_t expected = processor.size;
  422. if (unexpected != processor.size) {
  423. fprintf(stderr, "unexpected:%lu - expected:%lu = %ld != 0\n", (unsigned long) unexpected, (unsigned long) expected, (long) unexpected - expected);
  424. abort();
  425. }
  426. }')
  427. err_alloc:
  428. if (processor.reference) {
  429. zend_hash_destroy(&processor.zvalptrs);
  430. }
  431. zend_hash_destroy(&processor.strings);
  432. }
  433. /* }}} */
  434. IFAUTOCHECK(`xc_stack_destroy(&processor.allocsizes);')
  435. return dst;
  436. }
  437. /* }}} */
  438. ')
  439. DEFINE_STORE_API(`xc_entry_var_t')
  440. DEFINE_STORE_API(`xc_entry_php_t')
  441. DEFINE_STORE_API(`xc_entry_data_php_t')
  442. /* export: xc_entry_php_t *xc_processor_restore_xc_entry_php_t(xc_entry_php_t *dst, const xc_entry_php_t *src TSRMLS_DC); :export {{{ */
  443. xc_entry_php_t *xc_processor_restore_xc_entry_php_t(xc_entry_php_t *dst, const xc_entry_php_t *src TSRMLS_DC) {
  444. xc_processor_t processor;
  445. memset(&processor, 0, sizeof(processor));
  446. xc_restore_xc_entry_php_t(&processor, dst, src TSRMLS_CC);
  447. return dst;
  448. }
  449. /* }}} */
  450. /* export: xc_entry_data_php_t *xc_processor_restore_xc_entry_data_php_t(const xc_entry_php_t *entry_php, xc_entry_data_php_t *dst, const xc_entry_data_php_t *src, zend_bool readonly_protection TSRMLS_DC); :export {{{ */
  451. xc_entry_data_php_t *xc_processor_restore_xc_entry_data_php_t(const xc_entry_php_t *entry_php, xc_entry_data_php_t *dst, const xc_entry_data_php_t *src, zend_bool readonly_protection TSRMLS_DC) {
  452. xc_processor_t processor;
  453. memset(&processor, 0, sizeof(processor));
  454. processor.readonly_protection = readonly_protection;
  455. /* this function is used for php data only */
  456. if (src->have_references) {
  457. processor.reference = 1;
  458. }
  459. processor.entry_php_src = entry_php;
  460. if (processor.reference) {
  461. zend_hash_init(&processor.zvalptrs, 0, NULL, NULL, 0);
  462. }
  463. xc_restore_xc_entry_data_php_t(&processor, dst, src TSRMLS_CC);
  464. if (processor.reference) {
  465. zend_hash_destroy(&processor.zvalptrs);
  466. }
  467. return dst;
  468. }
  469. /* }}} */
  470. /* export: xc_entry_var_t *xc_processor_restore_xc_entry_var_t(xc_entry_var_t *dst, const xc_entry_var_t *src TSRMLS_DC); :export {{{ */
  471. xc_entry_var_t *xc_processor_restore_xc_entry_var_t(xc_entry_var_t *dst, const xc_entry_var_t *src TSRMLS_DC) {
  472. xc_processor_t processor;
  473. memset(&processor, 0, sizeof(processor));
  474. xc_restore_xc_entry_var_t(&processor, dst, src TSRMLS_CC);
  475. return dst;
  476. }
  477. /* }}} */
  478. /* export: zval *xc_processor_restore_zval(zval *dst, const zval *src, zend_bool have_references TSRMLS_DC); :export {{{ */
  479. zval *xc_processor_restore_zval(zval *dst, const zval *src, zend_bool have_references TSRMLS_DC) {
  480. xc_processor_t processor;
  481. memset(&processor, 0, sizeof(processor));
  482. processor.reference = have_references;
  483. if (processor.reference) {
  484. zend_hash_init(&processor.zvalptrs, 0, NULL, NULL, 0);
  485. dnl fprintf(stderr, "mark[%p] = %p\n", src, dst);
  486. zend_hash_add(&processor.zvalptrs, (char *)src, sizeof(src), (void*)&dst, sizeof(dst), NULL);
  487. }
  488. xc_restore_zval(&processor, dst, src TSRMLS_CC);
  489. if (processor.reference) {
  490. zend_hash_destroy(&processor.zvalptrs);
  491. }
  492. return dst;
  493. }
  494. /* }}} */
  495. /* export: void xc_dprint(xc_entry_php_t *src, int indent TSRMLS_DC); :export {{{ */
  496. #ifdef HAVE_XCACHE_DPRINT
  497. void xc_dprint(xc_entry_php_t *src, int indent TSRMLS_DC) {
  498. IFDPRINT(`INDENT()`'fprintf(stderr, "xc_entry_php_t:src");')
  499. xc_dprint_xc_entry_php_t(src, indent TSRMLS_CC);
  500. }
  501. #endif
  502. /* }}} */