xref: /PHP-8.1/sapi/phpdbg/phpdbg.c (revision 8ff2b6ab)
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