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.

481 lines
12 KiB

  1. #include "php.h"
  2. #include "xcache.h"
  3. #include "utils.h"
  4. #ifdef ZEND_ENGINE_2_1
  5. #include "zend_vm.h"
  6. #endif
  7. #include "opcode_spec.h"
  8. #undef NDEBUG
  9. #include "assert.h"
  10. xc_compile_result_t *xc_compile_result_init(xc_compile_result_t *cr, /* {{{ */
  11. zend_op_array *op_array,
  12. HashTable *function_table,
  13. HashTable *class_table)
  14. {
  15. if (cr) {
  16. cr->alloc = 0;
  17. }
  18. else {
  19. cr = emalloc(sizeof(xc_compile_result_t));
  20. cr->alloc = 1;
  21. }
  22. cr->op_array = op_array;
  23. cr->function_table = function_table;
  24. cr->class_table = class_table;
  25. return cr;
  26. }
  27. /* }}} */
  28. xc_compile_result_t *xc_compile_result_init_cur(xc_compile_result_t *cr, zend_op_array *op_array TSRMLS_DC) /* {{{ */
  29. {
  30. return xc_compile_result_init(cr, op_array, CG(function_table), CG(class_table));
  31. }
  32. /* }}} */
  33. void xc_compile_result_free(xc_compile_result_t *cr) /* {{{ */
  34. {
  35. if (cr->alloc) {
  36. efree(cr);
  37. }
  38. }
  39. /* }}} */
  40. int xc_apply_function(zend_function *zf, apply_func_t applyer TSRMLS_DC) /* {{{ */
  41. {
  42. switch (zf->type) {
  43. case ZEND_USER_FUNCTION:
  44. case ZEND_EVAL_CODE:
  45. return applyer(&zf->op_array TSRMLS_CC);
  46. break;
  47. case ZEND_INTERNAL_FUNCTION:
  48. case ZEND_OVERLOADED_FUNCTION:
  49. break;
  50. EMPTY_SWITCH_DEFAULT_CASE();
  51. }
  52. return 0;
  53. }
  54. /* }}} */
  55. typedef struct {
  56. apply_func_t applyer;
  57. zend_class_entry *ce;
  58. } xc_apply_method_info;
  59. int xc_apply_method(zend_function *zf, xc_apply_method_info *mi TSRMLS_DC) /* {{{ */
  60. {
  61. /* avoid duplicate apply for shadowed method */
  62. #ifdef ZEND_ENGINE_2
  63. if (mi->ce != zf->common.scope) {
  64. /* fprintf(stderr, "avoided duplicate %s\n", zf->common.function_name); */
  65. return 0;
  66. }
  67. #else
  68. char *name = zf->common.function_name;
  69. int name_s = strlen(name) + 1;
  70. zend_class_entry *ce;
  71. zend_function *ptr;
  72. for (ce = mi->ce->parent; ce; ce = ce->parent) {
  73. if (zend_hash_find(&ce->function_table, name, name_s, (void **) &ptr) == SUCCESS) {
  74. if (ptr->op_array.refcount == zf->op_array.refcount) {
  75. return 0;
  76. }
  77. }
  78. }
  79. #endif
  80. return xc_apply_function(zf, mi->applyer TSRMLS_CC);
  81. }
  82. /* }}} */
  83. #if 0
  84. int xc_apply_class(zend_class_entry *ce, apply_func_t applyer TSRMLS_DC) /* {{{ */
  85. {
  86. xc_apply_method_info mi;
  87. mi.applyer = applyer;
  88. mi.ce = ce;
  89. zend_hash_apply_with_argument(&(ce->function_table), (apply_func_arg_t) xc_apply_method, &mi TSRMLS_CC);
  90. return 0;
  91. }
  92. /* }}} */
  93. #endif
  94. static int xc_apply_cest(xc_cest_t *cest, apply_func_t applyer TSRMLS_DC) /* {{{ */
  95. {
  96. xc_apply_method_info mi;
  97. mi.applyer = applyer;
  98. mi.ce = CestToCePtr(*cest);
  99. zend_hash_apply_with_argument(&(CestToCePtr(*cest)->function_table), (apply_func_arg_t) xc_apply_method, &mi TSRMLS_CC);
  100. return 0;
  101. }
  102. /* }}} */
  103. int xc_apply_op_array(xc_compile_result_t *cr, apply_func_t applyer TSRMLS_DC) /* {{{ */
  104. {
  105. zend_hash_apply_with_argument(cr->function_table, (apply_func_arg_t) xc_apply_function, applyer TSRMLS_CC);
  106. zend_hash_apply_with_argument(cr->class_table, (apply_func_arg_t) xc_apply_cest, applyer TSRMLS_CC);
  107. return applyer(cr->op_array TSRMLS_CC);
  108. }
  109. /* }}} */
  110. int xc_undo_pass_two(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  111. {
  112. zend_op *opline, *end;
  113. if (!op_array->done_pass_two) {
  114. return 0;
  115. }
  116. opline = op_array->opcodes;
  117. end = opline + op_array->last;
  118. while (opline < end) {
  119. #ifdef ZEND_ENGINE_2_1
  120. switch (opline->opcode) {
  121. case ZEND_JMP:
  122. opline->op1.u.opline_num = opline->op1.u.jmp_addr - op_array->opcodes;
  123. assert(opline->op1.u.opline_num < op_array->last);
  124. break;
  125. case ZEND_JMPZ:
  126. case ZEND_JMPNZ:
  127. case ZEND_JMPZ_EX:
  128. case ZEND_JMPNZ_EX:
  129. opline->op2.u.opline_num = opline->op2.u.jmp_addr - op_array->opcodes;
  130. assert(opline->op2.u.opline_num < op_array->last);
  131. break;
  132. }
  133. #endif
  134. opline++;
  135. }
  136. op_array->done_pass_two = 0;
  137. return 0;
  138. }
  139. /* }}} */
  140. int xc_redo_pass_two(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  141. {
  142. zend_op *opline, *end;
  143. if (op_array->done_pass_two) {
  144. return 0;
  145. }
  146. /*
  147. op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
  148. op_array->size = op_array->last;
  149. */
  150. opline = op_array->opcodes;
  151. end = opline + op_array->last;
  152. while (opline < end) {
  153. if (opline->op1.op_type == IS_CONST) {
  154. opline->op1.u.constant.is_ref = 1;
  155. opline->op1.u.constant.refcount = 2; /* Make sure is_ref won't be reset */
  156. }
  157. if (opline->op2.op_type == IS_CONST) {
  158. opline->op2.u.constant.is_ref = 1;
  159. opline->op2.u.constant.refcount = 2;
  160. }
  161. #ifdef ZEND_ENGINE_2_1
  162. switch (opline->opcode) {
  163. case ZEND_JMP:
  164. assert(opline->op1.u.opline_num < op_array->last);
  165. opline->op1.u.jmp_addr = op_array->opcodes + opline->op1.u.opline_num;
  166. break;
  167. case ZEND_JMPZ:
  168. case ZEND_JMPNZ:
  169. case ZEND_JMPZ_EX:
  170. case ZEND_JMPNZ_EX:
  171. assert(opline->op2.u.opline_num < op_array->last);
  172. opline->op2.u.jmp_addr = op_array->opcodes + opline->op2.u.opline_num;
  173. break;
  174. }
  175. ZEND_VM_SET_OPCODE_HANDLER(opline);
  176. #endif
  177. opline++;
  178. }
  179. op_array->done_pass_two = 1;
  180. return 0;
  181. }
  182. /* }}} */
  183. #ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
  184. static void xc_fix_opcode_ex_znode(int tofix, xc_op_spec_t spec, znode *znode, int type TSRMLS_DC) /* {{{ */
  185. {
  186. #ifdef ZEND_ENGINE_2
  187. if ((znode->op_type != IS_UNUSED && (spec == OPSPEC_UCLASS || spec == OPSPEC_CLASS)) ||
  188. spec == OPSPEC_FETCH) {
  189. if (tofix) {
  190. switch (znode->op_type) {
  191. case IS_VAR:
  192. case IS_TMP_VAR:
  193. break;
  194. default:
  195. /* TODO: data lost, find a way to keep it */
  196. /* assert(znode->op_type == IS_CONST); */
  197. znode->op_type = IS_TMP_VAR;
  198. }
  199. }
  200. }
  201. switch (znode->op_type) {
  202. case IS_TMP_VAR:
  203. case IS_VAR:
  204. if (tofix) {
  205. znode->u.var /= sizeof(temp_variable);
  206. }
  207. else {
  208. znode->u.var *= sizeof(temp_variable);
  209. }
  210. }
  211. #endif
  212. }
  213. /* }}} */
  214. static void xc_fix_opcode_ex(zend_op_array *op_array, int tofix TSRMLS_DC) /* {{{ */
  215. {
  216. zend_op *opline;
  217. zend_uint i;
  218. opline = op_array->opcodes;
  219. for (i = 0; i < op_array->last; i ++, opline ++) {
  220. /* 3rd optimizer may have ... */
  221. if (opline->opcode < xc_get_opcode_spec_count()) {
  222. const xc_opcode_spec_t *spec;
  223. spec = xc_get_opcode_spec(opline->opcode);
  224. xc_fix_opcode_ex_znode(tofix, spec->op1, &opline->op1, 0 TSRMLS_CC);
  225. xc_fix_opcode_ex_znode(tofix, spec->op2, &opline->op2, 1 TSRMLS_CC);
  226. xc_fix_opcode_ex_znode(tofix, spec->res, &opline->result, 2 TSRMLS_CC);
  227. }
  228. }
  229. }
  230. /* }}} */
  231. int xc_fix_opcode(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  232. {
  233. xc_fix_opcode_ex(op_array, 1 TSRMLS_CC);
  234. return 0;
  235. }
  236. /* }}} */
  237. int xc_undo_fix_opcode(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  238. {
  239. xc_fix_opcode_ex(op_array, 0 TSRMLS_CC);
  240. return 0;
  241. }
  242. /* }}} */
  243. #endif
  244. #ifdef HAVE_XCACHE_CONSTANT
  245. void xc_install_constant(char *filename, zend_constant *constant, zend_uchar type, zstr key, uint len TSRMLS_DC) /* {{{ */
  246. {
  247. if (zend_u_hash_add(EG(zend_constants), type, key, len,
  248. constant, sizeof(zend_constant),
  249. NULL
  250. ) == FAILURE) {
  251. CG(zend_lineno) = 0;
  252. #ifdef IS_UNICODE
  253. zend_error(E_NOTICE, "Constant %R already defined", type, key);
  254. #else
  255. zend_error(E_NOTICE, "Constant %s already defined", key);
  256. #endif
  257. free(ZSTR_V(constant->name));
  258. if (!(constant->flags & CONST_PERSISTENT)) {
  259. zval_dtor(&constant->value);
  260. }
  261. }
  262. }
  263. /* }}} */
  264. #endif
  265. void xc_install_function(char *filename, zend_function *func, zend_uchar type, zstr key, uint len TSRMLS_DC) /* {{{ */
  266. {
  267. zend_bool istmpkey;
  268. if (func->type == ZEND_USER_FUNCTION) {
  269. #ifdef IS_UNICODE
  270. istmpkey = (type == IS_STRING && ZSTR_S(key)[0] == 0) || ZSTR_U(key)[0] == 0;
  271. #else
  272. istmpkey = ZSTR_S(key)[0] == 0;
  273. #endif
  274. if (istmpkey) {
  275. zend_u_hash_update(CG(function_table), type, key, len,
  276. func, sizeof(zend_op_array),
  277. NULL
  278. );
  279. }
  280. else if (zend_u_hash_add(CG(function_table), type, key, len,
  281. func, sizeof(zend_op_array),
  282. NULL
  283. ) == FAILURE) {
  284. CG(zend_lineno) = ZESW(func->op_array.opcodes[0].lineno, func->op_array.line_start);
  285. #ifdef IS_UNICODE
  286. zend_error(E_ERROR, "Cannot redeclare %R()", type, key);
  287. #else
  288. zend_error(E_ERROR, "Cannot redeclare %s()", key);
  289. #endif
  290. }
  291. }
  292. }
  293. /* }}} */
  294. ZESW(xc_cest_t *, void) xc_install_class(char *filename, xc_cest_t *cest, zend_uchar type, zstr key, uint len TSRMLS_DC) /* {{{ */
  295. {
  296. zend_bool istmpkey;
  297. zend_class_entry *cep = CestToCePtr(*cest);
  298. ZESW(void *stored_ce_ptr, NOTHING);
  299. #ifdef IS_UNICODE
  300. istmpkey = (type == IS_STRING && ZSTR_S(key)[0] == 0) || ZSTR_U(key)[0] == 0;
  301. #else
  302. istmpkey = ZSTR_S(key)[0] == 0;
  303. #endif
  304. if (istmpkey) {
  305. zend_u_hash_update(CG(class_table), type, key, len,
  306. cest, sizeof(xc_cest_t),
  307. ZESW(&stored_ce_ptr, NULL)
  308. );
  309. }
  310. else if (zend_u_hash_add(CG(class_table), type, key, len,
  311. cest, sizeof(xc_cest_t),
  312. ZESW(&stored_ce_ptr, NULL)
  313. ) == FAILURE) {
  314. CG(zend_lineno) = ZESW(0, cep->line_start);
  315. #ifdef IS_UNICODE
  316. zend_error(E_ERROR, "Cannot redeclare class %R", type, cep->name);
  317. #else
  318. zend_error(E_ERROR, "Cannot redeclare class %s", cep->name);
  319. #endif
  320. }
  321. ZESW(return (xc_cest_t *) stored_ce_ptr, NOTHING);
  322. }
  323. /* }}} */
  324. /* sandbox {{{ */
  325. #undef TG
  326. #undef OG
  327. #define TG(x) (sandbox->tmp_##x)
  328. #define OG(x) (sandbox->orig_##x)
  329. /* }}} */
  330. xc_sandbox_t *xc_sandbox_init(xc_sandbox_t *sandbox, char *filename TSRMLS_DC) /* {{{ */
  331. {
  332. if (sandbox) {
  333. memset(sandbox, 0, sizeof(sandbox[0]));
  334. }
  335. else {
  336. ECALLOC_ONE(sandbox);
  337. sandbox->alloc = 1;
  338. }
  339. memcpy(&OG(included_files), &EG(included_files), sizeof(EG(included_files)));
  340. #ifdef HAVE_XCACHE_CONSTANT
  341. OG(zend_constants) = EG(zend_constants);
  342. EG(zend_constants) = &TG(zend_constants);
  343. #endif
  344. OG(function_table) = CG(function_table);
  345. CG(function_table) = &TG(function_table);
  346. OG(class_table) = CG(class_table);
  347. CG(class_table) = &TG(class_table);
  348. EG(class_table) = CG(class_table);
  349. TG(included_files) = &EG(included_files);
  350. zend_hash_init_ex(TG(included_files), 5, NULL, NULL, 0, 1);
  351. #ifdef HAVE_XCACHE_CONSTANT
  352. zend_hash_init_ex(&TG(zend_constants), 20, NULL, EG(zend_constants)->pDestructor, 1, 0);
  353. #endif
  354. zend_hash_init_ex(&TG(function_table), 128, NULL, CG(function_table)->pDestructor, 0, 0);
  355. zend_hash_init_ex(&TG(class_table), 16, NULL, CG(class_table)->pDestructor, 0, 0);
  356. sandbox->filename = filename;
  357. #ifdef E_STRICT
  358. sandbox->orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
  359. EG(user_error_handler_error_reporting) &= ~E_STRICT;
  360. #endif
  361. return sandbox;
  362. }
  363. /* }}} */
  364. static void xc_sandbox_install(xc_sandbox_t *sandbox TSRMLS_DC) /* {{{ */
  365. {
  366. int i;
  367. Bucket *b;
  368. #ifdef HAVE_XCACHE_CONSTANT
  369. b = TG(zend_constants).pListHead;
  370. /* install constants */
  371. while (b != NULL) {
  372. zend_constant *c = (zend_constant*) b->pData;
  373. xc_install_constant(sandbox->filename, c,
  374. BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY(b)), b->nKeyLength TSRMLS_CC);
  375. b = b->pListNext;
  376. }
  377. #endif
  378. b = TG(function_table).pListHead;
  379. /* install function */
  380. while (b != NULL) {
  381. zend_function *func = (zend_function*) b->pData;
  382. xc_install_function(sandbox->filename, func,
  383. BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY(b)), b->nKeyLength TSRMLS_CC);
  384. b = b->pListNext;
  385. }
  386. b = TG(class_table).pListHead;
  387. /* install class */
  388. while (b != NULL) {
  389. xc_install_class(sandbox->filename, (xc_cest_t*) b->pData,
  390. BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY(b)), b->nKeyLength TSRMLS_CC);
  391. b = b->pListNext;
  392. }
  393. i = 1;
  394. zend_hash_add(&OG(included_files), sandbox->filename, strlen(sandbox->filename) + 1, (void *)&i, sizeof(int), NULL);
  395. }
  396. /* }}} */
  397. void xc_sandbox_free(xc_sandbox_t *sandbox, int install TSRMLS_DC) /* {{{ */
  398. {
  399. /* restore first first install function/class */
  400. #ifdef HAVE_XCACHE_CONSTANT
  401. EG(zend_constants) = OG(zend_constants);
  402. #endif
  403. CG(function_table) = OG(function_table);
  404. CG(class_table) = OG(class_table);
  405. EG(class_table) = CG(class_table);
  406. if (install) {
  407. CG(in_compilation) = 1;
  408. CG(compiled_filename) = sandbox->filename;
  409. CG(zend_lineno) = 0;
  410. xc_sandbox_install(sandbox TSRMLS_CC);
  411. CG(in_compilation) = 0;
  412. CG(compiled_filename) = NULL;
  413. /* no free as it's installed */
  414. #ifdef HAVE_XCACHE_CONSTANT
  415. TG(zend_constants).pDestructor = NULL;
  416. #endif
  417. TG(function_table).pDestructor = NULL;
  418. TG(class_table).pDestructor = NULL;
  419. }
  420. /* destroy all the tmp */
  421. #ifdef HAVE_XCACHE_CONSTANT
  422. zend_hash_destroy(&TG(zend_constants));
  423. #endif
  424. zend_hash_destroy(&TG(function_table));
  425. zend_hash_destroy(&TG(class_table));
  426. zend_hash_destroy(TG(included_files));
  427. /* restore orig here, as EG/CG holded tmp before */
  428. memcpy(&EG(included_files), &OG(included_files), sizeof(EG(included_files)));
  429. #ifdef E_STRICT
  430. EG(user_error_handler_error_reporting) = sandbox->orig_user_error_handler_error_reporting;
  431. #endif
  432. if (sandbox->alloc) {
  433. efree(sandbox);
  434. }
  435. }
  436. /* }}} */