1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Felipe Pena <felipe@php.net> |
14 | Authors: Joe Watkins <joe.watkins@live.co.uk> |
15 | Authors: Bob Weinand <bwoebi@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "phpdbg.h"
20 #include "phpdbg_prompt.h"
21 #include "phpdbg_bp.h"
22 #include "phpdbg_break.h"
23 #include "phpdbg_list.h"
24 #include "phpdbg_utils.h"
25 #include "phpdbg_set.h"
26 #include "phpdbg_io.h"
27 #include "zend_alloc.h"
28 #include "phpdbg_print.h"
29 #include "phpdbg_help.h"
30 #include "phpdbg_arginfo.h"
31 #include "zend_vm.h"
32 #include "php_ini_builder.h"
33
34 #include "ext/standard/basic_functions.h"
35
36 #if defined(PHP_WIN32) && defined(HAVE_OPENSSL)
37 # include "openssl/applink.c"
38 #endif
39
40 #if defined(PHP_WIN32) && defined(ZTS)
41 ZEND_TSRMLS_CACHE_DEFINE()
42 #endif
43
44 ZEND_DECLARE_MODULE_GLOBALS(phpdbg)
45 int phpdbg_startup_run = 0;
46
47 static bool phpdbg_booted = 0;
48 static bool phpdbg_fully_started = 0;
49 bool use_mm_wrappers = 1;
50
php_phpdbg_destroy_bp_file(zval * brake)51 static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */
52 {
53 zend_hash_destroy(Z_ARRVAL_P(brake));
54 efree(Z_ARRVAL_P(brake));
55 } /* }}} */
56
php_phpdbg_destroy_bp_symbol(zval * brake)57 static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
58 {
59 efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
60 efree(Z_PTR_P(brake));
61 } /* }}} */
62
php_phpdbg_destroy_bp_opcode(zval * brake)63 static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
64 {
65 efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
66 efree(Z_PTR_P(brake));
67 } /* }}} */
68
php_phpdbg_destroy_bp_opline(zval * brake)69 static void php_phpdbg_destroy_bp_opline(zval *brake) /* {{{ */
70 {
71 efree(Z_PTR_P(brake));
72 } /* }}} */
73
php_phpdbg_destroy_bp_methods(zval * brake)74 static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
75 {
76 zend_hash_destroy(Z_ARRVAL_P(brake));
77 efree(Z_ARRVAL_P(brake));
78 } /* }}} */
79
php_phpdbg_destroy_bp_condition(zval * data)80 static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
81 {
82 phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
83
84 if (brake->ops) {
85 destroy_op_array(brake->ops);
86 efree(brake->ops);
87 }
88 efree((char*) brake->code);
89 efree(brake);
90 } /* }}} */
91
php_phpdbg_destroy_registered(zval * data)92 static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
93 {
94 zend_function_dtor(data);
95 } /* }}} */
96
php_phpdbg_destroy_file_source(zval * data)97 static void php_phpdbg_destroy_file_source(zval *data) /* {{{ */
98 {
99 phpdbg_file_source *source = (phpdbg_file_source *) Z_PTR_P(data);
100 destroy_op_array(&source->op_array);
101 if (source->buf) {
102 efree(source->buf);
103 }
104 efree(source);
105 } /* }}} */
106
php_phpdbg_globals_ctor(zend_phpdbg_globals * pg)107 static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
108 {
109 pg->prompt[0] = NULL;
110 pg->prompt[1] = NULL;
111
112 pg->colors[0] = NULL;
113 pg->colors[1] = NULL;
114 pg->colors[2] = NULL;
115
116 pg->lines = phpdbg_get_terminal_height();
117 pg->exec = NULL;
118 pg->exec_len = 0;
119 pg->buffer = NULL;
120 pg->last_was_newline = 1;
121 pg->ops = NULL;
122 pg->vmret = 0;
123 pg->in_execution = 0;
124 pg->bp_count = 0;
125 pg->flags = PHPDBG_DEFAULT_FLAGS;
126 memset(pg->io, 0, sizeof(pg->io));
127 pg->frame.num = 0;
128 pg->sapi_name_ptr = NULL;
129 pg->unclean_eval = 0;
130
131 pg->req_id = 0;
132 pg->err_buf.active = 0;
133 pg->err_buf.type = 0;
134
135 pg->input_buflen = 0;
136 pg->sigsafe_mem.mem = NULL;
137 pg->sigsegv_bailout = NULL;
138
139 pg->oplog_list = NULL;
140 pg->stdin_file = NULL;
141
142 pg->cur_command = NULL;
143 pg->last_line = 0;
144
145 #ifdef HAVE_USERFAULTFD_WRITEFAULT
146 pg->watch_userfaultfd = 0;
147 pg->watch_userfault_thread = 0;
148 #endif
149 } /* }}} */
150
PHP_MINIT_FUNCTION(phpdbg)151 static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
152 {
153 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
154 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
155 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
156 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
157 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
158 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
159 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, php_phpdbg_destroy_bp_opline, 0);
160 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
161 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
162 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
163 zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
164
165 zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
166 zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
167
168 zend_hash_init(&PHPDBG_G(file_sources), 0, NULL, php_phpdbg_destroy_file_source, 0);
169 phpdbg_setup_watchpoints();
170
171 zend_execute_ex = phpdbg_execute_ex;
172
173 register_phpdbg_symbols(module_number);
174
175 return SUCCESS;
176 } /* }}} */
177
PHP_MSHUTDOWN_FUNCTION(phpdbg)178 static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
179 {
180 zend_hash_destroy(&PHPDBG_G(registered));
181 phpdbg_destroy_watchpoints();
182
183 if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
184 phpdbg_notice("Script ended normally");
185 }
186
187 /* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
188 if (use_mm_wrappers) {
189 /* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
190 *(int *) zend_mm_get_heap() = 0;
191 }
192
193 if (PHPDBG_G(buffer)) {
194 free(PHPDBG_G(buffer));
195 PHPDBG_G(buffer) = NULL;
196 }
197
198 if (PHPDBG_G(exec)) {
199 free(PHPDBG_G(exec));
200 PHPDBG_G(exec) = NULL;
201 }
202
203 if (PHPDBG_G(oplog_list)) {
204 phpdbg_oplog_list *cur = PHPDBG_G(oplog_list);
205 do {
206 phpdbg_oplog_list *prev = cur->prev;
207 efree(cur);
208 cur = prev;
209 } while (cur != NULL);
210
211 zend_arena_destroy(PHPDBG_G(oplog_arena));
212 PHPDBG_G(oplog_list) = NULL;
213 }
214
215 fflush(stdout);
216 if (SG(request_info).argv0) {
217 free(SG(request_info).argv0);
218 SG(request_info).argv0 = NULL;
219 }
220
221 return SUCCESS;
222 }
223 /* }}} */
224
PHP_RINIT_FUNCTION(phpdbg)225 static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
226 {
227 /* deactivate symbol table caching to have these properly destroyed upon stack leaving (especially important for watchpoints) */
228 EG(symtable_cache_limit) = EG(symtable_cache);
229
230 if (zend_vm_kind() != ZEND_VM_KIND_HYBRID) {
231 /* phpdbg cannot work JIT-ed code */
232 zend_string *key = zend_string_init(ZEND_STRL("opcache.jit"), false);
233 zend_string *value = zend_string_init(ZEND_STRL("off"), false);
234
235 zend_alter_ini_entry_ex(key, value, ZEND_INI_SYSTEM, ZEND_INI_STAGE_STARTUP, false);
236
237 zend_string_release_ex(key, false);
238 zend_string_release_ex(value, false);
239 }
240
241 return SUCCESS;
242 } /* }}} */
243
PHP_RSHUTDOWN_FUNCTION(phpdbg)244 static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
245 {
246 if (PHPDBG_G(stdin_file)) {
247 fclose(PHPDBG_G(stdin_file));
248 PHPDBG_G(stdin_file) = NULL;
249 }
250
251 return SUCCESS;
252 } /* }}} */
253
254 /* {{{ Attempt to set the execution context for phpdbg
255 If the execution context was set previously it is returned
256 If the execution context was not set previously boolean true is returned
257 If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
PHP_FUNCTION(phpdbg_exec)258 PHP_FUNCTION(phpdbg_exec)
259 {
260 zend_string *exec;
261
262 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &exec) == FAILURE) {
263 RETURN_THROWS();
264 }
265
266 {
267 zend_stat_t sb = {0};
268 bool result = 1;
269
270 if (VCWD_STAT(ZSTR_VAL(exec), &sb) != FAILURE) {
271 if (sb.st_mode & (S_IFREG|S_IFLNK)) {
272 if (PHPDBG_G(exec)) {
273 ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len));
274 free(PHPDBG_G(exec));
275 result = 0;
276 }
277
278 PHPDBG_G(exec) = zend_strndup(ZSTR_VAL(exec), ZSTR_LEN(exec));
279 PHPDBG_G(exec_len) = ZSTR_LEN(exec);
280
281 if (result) {
282 ZVAL_TRUE(return_value);
283 }
284 } else {
285 zend_error(E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", ZSTR_VAL(exec));
286 ZVAL_FALSE(return_value);
287 }
288 } else {
289 zend_error(E_WARNING, "Failed to set execution context (%s) the file does not exist", ZSTR_VAL(exec));
290
291 ZVAL_FALSE(return_value);
292 }
293 }
294 } /* }}} */
295
296 /* {{{ instructs phpdbg to insert a breakpoint at the next opcode */
PHP_FUNCTION(phpdbg_break_next)297 PHP_FUNCTION(phpdbg_break_next)
298 {
299 zend_execute_data *ex;
300
301 if (zend_parse_parameters_none() == FAILURE) {
302 RETURN_THROWS();
303 }
304
305 ex = EG(current_execute_data);
306 while (ex && ex->func && !ZEND_USER_CODE(ex->func->type)) {
307 ex = ex->prev_execute_data;
308 }
309
310 if (!ex) {
311 return;
312 }
313
314 phpdbg_set_breakpoint_opline_ex((phpdbg_opline_ptr_t) ex->opline + 1);
315 } /* }}} */
316
317 /* {{{ */
PHP_FUNCTION(phpdbg_break_file)318 PHP_FUNCTION(phpdbg_break_file)
319 {
320 char *file;
321 size_t flen;
322 zend_long line;
323
324 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &file, &flen, &line) == FAILURE) {
325 RETURN_THROWS();
326 }
327
328 phpdbg_set_breakpoint_file(file, 0, line);
329 } /* }}} */
330
331 /* {{{ */
PHP_FUNCTION(phpdbg_break_method)332 PHP_FUNCTION(phpdbg_break_method)
333 {
334 char *class, *method;
335 size_t clen, mlen;
336
337 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &class, &clen, &method, &mlen) == FAILURE) {
338 RETURN_THROWS();
339 }
340
341 phpdbg_set_breakpoint_method(class, method);
342 } /* }}} */
343
344 /* {{{ */
PHP_FUNCTION(phpdbg_break_function)345 PHP_FUNCTION(phpdbg_break_function)
346 {
347 char *function;
348 size_t function_len;
349
350 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &function, &function_len) == FAILURE) {
351 RETURN_THROWS();
352 }
353
354 phpdbg_set_breakpoint_symbol(function, function_len);
355 } /* }}} */
356
357 /* {{{ instructs phpdbg to clear breakpoints */
PHP_FUNCTION(phpdbg_clear)358 PHP_FUNCTION(phpdbg_clear)
359 {
360 if (zend_parse_parameters_none() == FAILURE) {
361 RETURN_THROWS();
362 }
363
364 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
365 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
366 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
367 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
368 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
369 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
370 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
371 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
372 zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
373 } /* }}} */
374
375 /* {{{ */
PHP_FUNCTION(phpdbg_color)376 PHP_FUNCTION(phpdbg_color)
377 {
378 zend_long element;
379 char *color;
380 size_t color_len;
381
382 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &element, &color, &color_len) == FAILURE) {
383 RETURN_THROWS();
384 }
385
386 switch (element) {
387 case PHPDBG_COLOR_NOTICE:
388 case PHPDBG_COLOR_ERROR:
389 case PHPDBG_COLOR_PROMPT:
390 phpdbg_set_color_ex(element, color, color_len);
391 break;
392
393 default:
394 zend_argument_value_error(1, "must be one of PHPDBG_COLOR_PROMPT, PHPDBG_COLOR_NOTICE, or PHPDBG_COLOR_ERROR");
395 }
396 } /* }}} */
397
398 /* {{{ */
PHP_FUNCTION(phpdbg_prompt)399 PHP_FUNCTION(phpdbg_prompt)
400 {
401 char *prompt = NULL;
402 size_t prompt_len = 0;
403
404 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &prompt, &prompt_len) == FAILURE) {
405 RETURN_THROWS();
406 }
407
408 phpdbg_set_prompt(prompt);
409 } /* }}} */
410
411 /* {{{ */
PHP_FUNCTION(phpdbg_start_oplog)412 PHP_FUNCTION(phpdbg_start_oplog)
413 {
414 phpdbg_oplog_list *prev;
415
416 if (zend_parse_parameters_none() == FAILURE) {
417 RETURN_THROWS();
418 }
419
420 prev = PHPDBG_G(oplog_list);
421
422 if (!prev) {
423 PHPDBG_G(oplog_arena) = zend_arena_create(64 * 1024);
424 }
425
426 PHPDBG_G(oplog_list) = emalloc(sizeof(phpdbg_oplog_list));
427 PHPDBG_G(oplog_list)->prev = prev;
428 PHPDBG_G(oplog_cur) = &PHPDBG_G(oplog_list)->start;
429 PHPDBG_G(oplog_cur)->next = NULL;
430 }
431
phpdbg_is_ignored_opcode(zend_uchar opcode)432 static zend_always_inline bool phpdbg_is_ignored_opcode(zend_uchar opcode) {
433 return
434 opcode == ZEND_NOP || opcode == ZEND_OP_DATA || opcode == ZEND_FE_FREE || opcode == ZEND_FREE || opcode == ZEND_ASSERT_CHECK || opcode == ZEND_VERIFY_RETURN_TYPE
435 || opcode == ZEND_DECLARE_CONST || opcode == ZEND_DECLARE_CLASS || opcode == ZEND_DECLARE_FUNCTION
436 || opcode == ZEND_DECLARE_CLASS_DELAYED
437 || opcode == ZEND_DECLARE_ANON_CLASS || opcode == ZEND_FAST_RET || opcode == ZEND_TICKS
438 || opcode == ZEND_EXT_STMT || opcode == ZEND_EXT_FCALL_BEGIN || opcode == ZEND_EXT_FCALL_END
439 || opcode == ZEND_BIND_GLOBAL
440 ;
441 }
442
phpdbg_oplog_fill_executable(zend_op_array * op_array,HashTable * insert_ht,bool by_opcode)443 static void phpdbg_oplog_fill_executable(zend_op_array *op_array, HashTable *insert_ht, bool by_opcode) {
444 /* ignore RECV_* opcodes */
445 zend_op *cur = op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC);
446 zend_op *end = op_array->opcodes + op_array->last;
447
448 zend_long insert_idx;
449 zval zero;
450 ZVAL_LONG(&zero, 0);
451
452 /* ignore autogenerated return (well, not too precise with finally branches, but that's okay) */
453 if (op_array->last >= 1 && (((end - 1)->opcode == ZEND_RETURN || (end - 1)->opcode == ZEND_RETURN_BY_REF || (end - 1)->opcode == ZEND_GENERATOR_RETURN)
454 && ((op_array->last > 1 && ((end - 2)->opcode == ZEND_RETURN || (end - 2)->opcode == ZEND_RETURN_BY_REF || (end - 2)->opcode == ZEND_GENERATOR_RETURN || (end - 2)->opcode == ZEND_THROW))
455 || op_array->function_name == NULL || (end - 1)->extended_value == -1))) {
456 end--;
457 }
458
459 for (; cur < end; cur++) {
460 zend_uchar opcode = cur->opcode;
461 if (phpdbg_is_ignored_opcode(opcode)) {
462 continue;
463 }
464
465 if (by_opcode) {
466 insert_idx = cur - op_array->opcodes;
467 } else {
468 insert_idx = cur->lineno;
469 }
470
471 if (opcode == ZEND_NEW && cur[1].opcode == ZEND_DO_FCALL) {
472 cur++;
473 }
474
475 zend_hash_index_update(insert_ht, insert_idx, &zero);
476 }
477 }
478
phpdbg_add_empty_array(HashTable * ht,zend_string * name)479 static inline HashTable* phpdbg_add_empty_array(HashTable *ht, zend_string *name) {
480 zval *ht_zv = zend_hash_find(ht, name);
481 if (!ht_zv) {
482 zval zv;
483 array_init(&zv);
484 ht_zv = zend_hash_add_new(ht, name, &zv);
485 }
486 return Z_ARR_P(ht_zv);
487 }
488
489 /* {{{ */
PHP_FUNCTION(phpdbg_get_executable)490 PHP_FUNCTION(phpdbg_get_executable)
491 {
492 HashTable *options = NULL;
493 zval *option_buffer;
494 bool by_function = 0;
495 bool by_opcode = 0;
496 HashTable *insert_ht;
497
498 zend_function *func;
499 zend_class_entry *ce;
500 zend_string *name;
501 HashTable *files = &PHPDBG_G(file_sources);
502 HashTable files_tmp;
503
504 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
505 RETURN_THROWS();
506 }
507
508 if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
509 by_function = zend_is_true(option_buffer);
510 }
511
512 if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
513 if (by_function) {
514 by_opcode = zend_is_true(option_buffer);
515 }
516 }
517
518 if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("files")))) {
519 ZVAL_DEREF(option_buffer);
520 if (Z_TYPE_P(option_buffer) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(option_buffer)) > 0) {
521 zval *filename;
522
523 files = &files_tmp;
524 zend_hash_init(files, 0, NULL, NULL, 0);
525
526 ZEND_HASH_FOREACH_VAL(Z_ARR_P(option_buffer), filename) {
527 zend_hash_add_empty_element(files, zval_get_string(filename));
528 } ZEND_HASH_FOREACH_END();
529 } else {
530 GC_ADDREF(files);
531 }
532 } else {
533 GC_ADDREF(files);
534 }
535
536 array_init(return_value);
537
538 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
539 if (func->type == ZEND_USER_FUNCTION) {
540 if (zend_hash_exists(files, func->op_array.filename)) {
541 insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
542
543 if (by_function) {
544 insert_ht = phpdbg_add_empty_array(insert_ht, name);
545 }
546
547 phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
548 }
549 }
550 } ZEND_HASH_FOREACH_END();
551
552 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), name, ce) {
553 if (ce->type == ZEND_USER_CLASS) {
554 if (zend_hash_exists(files, ce->info.user.filename)) {
555 ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, func) {
556 if (func->type == ZEND_USER_FUNCTION && zend_hash_exists(files, func->op_array.filename)) {
557 insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
558
559 if (by_function) {
560 zend_string *fn_name = strpprintf(ZSTR_LEN(name) + ZSTR_LEN(func->op_array.function_name) + 2, "%.*s::%.*s", (int) ZSTR_LEN(name), ZSTR_VAL(name), (int) ZSTR_LEN(func->op_array.function_name), ZSTR_VAL(func->op_array.function_name));
561 insert_ht = phpdbg_add_empty_array(insert_ht, fn_name);
562 zend_string_release(fn_name);
563 }
564
565 phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
566 }
567 } ZEND_HASH_FOREACH_END();
568 }
569 }
570 } ZEND_HASH_FOREACH_END();
571
572 ZEND_HASH_MAP_FOREACH_STR_KEY(files, name) {
573 phpdbg_file_source *source = zend_hash_find_ptr(&PHPDBG_G(file_sources), name);
574 if (source) {
575 phpdbg_oplog_fill_executable(
576 &source->op_array,
577 phpdbg_add_empty_array(Z_ARR_P(return_value), source->op_array.filename),
578 by_opcode);
579 }
580 } ZEND_HASH_FOREACH_END();
581
582 if (!GC_DELREF(files)) {
583 zend_hash_destroy(files);
584 }
585 }
586
587 /* {{{ */
PHP_FUNCTION(phpdbg_end_oplog)588 PHP_FUNCTION(phpdbg_end_oplog)
589 {
590 phpdbg_oplog_entry *cur;
591 phpdbg_oplog_list *prev;
592
593 HashTable *options = NULL;
594 zval *option_buffer;
595 bool by_function = 0;
596 bool by_opcode = 0;
597
598 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
599 RETURN_THROWS();
600 }
601
602 if (!PHPDBG_G(oplog_list)) {
603 zend_error(E_WARNING, "Cannot end an oplog without starting it");
604 return;
605 }
606
607 cur = PHPDBG_G(oplog_list)->start.next;
608 prev = PHPDBG_G(oplog_list)->prev;
609
610 efree(PHPDBG_G(oplog_list));
611 PHPDBG_G(oplog_list) = prev;
612
613 if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
614 by_function = zend_is_true(option_buffer);
615 }
616
617 if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
618 if (by_function) {
619 by_opcode = zend_is_true(option_buffer);
620 }
621 }
622
623 array_init(return_value);
624
625 {
626 zend_string *last_file = NULL;
627 HashTable *file_ht = NULL;
628 zend_string *last_function = (void *)~(uintptr_t)0;
629 zend_class_entry *last_scope = NULL;
630
631 HashTable *insert_ht = NULL;
632 zend_long insert_idx;
633
634 do {
635 zval zero;
636 ZVAL_LONG(&zero, 0);
637
638 if (cur->filename != last_file) {
639 last_file = cur->filename;
640 file_ht = insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), last_file);
641 }
642
643 if (by_function) {
644 if (cur->function_name == NULL) {
645 if (last_function != NULL) {
646 insert_ht = file_ht;
647 }
648 last_function = NULL;
649 } else if (cur->function_name != last_function || cur->scope != last_scope) {
650 zend_string *fn_name;
651 last_function = cur->function_name;
652 last_scope = cur->scope;
653 if (last_scope == NULL) {
654 fn_name = zend_string_copy(last_function);
655 } else {
656 fn_name = strpprintf(ZSTR_LEN(last_function) + ZSTR_LEN(last_scope->name) + 2, "%.*s::%.*s", (int) ZSTR_LEN(last_scope->name), ZSTR_VAL(last_scope->name), (int) ZSTR_LEN(last_function), ZSTR_VAL(last_function));
657 }
658 insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), fn_name);
659 zend_string_release(fn_name);
660 }
661 }
662
663 if (by_opcode) {
664 insert_idx = cur->op - cur->opcodes;
665 } else {
666 if (phpdbg_is_ignored_opcode(cur->op->opcode)) {
667 continue;
668 }
669
670 insert_idx = cur->op->lineno;
671 }
672
673 ZEND_ASSERT(insert_ht && file_ht);
674 {
675 zval *num = zend_hash_index_find(insert_ht, insert_idx);
676 if (!num) {
677 num = zend_hash_index_add_new(insert_ht, insert_idx, &zero);
678 }
679 Z_LVAL_P(num)++;
680 }
681
682 } while ((cur = cur->next));
683 }
684
685 if (!prev) {
686 zend_arena_destroy(PHPDBG_G(oplog_arena));
687 }
688 }
689
690 static zend_module_entry sapi_phpdbg_module_entry = {
691 STANDARD_MODULE_HEADER,
692 PHPDBG_NAME,
693 ext_functions,
694 PHP_MINIT(phpdbg),
695 PHP_MSHUTDOWN(phpdbg),
696 PHP_RINIT(phpdbg),
697 PHP_RSHUTDOWN(phpdbg),
698 NULL,
699 PHPDBG_VERSION,
700 STANDARD_MODULE_PROPERTIES
701 };
702
php_sapi_phpdbg_module_startup(sapi_module_struct * module)703 static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
704 {
705 if (php_module_startup(module, &sapi_phpdbg_module_entry) == FAILURE) {
706 return FAILURE;
707 }
708
709 phpdbg_booted = 1;
710
711 return SUCCESS;
712 } /* }}} */
713
php_sapi_phpdbg_read_cookies(void)714 static char* php_sapi_phpdbg_read_cookies(void) /* {{{ */
715 {
716 return NULL;
717 } /* }}} */
718
php_sapi_phpdbg_header_handler(sapi_header_struct * h,sapi_header_op_enum op,sapi_headers_struct * s)719 static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) /* {{{ */
720 {
721 return 0;
722 }
723 /* }}} */
724
php_sapi_phpdbg_send_headers(sapi_headers_struct * sapi_headers)725 static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
726 {
727 /* We do nothing here, this function is needed to prevent that the fallback
728 * header handling is called. */
729 return SAPI_HEADER_SENT_SUCCESSFULLY;
730 }
731 /* }}} */
732
php_sapi_phpdbg_send_header(sapi_header_struct * sapi_header,void * server_context)733 static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context) /* {{{ */
734 {
735 }
736 /* }}} */
737
php_sapi_phpdbg_log_message(const char * message,int syslog_type_int)738 static void php_sapi_phpdbg_log_message(const char *message, int syslog_type_int) /* {{{ */
739 {
740 /*
741 * We must not request TSRM before being booted
742 */
743 if (phpdbg_booted) {
744 if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) {
745 phpdbg_error("%s", message);
746 return;
747 }
748
749 phpdbg_error("%s", message);
750
751 if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
752 return;
753 }
754
755 if (PG(last_error_type) & E_FATAL_ERRORS) {
756 const char *file_char = zend_get_executed_filename();
757 zend_string *file = zend_string_init(file_char, strlen(file_char), 0);
758 phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
759 zend_string_release(file);
760
761 if (!phpdbg_fully_started) {
762 return;
763 }
764
765 do {
766 switch (phpdbg_interactive(1, NULL)) {
767 case PHPDBG_LEAVE:
768 case PHPDBG_FINISH:
769 case PHPDBG_UNTIL:
770 case PHPDBG_NEXT:
771 return;
772 }
773 } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
774 }
775 } else {
776 fprintf(stdout, "%s\n", message);
777 }
778 }
779 /* }}} */
780
php_sapi_phpdbg_activate(void)781 static int php_sapi_phpdbg_activate(void) /* {{{ */
782 {
783 return SUCCESS;
784 }
785
php_sapi_phpdbg_deactivate(void)786 static int php_sapi_phpdbg_deactivate(void) /* {{{ */
787 {
788 /* Everything using ZMM should be freed here... */
789 zend_hash_destroy(&PHPDBG_G(file_sources));
790 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
791 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
792 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
793 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
794 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
795 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
796 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
797 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
798 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
799 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
800 zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
801 zend_hash_destroy(&PHPDBG_G(seek));
802
803 if (PHPDBG_G(ops)) {
804 destroy_op_array(PHPDBG_G(ops));
805 efree(PHPDBG_G(ops));
806 PHPDBG_G(ops) = NULL;
807 }
808
809 return SUCCESS;
810 }
811
php_sapi_phpdbg_register_vars(zval * track_vars_array)812 static void php_sapi_phpdbg_register_vars(zval *track_vars_array) /* {{{ */
813 {
814 size_t len;
815 char *docroot = "";
816
817 /* In phpdbg mode, we consider the environment to be a part of the server variables
818 */
819 php_import_environment_variables(track_vars_array);
820
821 if (PHPDBG_G(exec)) {
822 len = PHPDBG_G(exec_len);
823 if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
824 php_register_variable("PHP_SELF", PHPDBG_G(exec), track_vars_array);
825 }
826 if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
827 php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), track_vars_array);
828 }
829
830 if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
831 php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), track_vars_array);
832 }
833 if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
834 php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), track_vars_array);
835 }
836 }
837
838 /* any old docroot will do */
839 len = 0;
840 if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) {
841 php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array);
842 }
843 }
844 /* }}} */
845
php_sapi_phpdbg_ub_write(const char * message,size_t length)846 static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length) /* {{{ */
847 {
848 return phpdbg_process_print(PHPDBG_G(io)[PHPDBG_STDOUT].fd, P_STDOUT, message, (int) length);
849 } /* }}} */
850
851 /* beginning of struct, see main/streams/plain_wrapper.c line 111 */
852 typedef struct {
853 FILE *file;
854 int fd;
855 } php_stdio_stream_data;
856
phpdbg_stdiop_write(php_stream * stream,const char * buf,size_t count)857 static ssize_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count) {
858 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
859
860 while (data->fd >= 0) {
861 struct stat stat[3];
862 memset(stat, 0, sizeof(stat));
863 int stat_stderr = fstat(fileno(stderr), &stat[2]);
864 int stat_stdout = fstat(fileno(stdout), &stat[0]);
865 int stat_datafd = fstat(data->fd, &stat[1]);
866 if ((stat_stderr < 0 && stat_stdout < 0) || stat_datafd < 0) {
867 break;
868 }
869
870 if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) {
871 phpdbg_script(P_STDOUT, "%.*s", (int) count, buf);
872 return count;
873 }
874 if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) {
875 phpdbg_script(P_STDERR, "%.*s", (int) count, buf);
876 return count;
877 }
878 break;
879 }
880
881 return PHPDBG_G(php_stdiop_write)(stream, buf, count);
882 }
883
884 /* copied from sapi/cli/php_cli.c cli_register_file_handles */
phpdbg_register_file_handles(void)885 void phpdbg_register_file_handles(void) /* {{{ */
886 {
887 zval zin, zout, zerr;
888 php_stream *s_in, *s_out, *s_err;
889 php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
890 zend_constant ic, oc, ec;
891
892 s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in);
893 s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
894 s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
895
896 if (s_in==NULL || s_out==NULL || s_err==NULL) {
897 if (s_in) php_stream_close(s_in);
898 if (s_out) php_stream_close(s_out);
899 if (s_err) php_stream_close(s_err);
900 return;
901 }
902
903 #if PHP_DEBUG
904 /* do not close stdout and stderr */
905 s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
906 s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
907 #endif
908
909 php_stream_to_zval(s_in, &zin);
910 php_stream_to_zval(s_out, &zout);
911 php_stream_to_zval(s_err, &zerr);
912
913 ic.value = zin;
914 ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0);
915 ic.name = zend_string_init(ZEND_STRL("STDIN"), 0);
916 zend_hash_del(EG(zend_constants), ic.name);
917 zend_register_constant(&ic);
918
919 oc.value = zout;
920 ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0);
921 oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0);
922 zend_hash_del(EG(zend_constants), oc.name);
923 zend_register_constant(&oc);
924
925 ec.value = zerr;
926 ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0);
927 ec.name = zend_string_init(ZEND_STRL("STDERR"), 0);
928 zend_hash_del(EG(zend_constants), ec.name);
929 zend_register_constant(&ec);
930 }
931 /* }}} */
932
933 /* {{{ sapi_module_struct phpdbg_sapi_module */
934 static sapi_module_struct phpdbg_sapi_module = {
935 "phpdbg", /* name */
936 "phpdbg", /* pretty name */
937
938 php_sapi_phpdbg_module_startup, /* startup */
939 php_module_shutdown_wrapper, /* shutdown */
940
941 php_sapi_phpdbg_activate, /* activate */
942 php_sapi_phpdbg_deactivate, /* deactivate */
943
944 php_sapi_phpdbg_ub_write, /* unbuffered write */
945 NULL, /* flush */
946 NULL, /* get uid */
947 NULL, /* getenv */
948
949 php_error, /* error handler */
950
951 php_sapi_phpdbg_header_handler, /* header handler */
952 php_sapi_phpdbg_send_headers, /* send headers handler */
953 php_sapi_phpdbg_send_header, /* send header handler */
954
955 NULL, /* read POST data */
956 php_sapi_phpdbg_read_cookies, /* read Cookies */
957
958 php_sapi_phpdbg_register_vars, /* register server variables */
959 php_sapi_phpdbg_log_message, /* Log message */
960 NULL, /* Get request time */
961 NULL, /* Child terminate */
962 STANDARD_SAPI_MODULE_PROPERTIES
963 };
964 /* }}} */
965
966 const opt_struct OPTIONS[] = { /* {{{ */
967 {'c', 1, "ini path override"},
968 {'d', 1, "define ini entry on command line"},
969 {'n', 0, "no php.ini"},
970 {'z', 1, "load zend_extension"},
971 /* phpdbg options */
972 {'q', 0, "no banner"},
973 {'v', 0, "disable quietness"},
974 {'b', 0, "boring colours"},
975 {'i', 1, "specify init"},
976 {'I', 0, "ignore init"},
977 {'O', 1, "opline log"},
978 {'r', 0, "run"},
979 {'e', 0, "generate ext_stmt opcodes"},
980 {'E', 0, "step-through-eval"},
981 {'s', 1, "script from stdin"},
982 {'S', 1, "sapi-name"},
983 {'p', 2, "show opcodes"},
984 {'h', 0, "help"},
985 {'V', 0, "version"},
986 {'-', 0, NULL}
987 }; /* }}} */
988
989 const char phpdbg_ini_hardcoded[] =
990 "html_errors=Off\n"
991 "register_argc_argv=On\n"
992 "implicit_flush=On\n"
993 "display_errors=Off\n"
994 "log_errors=On\n"
995 "max_execution_time=0\n"
996 "max_input_time=-1\n"
997 "error_log=\n"
998 "output_buffering=off\n";
999
phpdbg_welcome(bool cleaning)1000 static void phpdbg_welcome(bool cleaning) /* {{{ */
1001 {
1002 /* print blurb */
1003 if (!cleaning) {
1004 phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
1005 phpdbg_writeln("To get help using phpdbg type \"help\" and press enter");
1006 phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
1007 } else if (phpdbg_startup_run == 0) {
1008 phpdbg_write(
1009 "Classes %d\n"
1010 "Functions %d\n"
1011 "Constants %d\n"
1012 "Includes %d\n",
1013 zend_hash_num_elements(EG(class_table)),
1014 zend_hash_num_elements(EG(function_table)),
1015 zend_hash_num_elements(EG(zend_constants)),
1016 zend_hash_num_elements(&EG(included_files)));
1017 }
1018 } /* }}} */
1019
phpdbg_sigint_handler(int signo)1020 static inline void phpdbg_sigint_handler(int signo) /* {{{ */
1021 {
1022 if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
1023 /* set signalled only when not interactive */
1024 if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
1025 char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
1026
1027 phpdbg_set_sigsafe_mem(mem);
1028 zend_try {
1029 phpdbg_force_interruption();
1030 } zend_end_try()
1031 phpdbg_clear_sigsafe_mem();
1032
1033 PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
1034
1035 if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) {
1036 zend_bailout();
1037 }
1038 } else {
1039 PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
1040 if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
1041 PHPDBG_G(flags) |= PHPDBG_HAS_PAGINATION;
1042 PHPDBG_G(flags) &= ~PHPDBG_PREVENT_INTERACTIVE;
1043 }
1044 }
1045 }
1046 } /* }}} */
1047
1048 #ifndef _WIN32
phpdbg_signal_handler(int sig,siginfo_t * info,void * context)1049 void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
1050 {
1051 int is_handled = FAILURE;
1052
1053 switch (sig) {
1054 case SIGBUS:
1055 case SIGSEGV:
1056 is_handled = phpdbg_watchpoint_segfault_handler(info, context);
1057 if (is_handled == FAILURE) {
1058 if (PHPDBG_G(sigsegv_bailout)) {
1059 LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
1060 }
1061 zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
1062 }
1063 break;
1064 }
1065
1066 } /* }}} */
1067
1068
phpdbg_sighup_handler(int sig)1069 void phpdbg_sighup_handler(int sig) /* {{{ */
1070 {
1071 exit(0);
1072 } /* }}} */
1073 #endif
1074
phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1075 ZEND_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_ALLOC_SIZE(1) void *phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1076 {
1077 return _zend_mm_alloc(zend_mm_get_heap(), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1078 } /* }}} */
1079
phpdbg_free_wrapper(void * p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1080 void phpdbg_free_wrapper(void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1081 {
1082 zend_mm_heap *heap = zend_mm_get_heap();
1083 if (UNEXPECTED(heap == p)) {
1084 /* TODO: heap maybe allocated by mmap(zend_mm_init) or malloc(USE_ZEND_ALLOC=0)
1085 * let's prevent it from segfault for now
1086 */
1087 } else {
1088 phpdbg_watch_efree(p);
1089 _zend_mm_free(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1090 }
1091 } /* }}} */
1092
phpdbg_realloc_wrapper(void * ptr,size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1093 void *phpdbg_realloc_wrapper(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1094 {
1095 return _zend_mm_realloc(zend_mm_get_heap(), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1096 } /* }}} */
1097
phpdbg_stream_url_wrap_php(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)1098 php_stream *phpdbg_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
1099 {
1100 if (!strncasecmp(path, "php://", 6)) {
1101 path += 6;
1102 }
1103
1104 if (!strncasecmp(path, "stdin", 6) && PHPDBG_G(stdin_file)) {
1105 php_stream *stream = php_stream_fopen_from_fd(dup(fileno(PHPDBG_G(stdin_file))), "r", NULL);
1106 #ifdef PHP_WIN32
1107 if (context != NULL) {
1108 zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");
1109 if (blocking_pipes) {
1110 convert_to_long(blocking_pipes);
1111 php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, Z_LVAL_P(blocking_pipes), NULL);
1112 }
1113 }
1114 #endif
1115 return stream;
1116 }
1117
1118 return PHPDBG_G(orig_url_wrap_php)->wops->stream_opener(wrapper, path, mode, options, opened_path, context STREAMS_CC);
1119 } /* }}} */
1120
main(int argc,char ** argv)1121 int main(int argc, char **argv) /* {{{ */
1122 {
1123 sapi_module_struct *phpdbg = &phpdbg_sapi_module;
1124 char *sapi_name;
1125 struct php_ini_builder ini_builder;
1126 char **zend_extensions = NULL;
1127 zend_ulong zend_extensions_len = 0L;
1128 bool ini_ignore;
1129 char *ini_override;
1130 char *exec = NULL;
1131 char *first_command = NULL;
1132 char *init_file;
1133 size_t init_file_len;
1134 bool init_file_default;
1135 uint64_t flags;
1136 char *php_optarg;
1137 int php_optind, opt, show_banner = 1;
1138 long cleaning = -1;
1139 volatile bool quit_immediately = 0; /* somehow some gcc release builds will play a bit around with order in combination with setjmp..., hence volatile */
1140 zend_phpdbg_globals *settings = NULL;
1141 char *bp_tmp = NULL;
1142 char *print_opline_func;
1143 bool ext_stmt = 0;
1144 bool is_exit;
1145 int exit_status;
1146 char *read_from_stdin = NULL;
1147 zend_string *backup_phpdbg_compile = NULL;
1148 bool show_help = 0, show_version = 0;
1149 void* (*_malloc)(size_t);
1150 void (*_free)(void*);
1151 void* (*_realloc)(void*, size_t);
1152 php_stream_wrapper wrapper;
1153 php_stream_wrapper_ops wops;
1154
1155 #ifdef PHP_WIN32
1156 _fmode = _O_BINARY; /* sets default for file streams to binary */
1157 setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
1158 setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1159 setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1160 #else
1161 struct sigaction signal_struct;
1162 signal_struct.sa_sigaction = phpdbg_signal_handler;
1163 signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
1164 #endif
1165
1166 phpdbg_main:
1167 #ifdef ZTS
1168 php_tsrm_startup();
1169 # ifdef PHP_WIN32
1170 ZEND_TSRMLS_CACHE_UPDATE();
1171 # endif
1172 #endif
1173
1174 zend_signal_startup();
1175
1176 php_ini_builder_init(&ini_builder);
1177 ini_ignore = 0;
1178 ini_override = NULL;
1179 zend_extensions = NULL;
1180 zend_extensions_len = 0L;
1181 init_file = NULL;
1182 init_file_len = 0;
1183 init_file_default = 1;
1184 flags = PHPDBG_DEFAULT_FLAGS;
1185 is_exit = 0;
1186 php_optarg = NULL;
1187 php_optind = 1;
1188 opt = 0;
1189 sapi_name = NULL;
1190 exit_status = 0;
1191 if (settings) {
1192 exec = settings->exec;
1193 }
1194
1195 while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1196 switch (opt) {
1197 case 'r':
1198 if (settings == NULL) {
1199 phpdbg_startup_run++;
1200 }
1201 break;
1202 case 'n':
1203 ini_ignore = 1;
1204 break;
1205 case 'c':
1206 if (ini_override) {
1207 free(ini_override);
1208 }
1209 ini_override = strdup(php_optarg);
1210 break;
1211 case 'd':
1212 /* define ini entries on command line */
1213 php_ini_builder_define(&ini_builder, php_optarg);
1214 break;
1215
1216 case 'z':
1217 zend_extensions_len++;
1218 if (zend_extensions) {
1219 zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
1220 } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
1221 zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
1222 break;
1223
1224 /* begin phpdbg options */
1225
1226 case 's': { /* read script from stdin */
1227 if (settings == NULL) {
1228 read_from_stdin = strdup(php_optarg);
1229 }
1230 } break;
1231
1232 case 'S': { /* set SAPI name */
1233 sapi_name = strdup(php_optarg);
1234 } break;
1235
1236 case 'I': { /* ignore .phpdbginit */
1237 init_file_default = 0;
1238 } break;
1239
1240 case 'i': { /* set init file */
1241 if (init_file) {
1242 free(init_file);
1243 init_file = NULL;
1244 }
1245
1246 init_file_len = strlen(php_optarg);
1247 if (init_file_len) {
1248 init_file = strdup(php_optarg);
1249 }
1250 } break;
1251
1252 case 'v': /* set quietness off */
1253 flags &= ~PHPDBG_IS_QUIET;
1254 break;
1255
1256 case 'e':
1257 ext_stmt = 1;
1258 break;
1259
1260 case 'E': /* stepping through eval on */
1261 flags |= PHPDBG_IS_STEPONEVAL;
1262 break;
1263
1264 case 'b': /* set colours off */
1265 flags &= ~PHPDBG_IS_COLOURED;
1266 break;
1267
1268 case 'q': /* hide banner */
1269 show_banner = 0;
1270 break;
1271
1272 case 'p': {
1273 print_opline_func = php_optarg;
1274 show_banner = 0;
1275 settings = (void *) 0x1;
1276 } break;
1277
1278 case 'h': {
1279 show_help = 1;
1280 } break;
1281
1282 case 'V': {
1283 show_version = 1;
1284 } break;
1285 }
1286
1287 php_optarg = NULL;
1288 }
1289
1290 quit_immediately = phpdbg_startup_run > 1;
1291
1292 /* set exec if present on command line */
1293 if (!read_from_stdin && argc > php_optind) {
1294 if (!exec && strlen(argv[php_optind])) {
1295 exec = strdup(argv[php_optind]);
1296 }
1297 php_optind++;
1298 }
1299
1300 if (sapi_name) {
1301 phpdbg->name = sapi_name;
1302 }
1303
1304 phpdbg->ini_defaults = NULL;
1305 phpdbg->phpinfo_as_text = 1;
1306 phpdbg->php_ini_ignore_cwd = 1;
1307
1308 sapi_startup(phpdbg);
1309
1310 phpdbg->executable_location = argv[0];
1311 phpdbg->phpinfo_as_text = 1;
1312 phpdbg->php_ini_ignore = ini_ignore;
1313 phpdbg->php_ini_path_override = ini_override;
1314
1315 php_ini_builder_prepend_literal(&ini_builder, phpdbg_ini_hardcoded);
1316
1317 if (zend_extensions_len) {
1318 zend_ulong zend_extension = 0L;
1319
1320 while (zend_extension < zend_extensions_len) {
1321 const char *ze = zend_extensions[zend_extension];
1322 size_t ze_len = strlen(ze);
1323
1324 php_ini_builder_unquoted(&ini_builder, "zend_extension", strlen("zend_extension"), ze, ze_len);
1325
1326 free(zend_extensions[zend_extension]);
1327 zend_extension++;
1328 }
1329
1330 free(zend_extensions);
1331 }
1332
1333 phpdbg->ini_entries = php_ini_builder_finish(&ini_builder);
1334
1335 ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
1336
1337 /* set default colors */
1338 phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold"));
1339 phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold"));
1340 phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green"));
1341
1342 if (settings > (zend_phpdbg_globals *) 0x2) {
1343 #ifdef ZTS
1344 zend_phpdbg_globals *ptr = TSRMG_BULK_STATIC(phpdbg_globals_id, zend_phpdbg_globals *);
1345 *ptr = *settings;
1346 #else
1347 phpdbg_globals = *settings;
1348 #endif
1349 free(settings);
1350 } else {
1351 /* set default prompt */
1352 phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
1353 }
1354
1355 /* set flags from command line */
1356 PHPDBG_G(flags) = flags;
1357
1358 if (phpdbg->startup(phpdbg) == SUCCESS) {
1359 zend_mm_heap *mm_heap;
1360 #ifdef _WIN32
1361 EXCEPTION_POINTERS *xp;
1362 __try {
1363 #endif
1364
1365 if (show_version || show_help) {
1366 /* It ain't gonna proceed to real execution anyway,
1367 but the correct descriptor is needed already. */
1368 PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1369 if (show_help) {
1370 phpdbg_do_help_cmd(exec);
1371 } else if (show_version) {
1372 phpdbg_out(
1373 "phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) The PHP Group\n%s",
1374 PHPDBG_VERSION,
1375 __DATE__,
1376 __TIME__,
1377 PHP_VERSION,
1378 get_zend_version()
1379 );
1380 }
1381 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1382 php_module_shutdown();
1383 sapi_deactivate();
1384 sapi_shutdown();
1385 php_ini_builder_deinit(&ini_builder);
1386 if (ini_override) {
1387 free(ini_override);
1388 }
1389 if (exec) {
1390 free(exec);
1391 }
1392 if (init_file) {
1393 free(init_file);
1394 }
1395 goto free_and_return;
1396 }
1397
1398 zend_try {
1399 zend_signal_activate();
1400 } zend_end_try();
1401
1402 #ifndef _WIN32
1403 zend_signal(SIGHUP, phpdbg_sighup_handler);
1404 #endif
1405
1406 mm_heap = zend_mm_get_heap();
1407 zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc);
1408
1409 use_mm_wrappers = !_malloc && !_realloc && !_free;
1410
1411 PHPDBG_G(original_free_function) = _free;
1412 _free = phpdbg_watch_efree;
1413
1414 if (use_mm_wrappers) {
1415 #if ZEND_DEBUG
1416 zend_mm_set_custom_debug_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1417 #else
1418 zend_mm_set_custom_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1419 #endif
1420 } else {
1421 zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
1422 }
1423
1424 _free = PHPDBG_G(original_free_function);
1425
1426
1427 phpdbg_init_list();
1428
1429 PHPDBG_G(sapi_name_ptr) = sapi_name;
1430
1431 if (exec) { /* set execution context */
1432 PHPDBG_G(exec) = phpdbg_resolve_path(exec);
1433 PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
1434
1435 free(exec);
1436 exec = NULL;
1437 }
1438
1439 php_output_activate();
1440 php_output_deactivate();
1441
1442 if (SG(sapi_headers).mimetype) {
1443 efree(SG(sapi_headers).mimetype);
1444 SG(sapi_headers).mimetype = NULL;
1445 }
1446
1447 php_output_activate();
1448
1449 {
1450 int i;
1451
1452 SG(request_info).argc = argc - php_optind + 1;
1453 SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1454 for (i = SG(request_info).argc; --i;) {
1455 SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1456 }
1457 SG(request_info).argv[0] = PHPDBG_G(exec) ? estrdup(PHPDBG_G(exec)) : estrdup("");
1458 }
1459
1460 if (php_request_startup() == FAILURE) {
1461 PUTS("Could not startup");
1462 return 1;
1463 }
1464
1465 #ifndef _WIN32
1466 #ifdef HAVE_USERFAULTFD_WRITEFAULT
1467 if (!PHPDBG_G(watch_userfaultfd))
1468 #endif
1469 {
1470 zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1471 zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1472 }
1473 #endif
1474 zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
1475
1476
1477 PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1478 PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1479 PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
1480
1481 #ifndef _WIN32
1482 PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
1483 php_stream_stdio_ops.write = phpdbg_stdiop_write;
1484 #endif
1485
1486 {
1487 zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1488 php_stream_wrapper *tmp_wrapper = Z_PTR_P(zv);
1489 PHPDBG_G(orig_url_wrap_php) = tmp_wrapper;
1490 memcpy(&wrapper, tmp_wrapper, sizeof(wrapper));
1491 memcpy(&wops, tmp_wrapper->wops, sizeof(wops));
1492 wops.stream_opener = phpdbg_stream_url_wrap_php;
1493 wrapper.wops = (const php_stream_wrapper_ops*)&wops;
1494 Z_PTR_P(zv) = &wrapper;
1495 }
1496
1497 /* Make stdin, stdout and stderr accessible from PHP scripts */
1498 phpdbg_register_file_handles();
1499
1500 phpdbg_list_update();
1501
1502 if (show_banner && cleaning < 2) {
1503 /* print blurb */
1504 phpdbg_welcome(cleaning == 1);
1505 }
1506
1507 cleaning = -1;
1508
1509 if (ext_stmt) {
1510 CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1511 }
1512
1513 /* initialize from file */
1514 PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1515 zend_try {
1516 phpdbg_init(init_file, init_file_len, init_file_default);
1517 } zend_end_try();
1518 PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1519
1520 /* quit if init says so */
1521 if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1522 goto phpdbg_out;
1523 }
1524
1525 /* auto compile */
1526 if (read_from_stdin) {
1527 if (!read_from_stdin[0]) {
1528 if (!quit_immediately) {
1529 phpdbg_error("Impossible to not specify a stdin delimiter without -rr");
1530 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1531 goto phpdbg_out;
1532 }
1533 }
1534 if (show_banner || read_from_stdin[0]) {
1535 phpdbg_notice("Reading input from stdin; put '%s' followed by a newline on an own line after code to end input", read_from_stdin);
1536 }
1537
1538 if (phpdbg_startup_run > 0) {
1539 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1540 }
1541
1542 zend_try {
1543 phpdbg_param_t cmd;
1544 cmd.str = read_from_stdin;
1545 cmd.len = strlen(read_from_stdin);
1546 PHPDBG_COMMAND_HANDLER(stdin)(&cmd);
1547 } zend_end_try();
1548
1549 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1550 } else if (PHPDBG_G(exec)) {
1551 if (settings || phpdbg_startup_run > 0) {
1552 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1553 }
1554
1555 zend_try {
1556 if (backup_phpdbg_compile) {
1557 phpdbg_compile_stdin(backup_phpdbg_compile);
1558 } else {
1559 phpdbg_compile();
1560 }
1561 } zend_end_try();
1562 backup_phpdbg_compile = NULL;
1563
1564 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1565 }
1566
1567 if (bp_tmp) {
1568 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT | PHPDBG_IS_INITIALIZING;
1569 phpdbg_string_init(bp_tmp);
1570 free(bp_tmp);
1571 bp_tmp = NULL;
1572 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT & ~PHPDBG_IS_INITIALIZING;
1573 }
1574
1575 if (settings == (void *) 0x1) {
1576 if (PHPDBG_G(ops)) {
1577 phpdbg_print_opcodes(print_opline_func);
1578 } else {
1579 zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("No opcodes could be compiled | No file specified or compilation failed?\n"));
1580 }
1581 goto phpdbg_out;
1582 }
1583
1584 PG(during_request_startup) = 0;
1585
1586 phpdbg_fully_started = 1;
1587
1588 /* phpdbg main() */
1589 do {
1590 zend_try {
1591 if (phpdbg_startup_run) {
1592 phpdbg_startup_run = 0;
1593 if (quit_immediately) {
1594 PHPDBG_G(flags) = (PHPDBG_G(flags) & ~PHPDBG_HAS_PAGINATION) | PHPDBG_IS_INTERACTIVE | PHPDBG_PREVENT_INTERACTIVE;
1595 } else {
1596 PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
1597 }
1598 zend_try {
1599 if (first_command) {
1600 phpdbg_interactive(1, estrdup(first_command));
1601 } else {
1602 PHPDBG_COMMAND_HANDLER(run)(NULL);
1603 }
1604 } zend_end_try();
1605 if (quit_immediately) {
1606 /* if -r is on the command line more than once just quit */
1607 EG(bailout) = __orig_bailout; /* reset zend_try */
1608 exit_status = EG(exit_status);
1609 break;
1610 }
1611 }
1612
1613 CG(unclean_shutdown) = 0;
1614 phpdbg_interactive(1, NULL);
1615 } zend_catch {
1616 if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1617 char *bp_tmp_str;
1618 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1619 phpdbg_export_breakpoints_to_string(&bp_tmp_str);
1620 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1621 if (bp_tmp_str) {
1622 bp_tmp = strdup(bp_tmp_str);
1623 free(bp_tmp_str);
1624 }
1625 cleaning = 1;
1626 } else {
1627 cleaning = 0;
1628 }
1629 } zend_end_try();
1630 } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
1631
1632 #ifdef _WIN32
1633 } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
1634 phpdbg_error("Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
1635 }
1636 #endif
1637 phpdbg_out:
1638
1639 phpdbg_purge_watchpoint_tree();
1640
1641 if (first_command) {
1642 free(first_command);
1643 first_command = NULL;
1644 }
1645
1646 if (cleaning <= 0) {
1647 PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
1648 cleaning = -1;
1649 }
1650
1651 {
1652 int i;
1653 /* free argv */
1654 for (i = SG(request_info).argc; i--;) {
1655 efree(SG(request_info).argv[i]);
1656 }
1657 efree(SG(request_info).argv);
1658 }
1659
1660 php_ini_builder_deinit(&ini_builder);
1661
1662 if (ini_override) {
1663 free(ini_override);
1664 }
1665
1666 /* In case we aborted during script execution, we may not reset CG(unclean_shutdown) */
1667 if (!(PHPDBG_G(flags) & PHPDBG_IS_RUNNING)) {
1668 is_exit = !PHPDBG_G(in_execution);
1669 CG(unclean_shutdown) = is_exit || PHPDBG_G(unclean_eval);
1670 }
1671
1672 if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
1673 php_free_shutdown_functions();
1674 zend_objects_store_mark_destructed(&EG(objects_store));
1675 }
1676
1677 if (PHPDBG_G(exec) && strcmp("Standard input code", PHPDBG_G(exec)) == SUCCESS) { /* i.e. execution context has been read from stdin - back it up */
1678 phpdbg_file_source *data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), PHPDBG_G(exec), PHPDBG_G(exec_len));
1679 backup_phpdbg_compile = zend_string_alloc(data->len + 2, 1);
1680 GC_MAKE_PERSISTENT_LOCAL(backup_phpdbg_compile);
1681 sprintf(ZSTR_VAL(backup_phpdbg_compile), "?>%.*s", (int) data->len, data->buf);
1682 }
1683
1684 zend_try {
1685 php_request_shutdown(NULL);
1686 } zend_end_try();
1687
1688 /* backup globals when cleaning */
1689 if ((cleaning > 0) && !quit_immediately) {
1690 settings = calloc(1, sizeof(zend_phpdbg_globals));
1691
1692 php_phpdbg_globals_ctor(settings);
1693
1694 if (PHPDBG_G(exec)) {
1695 settings->exec = zend_strndup(PHPDBG_G(exec), PHPDBG_G(exec_len));
1696 settings->exec_len = PHPDBG_G(exec_len);
1697 }
1698 settings->prompt[0] = PHPDBG_G(prompt)[0];
1699 settings->prompt[1] = PHPDBG_G(prompt)[1];
1700 memcpy(ZEND_VOIDP(settings->colors), PHPDBG_G(colors), sizeof(settings->colors));
1701 settings->input_buflen = PHPDBG_G(input_buflen);
1702 memcpy(settings->input_buffer, PHPDBG_G(input_buffer), settings->input_buflen);
1703 settings->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK;
1704 first_command = PHPDBG_G(cur_command);
1705 } else {
1706 if (PHPDBG_G(prompt)[0]) {
1707 free(PHPDBG_G(prompt)[0]);
1708 }
1709 if (PHPDBG_G(prompt)[1]) {
1710 free(PHPDBG_G(prompt)[1]);
1711 }
1712 if (PHPDBG_G(cur_command)) {
1713 free(PHPDBG_G(cur_command));
1714 }
1715 }
1716
1717 if (exit_status == 0) {
1718 exit_status = EG(exit_status);
1719 }
1720
1721 php_output_deactivate();
1722
1723 if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
1724 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1725 if (PHPDBG_G(in_execution) || is_exit) {
1726 if (!quit_immediately && !phpdbg_startup_run) {
1727 PHPDBG_G(flags) -= PHPDBG_IS_QUITTING;
1728 cleaning++;
1729 }
1730 }
1731 }
1732
1733 {
1734 zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1735 Z_PTR_P(zv) = (void*)PHPDBG_G(orig_url_wrap_php);
1736 }
1737
1738 #ifndef _WIN32
1739 /* force override (no zend_signals) to prevent crashes due to signal recursion in SIGSEGV/SIGBUS handlers */
1740 signal(SIGSEGV, SIG_DFL);
1741 signal(SIGBUS, SIG_DFL);
1742
1743 /* reset it... else we risk a stack overflow upon next run (when clean'ing) */
1744 php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
1745 #endif
1746 }
1747
1748 php_module_shutdown();
1749
1750 sapi_shutdown();
1751
1752 if (sapi_name) {
1753 free(sapi_name);
1754 }
1755
1756 free_and_return:
1757 if (read_from_stdin) {
1758 free(read_from_stdin);
1759 read_from_stdin = NULL;
1760 }
1761
1762 #ifdef ZTS
1763 /* reset to original handlers - otherwise PHPDBG_G() in phpdbg_watch_efree will be segfaulty (with e.g. USE_ZEND_ALLOC=0) */
1764 if (!use_mm_wrappers) {
1765 zend_mm_set_custom_handlers(zend_mm_get_heap(), _malloc, _free, _realloc);
1766 }
1767
1768 ts_free_id(phpdbg_globals_id);
1769
1770 tsrm_shutdown();
1771 #endif
1772
1773 if ((cleaning > 0) && !quit_immediately) {
1774 /* reset internal php_getopt state */
1775 php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0);
1776
1777 goto phpdbg_main;
1778 }
1779
1780 if (backup_phpdbg_compile) {
1781 zend_string_free(backup_phpdbg_compile);
1782 }
1783
1784 /* usually 0; just for -rr */
1785 return exit_status;
1786 } /* }}} */
1787