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.

410 lines
11 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. void xc_install_function(char *filename, zend_function *func, zend_uchar type, char *key, uint len TSRMLS_DC) /* {{{ */
  245. {
  246. if (func->type == ZEND_USER_FUNCTION) {
  247. if (zend_u_hash_add(CG(function_table), type, key, len,
  248. func, sizeof(zend_op_array),
  249. NULL
  250. ) == FAILURE) {
  251. CG(in_compilation) = 1;
  252. CG(compiled_filename) = filename;
  253. CG(zend_lineno) = ZESW(func->op_array.opcodes[0].lineno, func->op_array.line_start);
  254. zend_error(E_ERROR, "Cannot redeclare %s()", key);
  255. }
  256. }
  257. }
  258. /* }}} */
  259. ZESW(xc_cest_t *, void) xc_install_class(char *filename, xc_cest_t *cest, zend_uchar type, void *key, uint len TSRMLS_DC) /* {{{ */
  260. {
  261. zend_class_entry *cep = CestToCePtr(*cest);
  262. ZESW(void *stored_ce_ptr, NOTHING);
  263. if (zend_u_hash_add(CG(class_table), type, key, len,
  264. cest, sizeof(xc_cest_t),
  265. ZESW(&stored_ce_ptr, NULL)
  266. ) == FAILURE) {
  267. CG(in_compilation) = 1;
  268. CG(compiled_filename) = filename;
  269. CG(zend_lineno) = ZESW(0, cep->line_start);
  270. zend_error(E_ERROR, "Cannot redeclare class %s", (char *) cep->name);
  271. }
  272. ZESW(return (xc_cest_t *) stored_ce_ptr, NOTHING);
  273. }
  274. /* }}} */
  275. /* sandbox {{{ */
  276. #undef TG
  277. #undef OG
  278. #define TG(x) (sandbox->tmp_##x)
  279. #define OG(x) (sandbox->orig_##x)
  280. /* }}} */
  281. xc_sandbox_t *xc_sandbox_init(xc_sandbox_t *sandbox, char *filename TSRMLS_DC) /* {{{ */
  282. {
  283. if (sandbox) {
  284. memset(sandbox, 0, sizeof(sandbox[0]));
  285. }
  286. else {
  287. ECALLOC_ONE(sandbox);
  288. sandbox->alloc = 1;
  289. }
  290. memcpy(&OG(included_files), &EG(included_files), sizeof(EG(included_files)));
  291. memcpy(&OG(open_files), &CG(open_files), sizeof(CG(open_files)));
  292. OG(function_table) = CG(function_table);
  293. CG(function_table) = &TG(function_table);
  294. OG(class_table) = CG(class_table);
  295. CG(class_table) = &TG(class_table);
  296. EG(class_table) = CG(class_table);
  297. TG(included_files) = &EG(included_files);
  298. TG(open_files) = &CG(open_files);
  299. zend_llist_init(TG(open_files), sizeof(zend_file_handle), (void (*)(void *)) zend_file_handle_dtor, 0);
  300. zend_hash_init_ex(TG(included_files), 5, NULL, NULL, 0, 1);
  301. zend_hash_init_ex(&TG(function_table), 128, NULL, ZEND_FUNCTION_DTOR, 0, 0);
  302. zend_hash_init_ex(&TG(class_table), 16, NULL, ZEND_CLASS_DTOR, 0, 0);
  303. sandbox->filename = filename;
  304. #ifdef E_STRICT
  305. sandbox->orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
  306. EG(user_error_handler_error_reporting) &= ~E_STRICT;
  307. #endif
  308. return sandbox;
  309. }
  310. /* }}} */
  311. static void xc_sandbox_install(xc_sandbox_t *sandbox TSRMLS_DC) /* {{{ */
  312. {
  313. int i;
  314. Bucket *b;
  315. zend_llist_position lpos;
  316. zend_file_handle *handle;
  317. b = TG(function_table).pListHead;
  318. /* install function */
  319. while (b != NULL) {
  320. zend_function *func = (zend_function*) b->pData;
  321. xc_install_function(sandbox->filename, func,
  322. BUCKET_KEY_TYPE(b), BUCKET_KEY(b), b->nKeyLength TSRMLS_CC);
  323. b = b->pListNext;
  324. }
  325. b = TG(class_table).pListHead;
  326. /* install class */
  327. while (b != NULL) {
  328. xc_install_class(sandbox->filename, (xc_cest_t*) b->pData,
  329. BUCKET_KEY_TYPE(b), BUCKET_KEY(b), b->nKeyLength TSRMLS_CC);
  330. b = b->pListNext;
  331. }
  332. i = 1;
  333. zend_hash_add(&OG(included_files), sandbox->filename, strlen(sandbox->filename) + 1, (void *)&i, sizeof(int), NULL);
  334. for (handle = zend_llist_get_first_ex(TG(open_files), &lpos);
  335. handle;
  336. handle = zend_llist_get_next_ex(TG(open_files), &lpos)) {
  337. zend_llist_add_element(&OG(open_files), handle);
  338. }
  339. }
  340. /* }}} */
  341. void xc_sandbox_free(xc_sandbox_t *sandbox, int install TSRMLS_DC) /* {{{ */
  342. {
  343. /* restore first first install function/class */
  344. CG(function_table) = OG(function_table);
  345. CG(class_table) = OG(class_table);
  346. EG(class_table) = CG(class_table);
  347. if (install) {
  348. xc_sandbox_install(sandbox TSRMLS_CC);
  349. /* no free as it's installed */
  350. TG(function_table).pDestructor = NULL;
  351. TG(class_table).pDestructor = NULL;
  352. TG(open_files)->dtor = NULL;
  353. }
  354. /* destroy all the tmp */
  355. zend_hash_destroy(&TG(function_table));
  356. zend_hash_destroy(&TG(class_table));
  357. zend_hash_destroy(TG(included_files));
  358. zend_llist_destroy(TG(open_files));
  359. /* restore orig here, as EG/CG holded tmp before */
  360. memcpy(&EG(included_files), &OG(included_files), sizeof(EG(included_files)));
  361. memcpy(&CG(open_files), &OG(open_files), sizeof(CG(open_files)));
  362. #ifdef E_STRICT
  363. EG(user_error_handler_error_reporting) = sandbox->orig_user_error_handler_error_reporting;
  364. #endif
  365. if (sandbox->alloc) {
  366. efree(sandbox);
  367. }
  368. }
  369. /* }}} */