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.

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