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"), 1);
233 zend_string *value = zend_string_init(ZEND_STRL("off"), 1);
234
235 zend_alter_ini_entry(key, value, ZEND_INI_SYSTEM, ZEND_INI_STAGE_STARTUP);
236
237 zend_string_release(key);
238 zend_string_release(value);
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(uint8_t opcode)432 static zend_always_inline bool phpdbg_is_ignored_opcode(uint8_t 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 || opcode == ZEND_BIND_INIT_STATIC_OR_JMP
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 uint8_t 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_script(P_STDOUT, "%.*s", (int) length, message);
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 Z_CONSTANT_FLAGS(ic.value) = 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 Z_CONSTANT_FLAGS(oc.value) = 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 Z_CONSTANT_FLAGS(ec.value) = 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 static 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 ZEND_NORETURN 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 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
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 ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
1150 void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
1151 void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
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 zend_mm_set_custom_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1416 } else {
1417 zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
1418 }
1419
1420 _free = PHPDBG_G(original_free_function);
1421
1422
1423 phpdbg_init_list();
1424
1425 PHPDBG_G(sapi_name_ptr) = sapi_name;
1426
1427 if (exec) { /* set execution context */
1428 PHPDBG_G(exec) = phpdbg_resolve_path(exec);
1429 PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
1430
1431 free(exec);
1432 exec = NULL;
1433 }
1434
1435 php_output_activate();
1436 php_output_deactivate();
1437
1438 if (SG(sapi_headers).mimetype) {
1439 efree(SG(sapi_headers).mimetype);
1440 SG(sapi_headers).mimetype = NULL;
1441 }
1442
1443 php_output_activate();
1444
1445 {
1446 int i;
1447
1448 SG(request_info).argc = argc - php_optind + 1;
1449 SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1450 for (i = SG(request_info).argc; --i;) {
1451 SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1452 }
1453 SG(request_info).argv[0] = PHPDBG_G(exec) ? estrdup(PHPDBG_G(exec)) : estrdup("");
1454 }
1455
1456 if (php_request_startup() == FAILURE) {
1457 PUTS("Could not startup");
1458 return 1;
1459 }
1460
1461 #ifndef _WIN32
1462 #ifdef HAVE_USERFAULTFD_WRITEFAULT
1463 if (!PHPDBG_G(watch_userfaultfd))
1464 #endif
1465 {
1466 zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1467 zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1468 }
1469 #endif
1470 zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
1471
1472
1473 PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1474 PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1475 PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
1476
1477 #ifndef _WIN32
1478 PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
1479 php_stream_stdio_ops.write = phpdbg_stdiop_write;
1480 #endif
1481
1482 {
1483 zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1484 php_stream_wrapper *tmp_wrapper = Z_PTR_P(zv);
1485 PHPDBG_G(orig_url_wrap_php) = tmp_wrapper;
1486 memcpy(&wrapper, tmp_wrapper, sizeof(wrapper));
1487 memcpy(&wops, tmp_wrapper->wops, sizeof(wops));
1488 wops.stream_opener = phpdbg_stream_url_wrap_php;
1489 wrapper.wops = (const php_stream_wrapper_ops*)&wops;
1490 Z_PTR_P(zv) = &wrapper;
1491 }
1492
1493 /* Make stdin, stdout and stderr accessible from PHP scripts */
1494 phpdbg_register_file_handles();
1495
1496 phpdbg_list_update();
1497
1498 if (show_banner && cleaning < 2) {
1499 /* print blurb */
1500 phpdbg_welcome(cleaning == 1);
1501 }
1502
1503 cleaning = -1;
1504
1505 if (ext_stmt) {
1506 CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1507 }
1508
1509 /* initialize from file */
1510 PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1511 zend_try {
1512 phpdbg_init(init_file, init_file_len, init_file_default);
1513 } zend_end_try();
1514 PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1515
1516 /* quit if init says so */
1517 if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1518 goto phpdbg_out;
1519 }
1520
1521 /* auto compile */
1522 if (read_from_stdin) {
1523 if (!read_from_stdin[0]) {
1524 if (!quit_immediately) {
1525 phpdbg_error("Impossible to not specify a stdin delimiter without -rr");
1526 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1527 goto phpdbg_out;
1528 }
1529 }
1530 if (show_banner || read_from_stdin[0]) {
1531 phpdbg_notice("Reading input from stdin; put '%s' followed by a newline on an own line after code to end input", read_from_stdin);
1532 }
1533
1534 if (phpdbg_startup_run > 0) {
1535 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1536 }
1537
1538 zend_try {
1539 phpdbg_param_t cmd;
1540 cmd.str = read_from_stdin;
1541 cmd.len = strlen(read_from_stdin);
1542 PHPDBG_COMMAND_HANDLER(stdin)(&cmd);
1543 } zend_end_try();
1544
1545 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1546 } else if (PHPDBG_G(exec)) {
1547 if (settings || phpdbg_startup_run > 0) {
1548 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1549 }
1550
1551 zend_try {
1552 if (backup_phpdbg_compile) {
1553 phpdbg_compile_stdin(backup_phpdbg_compile);
1554 } else {
1555 phpdbg_compile();
1556 }
1557 } zend_end_try();
1558 backup_phpdbg_compile = NULL;
1559
1560 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1561 }
1562
1563 if (bp_tmp) {
1564 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT | PHPDBG_IS_INITIALIZING;
1565 phpdbg_string_init(bp_tmp);
1566 free(bp_tmp);
1567 bp_tmp = NULL;
1568 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT & ~PHPDBG_IS_INITIALIZING;
1569 }
1570
1571 if (settings == (void *) 0x1) {
1572 if (PHPDBG_G(ops)) {
1573 phpdbg_print_opcodes(print_opline_func);
1574 } else {
1575 zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("No opcodes could be compiled | No file specified or compilation failed?\n"));
1576 }
1577 goto phpdbg_out;
1578 }
1579
1580 PG(during_request_startup) = 0;
1581
1582 phpdbg_fully_started = 1;
1583
1584 /* phpdbg main() */
1585 do {
1586 zend_try {
1587 if (phpdbg_startup_run) {
1588 phpdbg_startup_run = 0;
1589 if (quit_immediately) {
1590 PHPDBG_G(flags) = (PHPDBG_G(flags) & ~PHPDBG_HAS_PAGINATION) | PHPDBG_IS_INTERACTIVE | PHPDBG_PREVENT_INTERACTIVE;
1591 } else {
1592 PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
1593 }
1594 zend_try {
1595 if (first_command) {
1596 phpdbg_interactive(1, estrdup(first_command));
1597 } else {
1598 PHPDBG_COMMAND_HANDLER(run)(NULL);
1599 }
1600 } zend_end_try();
1601 if (quit_immediately) {
1602 /* if -r is on the command line more than once just quit */
1603 EG(bailout) = __orig_bailout; /* reset zend_try */
1604 exit_status = EG(exit_status);
1605 break;
1606 }
1607 }
1608
1609 CG(unclean_shutdown) = 0;
1610 phpdbg_interactive(1, NULL);
1611 } zend_catch {
1612 if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1613 char *bp_tmp_str;
1614 PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1615 phpdbg_export_breakpoints_to_string(&bp_tmp_str);
1616 PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1617 if (bp_tmp_str) {
1618 bp_tmp = strdup(bp_tmp_str);
1619 free(bp_tmp_str);
1620 }
1621 cleaning = 1;
1622 } else {
1623 cleaning = 0;
1624 }
1625 } zend_end_try();
1626 } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
1627
1628 #ifdef _WIN32
1629 } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
1630 phpdbg_error("Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
1631 }
1632 #endif
1633 phpdbg_out:
1634
1635 phpdbg_purge_watchpoint_tree();
1636
1637 if (first_command) {
1638 free(first_command);
1639 first_command = NULL;
1640 }
1641
1642 if (cleaning <= 0) {
1643 PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
1644 cleaning = -1;
1645 }
1646
1647 {
1648 int i;
1649 /* free argv */
1650 for (i = SG(request_info).argc; i--;) {
1651 efree(SG(request_info).argv[i]);
1652 }
1653 efree(SG(request_info).argv);
1654 }
1655
1656 php_ini_builder_deinit(&ini_builder);
1657
1658 if (ini_override) {
1659 free(ini_override);
1660 }
1661
1662 /* In case we aborted during script execution, we may not reset CG(unclean_shutdown) */
1663 if (!(PHPDBG_G(flags) & PHPDBG_IS_RUNNING)) {
1664 is_exit = !PHPDBG_G(in_execution);
1665 CG(unclean_shutdown) = is_exit || PHPDBG_G(unclean_eval);
1666 }
1667
1668 if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
1669 php_free_shutdown_functions();
1670 zend_objects_store_mark_destructed(&EG(objects_store));
1671 }
1672
1673 if (PHPDBG_G(exec) && strcmp("Standard input code", PHPDBG_G(exec)) == SUCCESS) { /* i.e. execution context has been read from stdin - back it up */
1674 phpdbg_file_source *data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), PHPDBG_G(exec), PHPDBG_G(exec_len));
1675 backup_phpdbg_compile = zend_string_alloc(data->len + 2, 1);
1676 GC_MAKE_PERSISTENT_LOCAL(backup_phpdbg_compile);
1677 sprintf(ZSTR_VAL(backup_phpdbg_compile), "?>%.*s", (int) data->len, data->buf);
1678 }
1679
1680 zend_try {
1681 php_request_shutdown(NULL);
1682 } zend_end_try();
1683
1684 /* backup globals when cleaning */
1685 if ((cleaning > 0) && !quit_immediately) {
1686 settings = calloc(1, sizeof(zend_phpdbg_globals));
1687
1688 php_phpdbg_globals_ctor(settings);
1689
1690 if (PHPDBG_G(exec)) {
1691 settings->exec = zend_strndup(PHPDBG_G(exec), PHPDBG_G(exec_len));
1692 settings->exec_len = PHPDBG_G(exec_len);
1693 }
1694 settings->prompt[0] = PHPDBG_G(prompt)[0];
1695 settings->prompt[1] = PHPDBG_G(prompt)[1];
1696 memcpy(ZEND_VOIDP(settings->colors), PHPDBG_G(colors), sizeof(settings->colors));
1697 settings->input_buflen = PHPDBG_G(input_buflen);
1698 memcpy(settings->input_buffer, PHPDBG_G(input_buffer), settings->input_buflen);
1699 settings->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK;
1700 first_command = PHPDBG_G(cur_command);
1701 } else {
1702 if (PHPDBG_G(prompt)[0]) {
1703 free(PHPDBG_G(prompt)[0]);
1704 }
1705 if (PHPDBG_G(prompt)[1]) {
1706 free(PHPDBG_G(prompt)[1]);
1707 }
1708 if (PHPDBG_G(cur_command)) {
1709 free(PHPDBG_G(cur_command));
1710 }
1711 }
1712
1713 if (exit_status == 0) {
1714 exit_status = EG(exit_status);
1715 }
1716
1717 php_output_deactivate();
1718
1719 if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
1720 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1721 if (PHPDBG_G(in_execution) || is_exit) {
1722 if (!quit_immediately && !phpdbg_startup_run) {
1723 PHPDBG_G(flags) -= PHPDBG_IS_QUITTING;
1724 cleaning++;
1725 }
1726 }
1727 }
1728
1729 {
1730 zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1731 Z_PTR_P(zv) = (void*)PHPDBG_G(orig_url_wrap_php);
1732 }
1733
1734 #ifndef _WIN32
1735 /* force override (no zend_signals) to prevent crashes due to signal recursion in SIGSEGV/SIGBUS handlers */
1736 signal(SIGSEGV, SIG_DFL);
1737 signal(SIGBUS, SIG_DFL);
1738
1739 /* reset it... else we risk a stack overflow upon next run (when clean'ing) */
1740 php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
1741 #endif
1742 }
1743
1744 php_module_shutdown();
1745
1746 sapi_shutdown();
1747
1748 if (sapi_name) {
1749 free(sapi_name);
1750 }
1751
1752 free_and_return:
1753 if (read_from_stdin) {
1754 free(read_from_stdin);
1755 read_from_stdin = NULL;
1756 }
1757
1758 #ifdef ZTS
1759 /* reset to original handlers - otherwise PHPDBG_G() in phpdbg_watch_efree will be segfaulty (with e.g. USE_ZEND_ALLOC=0) */
1760 if (!use_mm_wrappers) {
1761 zend_mm_set_custom_handlers(zend_mm_get_heap(), _malloc, _free, _realloc);
1762 }
1763
1764 ts_free_id(phpdbg_globals_id);
1765
1766 tsrm_shutdown();
1767 #endif
1768
1769 if ((cleaning > 0) && !quit_immediately) {
1770 /* reset internal php_getopt state */
1771 php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0);
1772
1773 goto phpdbg_main;
1774 }
1775
1776 if (backup_phpdbg_compile) {
1777 zend_string_free(backup_phpdbg_compile);
1778 }
1779
1780 /* usually 0; just for -rr */
1781 return exit_status;
1782 } /* }}} */
1783