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