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