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.

911 lines
25 KiB

  1. /* {{{ macros */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <signal.h>
  6. #include "xcache.h"
  7. #ifdef HAVE_XCACHE_OPTIMIZER
  8. # include "mod_optimizer/xc_optimizer.h"
  9. #endif
  10. #ifdef HAVE_XCACHE_CACHER
  11. # include "mod_cacher/xc_cacher.h"
  12. #endif
  13. #ifdef HAVE_XCACHE_COVERAGER
  14. # include "mod_coverager/xc_coverager.h"
  15. #endif
  16. #ifdef HAVE_XCACHE_DISASSEMBLER
  17. # include "mod_disassembler/xc_disassembler.h"
  18. #endif
  19. #include "xcache_globals.h"
  20. #include "xcache/xc_extension.h"
  21. #include "xcache/xc_ini.h"
  22. #include "xcache/xc_const_string.h"
  23. #include "xcache/xc_opcode_spec.h"
  24. #include "xcache/xc_utils.h"
  25. #include "util/xc_stack.h"
  26. #include "php.h"
  27. #include "ext/standard/info.h"
  28. #include "ext/standard/php_string.h"
  29. #include "SAPI.h"
  30. /* }}} */
  31. /* {{{ globals */
  32. static char *xc_coredump_dir = NULL;
  33. #ifdef ZEND_WIN32
  34. static zend_ulong xc_coredump_type = 0;
  35. #endif
  36. static zend_bool xc_disable_on_crash = 0;
  37. static zend_compile_file_t *old_compile_file = NULL;
  38. zend_bool xc_test = 0;
  39. ZEND_DECLARE_MODULE_GLOBALS(xcache)
  40. /* }}} */
  41. static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  42. {
  43. XG(initial_compile_file_called) = 1;
  44. return old_compile_file(h, type TSRMLS_CC);
  45. }
  46. /* }}} */
  47. /* devel helper function */
  48. #if 0
  49. static void xc_list_extensions() /* {{{ */
  50. {
  51. zend_llist_element *element;
  52. zend_extension *ext;
  53. fprintf(stderr, "extensions:\n");
  54. for (element = zend_extensions.head; element; element = element->next) {
  55. ext = (zend_extension *) element->data;
  56. fprintf(stderr, " - %s\n", ext->name);
  57. }
  58. }
  59. /* }}} */
  60. #endif
  61. /* module helper function */
  62. static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
  63. {
  64. typedef struct {
  65. const char *prefix;
  66. zend_uchar (*getsize)();
  67. const char *(*get)(zend_uchar i);
  68. } xc_nameinfo_t;
  69. xc_nameinfo_t nameinfos[] = {
  70. { "", xc_get_op_type_count, xc_get_op_type },
  71. { "", xc_get_data_type_count, xc_get_data_type },
  72. { "", xc_get_opcode_count, xc_get_opcode },
  73. { "OPSPEC_", xc_get_op_spec_count, xc_get_op_spec },
  74. { NULL, NULL, NULL }
  75. };
  76. int undefdone = 0;
  77. xc_nameinfo_t *p;
  78. for (p = nameinfos; p->getsize; p ++) {
  79. zend_uchar i, count;
  80. char const_name[96];
  81. int const_name_len;
  82. count = p->getsize();
  83. for (i = 0; i < count; i ++) {
  84. const char *name = p->get(i);
  85. if (!name) continue;
  86. if (strcmp(name, "UNDEF") == 0) {
  87. if (undefdone) continue;
  88. undefdone = 1;
  89. }
  90. const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
  91. zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  92. }
  93. }
  94. zend_register_long_constant(XCACHE_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  95. zend_register_stringl_constant(XCACHE_STRS("XCACHE_VERSION"), XCACHE_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  96. zend_register_stringl_constant(XCACHE_STRS("XCACHE_MODULES"), XCACHE_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  97. return 0;
  98. }
  99. /* }}} */
  100. /* {{{ PHP_GINIT_FUNCTION(xcache) */
  101. #ifdef __GNUC__
  102. #pragma GCC push_options
  103. #pragma GCC diagnostic ignored "-Wshadow"
  104. #endif
  105. #ifdef PHP_GINIT_FUNCTION
  106. static PHP_GINIT_FUNCTION(xcache)
  107. #else
  108. static void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  109. #endif
  110. {
  111. #ifdef __GNUC__
  112. #pragma GCC pop_options
  113. #endif
  114. memset(xcache_globals, 0, sizeof(zend_xcache_globals));
  115. #ifdef HAVE_XCACHE_CONSTANT
  116. zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
  117. #endif
  118. zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
  119. zend_hash_init_ex(&xcache_globals->internal_class_table, 1, NULL, NULL, 1, 0);
  120. }
  121. /* }}} */
  122. /* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
  123. static
  124. #ifdef PHP_GSHUTDOWN_FUNCTION
  125. PHP_GSHUTDOWN_FUNCTION(xcache)
  126. #else
  127. void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  128. #endif
  129. {
  130. size_t i;
  131. if (xcache_globals->php_holds != NULL) {
  132. for (i = 0; i < xcache_globals->php_holds_size; i ++) {
  133. xc_stack_destroy(&xcache_globals->php_holds[i]);
  134. }
  135. free(xcache_globals->php_holds);
  136. xcache_globals->php_holds = NULL;
  137. xcache_globals->php_holds_size = 0;
  138. }
  139. if (xcache_globals->var_holds != NULL) {
  140. for (i = 0; i < xcache_globals->var_holds_size; i ++) {
  141. xc_stack_destroy(&xcache_globals->var_holds[i]);
  142. }
  143. free(xcache_globals->var_holds);
  144. xcache_globals->var_holds = NULL;
  145. xcache_globals->var_holds_size = 0;
  146. }
  147. if (xcache_globals->internal_table_copied) {
  148. #ifdef HAVE_XCACHE_CONSTANT
  149. zend_hash_destroy(&xcache_globals->internal_constant_table);
  150. #endif
  151. zend_hash_destroy(&xcache_globals->internal_function_table);
  152. zend_hash_destroy(&xcache_globals->internal_class_table);
  153. }
  154. }
  155. /* }}} */
  156. /* {{{ proto int xcache_get_refcount(mixed variable)
  157. XCache internal uses only: Get reference count of variable */
  158. PHP_FUNCTION(xcache_get_refcount)
  159. {
  160. zval *variable;
  161. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  162. RETURN_NULL();
  163. }
  164. RETURN_LONG(Z_REFCOUNT(*variable));
  165. }
  166. /* }}} */
  167. /* {{{ proto bool xcache_get_isref(mixed variable)
  168. XCache internal uses only: Check if variable data is marked referenced */
  169. #ifdef ZEND_BEGIN_ARG_INFO_EX
  170. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
  171. ZEND_ARG_INFO(1, variable)
  172. ZEND_END_ARG_INFO()
  173. #else
  174. static unsigned char arginfo_xcache_get_isref[] = { 1, BYREF_FORCE };
  175. #endif
  176. PHP_FUNCTION(xcache_get_isref)
  177. {
  178. zval *variable;
  179. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  180. RETURN_NULL();
  181. }
  182. RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
  183. }
  184. /* }}} */
  185. #ifdef HAVE_XCACHE_DPRINT
  186. /* {{{ proto bool xcache_dprint(mixed value)
  187. Prints variable (or value) internal struct (debug only) */
  188. #include "xc_processor.h"
  189. PHP_FUNCTION(xcache_dprint)
  190. {
  191. zval *value;
  192. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  193. return;
  194. }
  195. xc_dprint_zval(value, 0 TSRMLS_CC);
  196. }
  197. /* }}} */
  198. #endif
  199. /* {{{ proto string xcache_asm(string filename)
  200. */
  201. #ifdef HAVE_XCACHE_ASSEMBLER
  202. PHP_FUNCTION(xcache_asm)
  203. {
  204. }
  205. #endif
  206. /* }}} */
  207. /* {{{ proto string xcache_encode(string filename)
  208. Encode php file into XCache opcode encoded format */
  209. #ifdef HAVE_XCACHE_ENCODER
  210. PHP_FUNCTION(xcache_encode)
  211. {
  212. }
  213. #endif
  214. /* }}} */
  215. /* {{{ proto bool xcache_decode_file(string filename)
  216. Decode(load) opcode from XCache encoded format file */
  217. #ifdef HAVE_XCACHE_DECODER
  218. PHP_FUNCTION(xcache_decode_file)
  219. {
  220. }
  221. #endif
  222. /* }}} */
  223. /* {{{ proto bool xcache_decode_string(string data)
  224. Decode(load) opcode from XCache encoded format data */
  225. #ifdef HAVE_XCACHE_DECODER
  226. PHP_FUNCTION(xcache_decode_string)
  227. {
  228. }
  229. #endif
  230. /* }}} */
  231. /* {{{ xc_call_getter */
  232. typedef const char *(xc_name_getter_t)(zend_uchar type);
  233. static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
  234. {
  235. long spec;
  236. const char *name;
  237. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  238. return;
  239. }
  240. if (spec >= 0 && spec < count) {
  241. name = getter((zend_uchar) spec);
  242. if (name) {
  243. /* RETURN_STRING */
  244. int len = (int) strlen(name);
  245. return_value->value.str.len = len;
  246. return_value->value.str.val = estrndup(name, len);
  247. return_value->type = IS_STRING;
  248. return;
  249. }
  250. }
  251. RETURN_NULL();
  252. }
  253. /* }}} */
  254. /* {{{ proto string xcache_get_op_type(int op_type) */
  255. PHP_FUNCTION(xcache_get_op_type)
  256. {
  257. xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  258. }
  259. /* }}} */
  260. /* {{{ proto string xcache_get_data_type(int type) */
  261. PHP_FUNCTION(xcache_get_data_type)
  262. {
  263. xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  264. }
  265. /* }}} */
  266. /* {{{ proto string xcache_get_opcode(int opcode) */
  267. PHP_FUNCTION(xcache_get_opcode)
  268. {
  269. xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  270. }
  271. /* }}} */
  272. /* {{{ proto string xcache_get_op_spec(int op_type) */
  273. PHP_FUNCTION(xcache_get_op_spec)
  274. {
  275. xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  276. }
  277. /* }}} */
  278. /* {{{ proto string xcache_get_opcode_spec(int opcode) */
  279. PHP_FUNCTION(xcache_get_opcode_spec)
  280. {
  281. long spec;
  282. const xc_opcode_spec_t *opspec;
  283. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  284. return;
  285. }
  286. if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
  287. opspec = xc_get_opcode_spec((zend_uchar) spec);
  288. if (opspec) {
  289. array_init(return_value);
  290. add_assoc_long_ex(return_value, XCACHE_STRS("ext"), opspec->ext);
  291. add_assoc_long_ex(return_value, XCACHE_STRS("op1"), opspec->op1);
  292. add_assoc_long_ex(return_value, XCACHE_STRS("op2"), opspec->op2);
  293. add_assoc_long_ex(return_value, XCACHE_STRS("res"), opspec->res);
  294. return;
  295. }
  296. }
  297. RETURN_NULL();
  298. }
  299. /* }}} */
  300. /* {{{ proto mixed xcache_get_special_value(zval value)
  301. XCache internal use only: For decompiler to get static value with type fixed */
  302. PHP_FUNCTION(xcache_get_special_value)
  303. {
  304. zval *value;
  305. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  306. return;
  307. }
  308. switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
  309. case IS_CONSTANT:
  310. *return_value = *value;
  311. zval_copy_ctor(return_value);
  312. return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
  313. break;
  314. case IS_CONSTANT_ARRAY:
  315. *return_value = *value;
  316. zval_copy_ctor(return_value);
  317. return_value->type = IS_ARRAY;
  318. break;
  319. default:
  320. if ((Z_TYPE_P(value) & ~IS_CONSTANT_TYPE_MASK)) {
  321. *return_value = *value;
  322. zval_copy_ctor(return_value);
  323. return_value->type &= IS_CONSTANT_TYPE_MASK;
  324. }
  325. else {
  326. RETURN_NULL();
  327. }
  328. }
  329. }
  330. /* }}} */
  331. /* {{{ proto int xcache_get_type(zval value)
  332. XCache internal use only for disassembler to get variable type in engine level */
  333. PHP_FUNCTION(xcache_get_type)
  334. {
  335. zval *value;
  336. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  337. return;
  338. }
  339. RETURN_LONG(Z_TYPE_P(value));
  340. }
  341. /* }}} */
  342. /* {{{ proto string xcache_coredump(int op_type) */
  343. PHP_FUNCTION(xcache_coredump)
  344. {
  345. if (xc_test) {
  346. char *null_ptr = NULL;
  347. *null_ptr = 0;
  348. raise(SIGSEGV);
  349. }
  350. else {
  351. php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
  352. }
  353. }
  354. /* }}} */
  355. /* {{{ proto string xcache_is_autoglobal(string name) */
  356. PHP_FUNCTION(xcache_is_autoglobal)
  357. {
  358. zval *name;
  359. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  360. return;
  361. }
  362. #ifdef IS_UNICODE
  363. convert_to_unicode(name);
  364. #else
  365. convert_to_string(name);
  366. #endif
  367. RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
  368. }
  369. /* }}} */
  370. static zend_function_entry xcache_functions[] = /* {{{ */
  371. {
  372. PHP_FE(xcache_coredump, NULL)
  373. #ifdef HAVE_XCACHE_ASSEMBLER
  374. PHP_FE(xcache_asm, NULL)
  375. #endif
  376. #ifdef HAVE_XCACHE_ENCODER
  377. PHP_FE(xcache_encode, NULL)
  378. #endif
  379. #ifdef HAVE_XCACHE_DECODER
  380. PHP_FE(xcache_decode_file, NULL)
  381. PHP_FE(xcache_decode_string, NULL)
  382. #endif
  383. PHP_FE(xcache_get_special_value, NULL)
  384. PHP_FE(xcache_get_type, NULL)
  385. PHP_FE(xcache_get_op_type, NULL)
  386. PHP_FE(xcache_get_data_type, NULL)
  387. PHP_FE(xcache_get_opcode, NULL)
  388. PHP_FE(xcache_get_opcode_spec, NULL)
  389. PHP_FE(xcache_is_autoglobal, NULL)
  390. PHP_FE(xcache_get_refcount, NULL)
  391. PHP_FE(xcache_get_isref, arginfo_xcache_get_isref)
  392. #ifdef HAVE_XCACHE_DPRINT
  393. PHP_FE(xcache_dprint, NULL)
  394. #endif
  395. PHP_FE_END
  396. };
  397. /* }}} */
  398. #ifdef ZEND_WIN32
  399. #include "dbghelp.h"
  400. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
  401. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  402. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  403. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  404. );
  405. static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
  406. static HMODULE dbghelpModule = NULL;
  407. static char crash_dumpPath[_MAX_PATH] = { 0 };
  408. static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
  409. static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
  410. {
  411. HANDLE fileHandle;
  412. LONG ret = EXCEPTION_CONTINUE_SEARCH;
  413. SetUnhandledExceptionFilter(oldFilter);
  414. /* create the file */
  415. fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  416. if (fileHandle != INVALID_HANDLE_VALUE) {
  417. MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
  418. MINIDUMP_TYPE type = xc_coredump_type ? xc_coredump_type : (MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory);
  419. BOOL ok;
  420. exceptionInformation.ThreadId = GetCurrentThreadId();
  421. exceptionInformation.ExceptionPointers = pExceptionInfo;
  422. exceptionInformation.ClientPointers = FALSE;
  423. /* write the dump */
  424. ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, type, &exceptionInformation, NULL, NULL);
  425. CloseHandle(fileHandle);
  426. if (ok) {
  427. zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
  428. ret = EXCEPTION_EXECUTE_HANDLER;
  429. }
  430. else {
  431. zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
  432. }
  433. }
  434. else {
  435. zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
  436. }
  437. if (xc_disable_on_crash) {
  438. xc_disable_on_crash = 0;
  439. xc_cacher_disable();
  440. }
  441. return ret;
  442. }
  443. /* }}} */
  444. static void xcache_restore_crash_handler() /* {{{ */
  445. {
  446. if (oldFilter) {
  447. SetUnhandledExceptionFilter(oldFilter);
  448. oldFilter = NULL;
  449. }
  450. }
  451. /* }}} */
  452. static void xcache_init_crash_handler() /* {{{ */
  453. {
  454. /* firstly see if dbghelp.dll is around and has the function we need
  455. look next to the EXE first, as the one in System32 might be old
  456. (e.g. Windows 2000) */
  457. char dbghelpPath[_MAX_PATH];
  458. if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
  459. char *slash = strchr(dbghelpPath, '\\');
  460. if (slash) {
  461. strcpy(slash + 1, "DBGHELP.DLL");
  462. dbghelpModule = LoadLibrary(dbghelpPath);
  463. }
  464. }
  465. if (!dbghelpModule) {
  466. /* load any version we can */
  467. dbghelpModule = LoadLibrary("DBGHELP.DLL");
  468. if (!dbghelpModule) {
  469. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
  470. return;
  471. }
  472. }
  473. dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
  474. if (!dbghelp_MiniDumpWriteDump) {
  475. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL too old. Get updated dll and put it aside of php_xcache.dll");
  476. return;
  477. }
  478. #ifdef XCACHE_VERSION_REVISION
  479. #define REVISION "r" XCACHE_VERSION_REVISION
  480. #else
  481. #define REVISION ""
  482. #endif
  483. sprintf(crash_dumpPath, "%s\\php-%s-xcache-%s%s-%lu-%lu.dmp", xc_coredump_dir, zend_get_module_version("standard"), XCACHE_VERSION, REVISION, (unsigned long) time(NULL), (unsigned long) GetCurrentProcessId());
  484. #undef REVISION
  485. oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
  486. }
  487. /* }}} */
  488. #else
  489. /* old signal handlers {{{ */
  490. typedef void (*xc_sighandler_t)(int);
  491. #define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
  492. #include "util/xc_foreachcoresig.h"
  493. #undef FOREACH_SIG
  494. /* }}} */
  495. static void xcache_signal_handler(int sig);
  496. static void xcache_restore_crash_handler() /* {{{ */
  497. {
  498. #define FOREACH_SIG(sig) do { \
  499. if (old_##sig##_handler != xcache_signal_handler) { \
  500. signal(sig, old_##sig##_handler); \
  501. } \
  502. else { \
  503. signal(sig, SIG_DFL); \
  504. } \
  505. } while (0)
  506. #include "util/xc_foreachcoresig.h"
  507. #undef FOREACH_SIG
  508. }
  509. /* }}} */
  510. static void xcache_init_crash_handler() /* {{{ */
  511. {
  512. #define FOREACH_SIG(sig) \
  513. old_##sig##_handler = signal(sig, xcache_signal_handler)
  514. #include "util/xc_foreachcoresig.h"
  515. #undef FOREACH_SIG
  516. }
  517. /* }}} */
  518. static void xcache_signal_handler(int sig) /* {{{ */
  519. {
  520. xcache_restore_crash_handler();
  521. if (xc_coredump_dir && xc_coredump_dir[0]) {
  522. if (chdir(xc_coredump_dir) != 0) {
  523. /* error, but nothing can do about it
  524. * and should'nt print anything which might SEGV again */
  525. }
  526. }
  527. if (xc_disable_on_crash) {
  528. xc_disable_on_crash = 0;
  529. xc_cacher_disable();
  530. }
  531. raise(sig);
  532. }
  533. /* }}} */
  534. #endif
  535. /* {{{ incompatible zend extensions handling */
  536. typedef struct {
  537. const char *name;
  538. startup_func_t old_startup;
  539. } xc_incompatible_zend_extension_info_t;
  540. static xc_incompatible_zend_extension_info_t xc_incompatible_zend_extensions[] = {
  541. { "Zend Extension Manager", NULL },
  542. { "Zend Optimizer", NULL },
  543. { "the ionCube PHP Loader", NULL }
  544. };
  545. static xc_incompatible_zend_extension_info_t *xc_get_incompatible_zend_extension_info(const char *name)
  546. {
  547. size_t i;
  548. for (i = 0; i < sizeof(xc_incompatible_zend_extensions) / sizeof(xc_incompatible_zend_extensions[0]); ++i) {
  549. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = &xc_incompatible_zend_extensions[i];
  550. if (strcmp(incompatible_zend_extension_info->name, name) == 0) {
  551. return incompatible_zend_extension_info;
  552. }
  553. }
  554. return NULL;
  555. }
  556. /* }}} */
  557. static void xc_zend_llist_add_element(zend_llist *list, zend_llist_element *element) /* {{{ */
  558. {
  559. if (!zend_extensions.head) {
  560. zend_extensions.head = zend_extensions.tail = element;
  561. }
  562. else {
  563. zend_extensions.tail->next = element;
  564. element->prev = zend_extensions.tail;
  565. zend_extensions.tail = element;
  566. }
  567. }
  568. /* }}} */
  569. static int xc_incompatible_zend_extension_startup_hook(zend_extension *extension) /* {{{ */
  570. {
  571. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(extension->name);
  572. int status;
  573. zend_bool catched = 0;
  574. zend_llist saved_zend_extensions_container; /* without elements */
  575. zend_llist_element **saved_zend_extensions_elments;
  576. size_t new_zend_extensions_elments_count;
  577. zend_llist_element **new_zend_extensions_elments;
  578. zend_extension *ext;
  579. size_t i;
  580. zend_llist_element *element;
  581. TSRMLS_FETCH();
  582. /* restore startup hack */
  583. extension->startup = incompatible_zend_extension_info->old_startup;
  584. incompatible_zend_extension_info->old_startup = NULL;
  585. assert(extension->startup);
  586. /* save extensions list */
  587. saved_zend_extensions_container = zend_extensions;
  588. saved_zend_extensions_elments = malloc(sizeof(zend_llist_element *) * saved_zend_extensions_container.count);
  589. for (i = 0, element = saved_zend_extensions_container.head; element; ++i, element = element->next) {
  590. saved_zend_extensions_elments[i] = element;
  591. }
  592. /* hide all XCache extensions from it */
  593. zend_extensions.head = NULL;
  594. zend_extensions.tail = NULL;
  595. zend_extensions.count = 0;
  596. for (i = 0; i < saved_zend_extensions_container.count; ++i) {
  597. element = saved_zend_extensions_elments[i];
  598. element->next = element->prev = NULL;
  599. ext = (zend_extension *) element->data;
  600. if (!(strcmp(ext->name, XCACHE_NAME) == 0 || strncmp(ext->name, XCACHE_NAME " ", sizeof(XCACHE_NAME " ") - 1) == 0)) {
  601. xc_zend_llist_add_element(&zend_extensions, element);
  602. ++zend_extensions.count;
  603. }
  604. }
  605. assert(extension->startup != xc_incompatible_zend_extension_startup_hook);
  606. zend_try {
  607. status = extension->startup(extension);
  608. } zend_catch {
  609. catched = 1;
  610. } zend_end_try();
  611. /* save newly added extensions added by this extension*/
  612. new_zend_extensions_elments_count = zend_extensions.count - 1;
  613. new_zend_extensions_elments = NULL;
  614. if (new_zend_extensions_elments_count) {
  615. new_zend_extensions_elments = malloc(sizeof(zend_llist_element *) * new_zend_extensions_elments_count);
  616. element = zend_extensions.head;
  617. for (i = 0, element = element->next; element; ++i, element = element->next) {
  618. new_zend_extensions_elments[i] = element;
  619. }
  620. }
  621. /* restore original extension list*/
  622. zend_extensions = saved_zend_extensions_container;
  623. zend_extensions.head = NULL;
  624. zend_extensions.tail = NULL;
  625. zend_extensions.count = 0;
  626. for (i = 0; i < saved_zend_extensions_container.count; ++i) {
  627. element = saved_zend_extensions_elments[i];
  628. element->next = element->prev = NULL;
  629. xc_zend_llist_add_element(&zend_extensions, element);
  630. ++zend_extensions.count;
  631. ext = (zend_extension *) element->data;
  632. if (ext == extension && new_zend_extensions_elments_count) {
  633. /* add new created extension */
  634. size_t j;
  635. for (j = 0; j < new_zend_extensions_elments_count; ++j) {
  636. element = new_zend_extensions_elments[j];
  637. element->next = element->prev = NULL;
  638. xc_zend_llist_add_element(&zend_extensions, element);
  639. ++zend_extensions.count;
  640. }
  641. }
  642. }
  643. free(saved_zend_extensions_elments);
  644. if (new_zend_extensions_elments) {
  645. free(new_zend_extensions_elments);
  646. }
  647. if (catched) {
  648. zend_bailout();
  649. }
  650. return status;
  651. }
  652. /* }}} */
  653. static int xc_zend_startup(zend_extension *extension) /* {{{ */
  654. {
  655. zend_llist_position lpos;
  656. zend_extension *ext;
  657. ext = (zend_extension *) zend_extensions.head->data;
  658. if (strcmp(ext->name, XCACHE_NAME) != 0) {
  659. zend_error(E_WARNING, "XCache failed to load itself as the before \"%s\". compatibility downgraded", ext->name);
  660. }
  661. old_compile_file = zend_compile_file;
  662. zend_compile_file = xc_check_initial_compile_file;
  663. for (ext = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &lpos);
  664. ext;
  665. ext = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &lpos)) {
  666. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(ext->name);
  667. if (incompatible_zend_extension_info) {
  668. assert(!incompatible_zend_extension_info->old_startup);
  669. incompatible_zend_extension_info->old_startup = ext->startup;
  670. ext->startup = xc_incompatible_zend_extension_startup_hook;
  671. }
  672. }
  673. return SUCCESS;
  674. }
  675. /* }}} */
  676. static void xc_zend_shutdown(zend_extension *extension) /* {{{ */
  677. {
  678. }
  679. /* }}} */
  680. /* {{{ zend extension definition structure */
  681. static zend_extension xc_zend_extension_entry = {
  682. XCACHE_NAME,
  683. XCACHE_VERSION,
  684. XCACHE_AUTHOR,
  685. XCACHE_URL,
  686. XCACHE_COPYRIGHT,
  687. xc_zend_startup,
  688. xc_zend_shutdown,
  689. NULL, /* activate_func_t */
  690. NULL, /* deactivate_func_t */
  691. NULL, /* message_handler_func_t */
  692. NULL, /* op_array_handler_func_t */
  693. NULL, /* statement_handler_func_t */
  694. NULL, /* fcall_begin_handler_func_t */
  695. NULL, /* fcall_end_handler_func_t */
  696. NULL, /* op_array_ctor_func_t */
  697. NULL, /* op_array_dtor_func_t */
  698. STANDARD_ZEND_EXTENSION_PROPERTIES
  699. };
  700. /* }}} */
  701. /* {{{ PHP_INI */
  702. PHP_INI_BEGIN()
  703. PHP_INI_ENTRY1 ("xcache.coredump_directory", "", PHP_INI_SYSTEM, xcache_OnUpdateString, &xc_coredump_dir)
  704. #ifdef ZEND_WIN32
  705. PHP_INI_ENTRY1 ("xcache.coredump_type", "0", PHP_INI_SYSTEM, xcache_OnUpdateULong, &xc_coredump_type)
  706. #endif
  707. PHP_INI_ENTRY1_EX ("xcache.disable_on_crash", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_disable_on_crash, zend_ini_boolean_displayer_cb)
  708. PHP_INI_ENTRY1_EX ("xcache.test", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_test, zend_ini_boolean_displayer_cb)
  709. STD_PHP_INI_BOOLEAN("xcache.experimental", "0", PHP_INI_ALL, OnUpdateBool, experimental, zend_xcache_globals, xcache_globals)
  710. PHP_INI_END()
  711. /* }}} */
  712. static PHP_MINFO_FUNCTION(xcache) /* {{{ */
  713. {
  714. php_info_print_table_start();
  715. php_info_print_table_row(2, "XCache Version", XCACHE_VERSION);
  716. #ifdef XCACHE_VERSION_REVISION
  717. php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
  718. #endif
  719. php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
  720. php_info_print_table_end();
  721. DISPLAY_INI_ENTRIES();
  722. }
  723. /* }}} */
  724. static PHP_MINIT_FUNCTION(xcache) /* {{{ */
  725. {
  726. #ifndef PHP_GINIT
  727. ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
  728. #endif
  729. REGISTER_INI_ENTRIES();
  730. if (xc_coredump_dir && xc_coredump_dir[0]) {
  731. xcache_init_crash_handler();
  732. }
  733. if (strcmp(sapi_module.name, "cli") == 0) {
  734. char *env;
  735. if ((env = getenv("XCACHE_TEST")) != NULL) {
  736. xc_test = (zend_bool) atoi(env);
  737. }
  738. }
  739. xc_init_constant(module_number TSRMLS_CC);
  740. xc_shm_init_modules();
  741. /* must be the first */
  742. xcache_zend_extension_add(&xc_zend_extension_entry, 1);
  743. #ifdef HAVE_XCACHE_OPTIMIZER
  744. xc_optimizer_startup_module();
  745. #endif
  746. #ifdef HAVE_XCACHE_CACHER
  747. xc_cacher_startup_module();
  748. #endif
  749. #ifdef HAVE_XCACHE_COVERAGER
  750. xc_coverager_startup_module();
  751. #endif
  752. #ifdef HAVE_XCACHE_DISASSEMBLER
  753. xc_disassembler_startup_module();
  754. #endif
  755. return SUCCESS;
  756. }
  757. /* }}} */
  758. static PHP_MSHUTDOWN_FUNCTION(xcache) /* {{{ */
  759. {
  760. if (old_compile_file && zend_compile_file == xc_check_initial_compile_file) {
  761. zend_compile_file = old_compile_file;
  762. old_compile_file = NULL;
  763. }
  764. if (xc_coredump_dir && xc_coredump_dir[0]) {
  765. xcache_restore_crash_handler();
  766. }
  767. if (xc_coredump_dir) {
  768. pefree(xc_coredump_dir, 1);
  769. xc_coredump_dir = NULL;
  770. }
  771. #ifndef PHP_GINIT
  772. # ifdef ZTS
  773. ts_free_id(xcache_globals_id);
  774. # else
  775. xc_shutdown_globals(&xcache_globals TSRMLS_CC);
  776. # endif
  777. #endif
  778. UNREGISTER_INI_ENTRIES();
  779. xcache_zend_extension_remove(&xc_zend_extension_entry);
  780. return SUCCESS;
  781. }
  782. /* }}} */
  783. /* {{{ module dependencies */
  784. #ifdef STANDARD_MODULE_HEADER_EX
  785. static zend_module_dep xcache_module_deps[] = {
  786. ZEND_MOD_REQUIRED("standard")
  787. ZEND_MOD_CONFLICTS("apc")
  788. ZEND_MOD_CONFLICTS("eAccelerator")
  789. ZEND_MOD_CONFLICTS("Turck MMCache")
  790. ZEND_MOD_END
  791. };
  792. #endif
  793. /* }}} */
  794. /* {{{ module definition structure */
  795. zend_module_entry xcache_module_entry = {
  796. #ifdef STANDARD_MODULE_HEADER_EX
  797. STANDARD_MODULE_HEADER_EX,
  798. NULL,
  799. xcache_module_deps,
  800. #else
  801. STANDARD_MODULE_HEADER,
  802. #endif
  803. XCACHE_NAME,
  804. xcache_functions,
  805. PHP_MINIT(xcache),
  806. PHP_MSHUTDOWN(xcache),
  807. NULL, /* RINIT */
  808. NULL, /* RSHUTDOWN */
  809. PHP_MINFO(xcache),
  810. XCACHE_VERSION,
  811. #ifdef PHP_GINIT
  812. PHP_MODULE_GLOBALS(xcache),
  813. PHP_GINIT(xcache),
  814. PHP_GSHUTDOWN(xcache),
  815. #endif
  816. #ifdef ZEND_ENGINE_2
  817. NULL /* ZEND_MODULE_POST_ZEND_DEACTIVATE_N */,
  818. #else
  819. NULL,
  820. NULL,
  821. #endif
  822. STANDARD_MODULE_PROPERTIES_EX
  823. };
  824. #ifdef COMPILE_DL_XCACHE
  825. ZEND_GET_MODULE(xcache)
  826. #endif
  827. /* }}} */