xref: /PHP-7.0/sapi/phpdbg/phpdbg.c (revision 0db20a7c)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2017 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Felipe Pena <felipe@php.net>                                |
16    | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
17    | Authors: Bob Weinand <bwoebi@php.net>                                |
18    +----------------------------------------------------------------------+
19 */
20 
21 #if !defined(ZEND_SIGNALS) || defined(_WIN32)
22 # include <signal.h>
23 #endif
24 
25 #include "phpdbg.h"
26 #include "phpdbg_prompt.h"
27 #include "phpdbg_bp.h"
28 #include "phpdbg_break.h"
29 #include "phpdbg_list.h"
30 #include "phpdbg_utils.h"
31 #include "phpdbg_set.h"
32 #include "phpdbg_io.h"
33 #include "zend_alloc.h"
34 #include "phpdbg_eol.h"
35 #include "phpdbg_print.h"
36 #include "phpdbg_help.h"
37 
38 #include "ext/standard/basic_functions.h"
39 
40 /* {{{ remote console headers */
41 #ifndef _WIN32
42 #	include <sys/socket.h>
43 #	include <sys/select.h>
44 #	include <sys/time.h>
45 #	include <sys/types.h>
46 #	if HAVE_POLL_H
47 #		include <poll.h>
48 #	elif HAVE_SYS_POLL_H
49 #		include <sys/poll.h>
50 #	endif
51 #	include <netinet/in.h>
52 #	include <unistd.h>
53 #	include <arpa/inet.h>
54 #endif /* }}} */
55 
56 #if defined(PHP_WIN32) && defined(HAVE_OPENSSL)
57 # include "openssl/applink.c"
58 #endif
59 
60 #if defined(PHP_WIN32) && defined(ZTS)
61 ZEND_TSRMLS_CACHE_DEFINE()
62 #endif
63 
64 ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
65 int phpdbg_startup_run = 0;
66 
PHP_INI_MH(OnUpdateEol)67 static PHP_INI_MH(OnUpdateEol)
68 {
69 	if (!new_value) {
70 		return FAILURE;
71 	}
72 
73 	return phpdbg_eol_global_update(ZSTR_VAL(new_value));
74 }
75 
76 PHP_INI_BEGIN()
77 	STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, socket_path, zend_phpdbg_globals, phpdbg_globals)
78 	STD_PHP_INI_ENTRY("phpdbg.eol", "2", PHP_INI_ALL, OnUpdateEol, socket_path, zend_phpdbg_globals, phpdbg_globals)
79 PHP_INI_END()
80 
81 static zend_bool phpdbg_booted = 0;
82 static zend_bool phpdbg_fully_started = 0;
83 
php_phpdbg_globals_ctor(zend_phpdbg_globals * pg)84 static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
85 {
86 	pg->prompt[0] = NULL;
87 	pg->prompt[1] = NULL;
88 
89 	pg->colors[0] = NULL;
90 	pg->colors[1] = NULL;
91 	pg->colors[2] = NULL;
92 
93 	pg->lines = phpdbg_get_terminal_height();
94 	pg->exec = NULL;
95 	pg->exec_len = 0;
96 	pg->buffer = NULL;
97 	pg->last_was_newline = 1;
98 	pg->ops = NULL;
99 	pg->vmret = 0;
100 	pg->in_execution = 0;
101 	pg->bp_count = 0;
102 	pg->flags = PHPDBG_DEFAULT_FLAGS;
103 	pg->oplog = NULL;
104 	memset(pg->io, 0, sizeof(pg->io));
105 	pg->frame.num = 0;
106 	pg->sapi_name_ptr = NULL;
107 	pg->socket_fd = -1;
108 	pg->socket_server_fd = -1;
109 	pg->unclean_eval = 0;
110 
111 	pg->req_id = 0;
112 	pg->err_buf.active = 0;
113 	pg->err_buf.type = 0;
114 
115 	pg->input_buflen = 0;
116 	pg->sigsafe_mem.mem = NULL;
117 	pg->sigsegv_bailout = NULL;
118 
119 	pg->oplog_list = NULL;
120 
121 #ifdef PHP_WIN32
122 	pg->sigio_watcher_thread = INVALID_HANDLE_VALUE;
123 	memset(&pg->swd, 0, sizeof(struct win32_sigio_watcher_data));
124 #endif
125 
126 	pg->eol = PHPDBG_EOL_LF;
127 
128 	pg->stdin_file = NULL;
129 
130 	pg->cur_command = NULL;
131 } /* }}} */
132 
PHP_MINIT_FUNCTION(phpdbg)133 static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
134 {
135 	ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
136 	REGISTER_INI_ENTRIES();
137 
138 	zend_execute_ex = phpdbg_execute_ex;
139 
140 	REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
141 
142 	REGISTER_LONG_CONSTANT("PHPDBG_FILE",   FILE_PARAM, CONST_CS|CONST_PERSISTENT);
143 	REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT);
144 	REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT);
145 	REGISTER_LONG_CONSTANT("PHPDBG_FUNC",   STR_PARAM, CONST_CS|CONST_PERSISTENT);
146 
147 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
148 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
149 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR",  PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
150 
151 	return SUCCESS;
152 } /* }}} */
153 
php_phpdbg_destroy_bp_file(zval * brake)154 static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */
155 {
156 	zend_hash_destroy(Z_ARRVAL_P(brake));
157 	efree(Z_ARRVAL_P(brake));
158 } /* }}} */
159 
php_phpdbg_destroy_bp_symbol(zval * brake)160 static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
161 {
162 	efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
163 	efree(Z_PTR_P(brake));
164 } /* }}} */
165 
php_phpdbg_destroy_bp_opcode(zval * brake)166 static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
167 {
168 	efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
169 	efree(Z_PTR_P(brake));
170 } /* }}} */
171 
php_phpdbg_destroy_bp_opline(zval * brake)172 static void php_phpdbg_destroy_bp_opline(zval *brake) /* {{{ */
173 {
174 	efree(Z_PTR_P(brake));
175 } /* }}} */
176 
php_phpdbg_destroy_bp_methods(zval * brake)177 static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
178 {
179 	zend_hash_destroy(Z_ARRVAL_P(brake));
180 	efree(Z_ARRVAL_P(brake));
181 } /* }}} */
182 
php_phpdbg_destroy_bp_condition(zval * data)183 static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
184 {
185 	phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
186 
187 	if (brake->ops) {
188 		destroy_op_array(brake->ops);
189 		efree(brake->ops);
190 	}
191 	efree((char*) brake->code);
192 	efree(brake);
193 } /* }}} */
194 
php_phpdbg_destroy_registered(zval * data)195 static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
196 {
197 	zend_function *function = (zend_function *) Z_PTR_P(data);
198 	destroy_zend_function(function);
199 } /* }}} */
200 
201 
PHP_RINIT_FUNCTION(phpdbg)202 static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
203 {
204 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
205 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
206 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
207 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
208 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
209 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
210 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, php_phpdbg_destroy_bp_opline, 0);
211 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
212 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
213 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
214 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
215 
216 	zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
217 	zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
218 
219 	return SUCCESS;
220 } /* }}} */
221 
PHP_RSHUTDOWN_FUNCTION(phpdbg)222 static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
223 {
224 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
225 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
226 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
227 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
228 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
229 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
230 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
231 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
232 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
233 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
234 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
235 	zend_hash_destroy(&PHPDBG_G(file_sources));
236 	zend_hash_destroy(&PHPDBG_G(seek));
237 	zend_hash_destroy(&PHPDBG_G(registered));
238 	zend_hash_destroy(&PHPDBG_G(watchpoints));
239 	zend_llist_destroy(&PHPDBG_G(watchlist_mem));
240 
241 	if (PHPDBG_G(buffer)) {
242 		free(PHPDBG_G(buffer));
243 		PHPDBG_G(buffer) = NULL;
244 	}
245 
246 	if (PHPDBG_G(exec)) {
247 		efree(PHPDBG_G(exec));
248 		PHPDBG_G(exec) = NULL;
249 	}
250 
251 	if (PHPDBG_G(oplog)) {
252 		fclose(PHPDBG_G(oplog));
253 		PHPDBG_G(oplog) = NULL;
254 	}
255 
256 	if (PHPDBG_G(ops)) {
257 		destroy_op_array(PHPDBG_G(ops));
258 		efree(PHPDBG_G(ops));
259 		PHPDBG_G(ops) = NULL;
260 	}
261 
262 	if (PHPDBG_G(oplog_list)) {
263 		phpdbg_oplog_list *cur = PHPDBG_G(oplog_list);
264 		do {
265 			phpdbg_oplog_list *prev = cur->prev;
266 			efree(cur);
267 			cur = prev;
268 		} while (cur != NULL);
269 
270 		zend_arena_destroy(PHPDBG_G(oplog_arena));
271 		PHPDBG_G(oplog_list) = NULL;
272 	}
273 
274 	if (PHPDBG_G(stdin_file)) {
275 		fclose(PHPDBG_G(stdin_file));
276 		PHPDBG_G(stdin_file) = NULL;
277 	}
278 
279 	return SUCCESS;
280 } /* }}} */
281 
282 /* {{{ proto mixed phpdbg_exec(string context)
283 	Attempt to set the execution context for phpdbg
284 	If the execution context was set previously it is returned
285 	If the execution context was not set previously boolean true is returned
286 	If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
PHP_FUNCTION(phpdbg_exec)287 static PHP_FUNCTION(phpdbg_exec)
288 {
289 	zend_string *exec;
290 
291 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &exec) == FAILURE) {
292 		return;
293 	}
294 
295 	{
296 		zend_stat_t sb;
297 		zend_bool result = 1;
298 
299 		if (VCWD_STAT(ZSTR_VAL(exec), &sb) != FAILURE) {
300 			if (sb.st_mode & (S_IFREG|S_IFLNK)) {
301 				if (PHPDBG_G(exec)) {
302 					ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len));
303 					efree(PHPDBG_G(exec));
304 					result = 0;
305 				}
306 
307 				PHPDBG_G(exec) = estrndup(ZSTR_VAL(exec), ZSTR_LEN(exec));
308 				PHPDBG_G(exec_len) = ZSTR_LEN(exec);
309 
310 				if (result) {
311 					ZVAL_TRUE(return_value);
312 				}
313 			} else {
314 				zend_error(E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", ZSTR_VAL(exec));
315 				ZVAL_FALSE(return_value);
316 			}
317 		} else {
318 			zend_error(E_WARNING, "Failed to set execution context (%s) the file does not exist", ZSTR_VAL(exec));
319 
320 			ZVAL_FALSE(return_value);
321 		}
322 	}
323 } /* }}} */
324 
325 /* {{{ proto void phpdbg_break()
326     instructs phpdbg to insert a breakpoint at the next opcode */
PHP_FUNCTION(phpdbg_break_next)327 static PHP_FUNCTION(phpdbg_break_next)
328 {
329 	zend_execute_data *ex = EG(current_execute_data);
330 
331 	while (ex && ex->func && !ZEND_USER_CODE(ex->func->type)) {
332 		ex = ex->prev_execute_data;
333 	}
334 
335 	if (zend_parse_parameters_none() == FAILURE || !ex) {
336 		return;
337 	}
338 
339 	phpdbg_set_breakpoint_opline_ex((phpdbg_opline_ptr_t) ex->opline + 1);
340 } /* }}} */
341 
342 /* {{{ proto void phpdbg_break_file(string file, integer line) */
PHP_FUNCTION(phpdbg_break_file)343 static PHP_FUNCTION(phpdbg_break_file)
344 {
345 	char *file;
346 	size_t flen;
347 	zend_long line;
348 
349 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &file, &flen, &line) == FAILURE) {
350 		return;
351 	}
352 
353 	phpdbg_set_breakpoint_file(file, line);
354 } /* }}} */
355 
356 /* {{{ proto void phpdbg_break_method(string class, string method) */
PHP_FUNCTION(phpdbg_break_method)357 static PHP_FUNCTION(phpdbg_break_method)
358 {
359 	char *class = NULL, *method = NULL;
360 	size_t clen = 0, mlen = 0;
361 
362 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &class, &clen, &method, &mlen) == FAILURE) {
363 		return;
364 	}
365 
366 	phpdbg_set_breakpoint_method(class, method);
367 } /* }}} */
368 
369 /* {{{ proto void phpdbg_break_function(string function) */
PHP_FUNCTION(phpdbg_break_function)370 static PHP_FUNCTION(phpdbg_break_function)
371 {
372 	char    *function = NULL;
373 	size_t   function_len;
374 
375 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &function, &function_len) == FAILURE) {
376 		return;
377 	}
378 
379 	phpdbg_set_breakpoint_symbol(function, function_len);
380 } /* }}} */
381 
382 /* {{{ proto void phpdbg_clear(void)
383    instructs phpdbg to clear breakpoints */
PHP_FUNCTION(phpdbg_clear)384 static PHP_FUNCTION(phpdbg_clear)
385 {
386 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
387 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
388 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
389 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
390 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
391 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
392 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
393 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
394 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
395 } /* }}} */
396 
397 /* {{{ proto void phpdbg_color(integer element, string color) */
PHP_FUNCTION(phpdbg_color)398 static PHP_FUNCTION(phpdbg_color)
399 {
400 	zend_long element;
401 	char *color;
402 	size_t color_len;
403 
404 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &element, &color, &color_len) == FAILURE) {
405 		return;
406 	}
407 
408 	switch (element) {
409 		case PHPDBG_COLOR_NOTICE:
410 		case PHPDBG_COLOR_ERROR:
411 		case PHPDBG_COLOR_PROMPT:
412 			phpdbg_set_color_ex(element, color, color_len);
413 		break;
414 
415 		default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant");
416 	}
417 } /* }}} */
418 
419 /* {{{ proto void phpdbg_prompt(string prompt) */
PHP_FUNCTION(phpdbg_prompt)420 static PHP_FUNCTION(phpdbg_prompt)
421 {
422 	char *prompt = NULL;
423 	size_t prompt_len = 0;
424 
425 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &prompt, &prompt_len) == FAILURE) {
426 		return;
427 	}
428 
429 	phpdbg_set_prompt(prompt);
430 } /* }}} */
431 
432 /* {{{ proto void phpdbg_start_oplog() */
PHP_FUNCTION(phpdbg_start_oplog)433 static PHP_FUNCTION(phpdbg_start_oplog)
434 {
435 	phpdbg_oplog_list *prev;
436 
437 	if (zend_parse_parameters_none() == FAILURE) {
438 		return;
439 	}
440 
441 	prev = PHPDBG_G(oplog_list);
442 
443 	if (!prev) {
444 		PHPDBG_G(oplog_arena) = zend_arena_create(64 * 1024);
445 
446 		PHPDBG_G(oplog_cur) = ((phpdbg_oplog_entry *) zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry))) + 1;
447 		PHPDBG_G(oplog_cur)->next = NULL;
448 	}
449 
450 	PHPDBG_G(oplog_list) = emalloc(sizeof(phpdbg_oplog_list));
451 	PHPDBG_G(oplog_list)->prev = prev;
452 	PHPDBG_G(oplog_list)->start = PHPDBG_G(oplog_cur);
453 }
454 
phpdbg_is_ignored_opcode(zend_uchar opcode)455 static zend_always_inline zend_bool phpdbg_is_ignored_opcode(zend_uchar opcode) {
456 	return
457 	    opcode == ZEND_NOP || opcode == ZEND_OP_DATA || opcode == ZEND_FE_FREE || opcode == ZEND_FREE || opcode == ZEND_ASSERT_CHECK || opcode == ZEND_VERIFY_RETURN_TYPE
458 	 || opcode == ZEND_DECLARE_CONST || opcode == ZEND_DECLARE_CLASS || opcode == ZEND_DECLARE_INHERITED_CLASS || opcode == ZEND_DECLARE_FUNCTION
459 	 || opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED || opcode == ZEND_VERIFY_ABSTRACT_CLASS || opcode == ZEND_ADD_TRAIT || opcode == ZEND_BIND_TRAITS
460 	 || opcode == ZEND_DECLARE_ANON_CLASS || opcode == ZEND_DECLARE_ANON_INHERITED_CLASS || opcode == ZEND_FAST_RET || opcode == ZEND_TICKS
461 	 || opcode == ZEND_EXT_STMT || opcode == ZEND_EXT_FCALL_BEGIN || opcode == ZEND_EXT_FCALL_END || opcode == ZEND_EXT_NOP || opcode == ZEND_BIND_GLOBAL
462 	;
463 }
464 
phpdbg_oplog_fill_executable(zend_op_array * op_array,HashTable * insert_ht,zend_bool by_opcode)465 static void phpdbg_oplog_fill_executable(zend_op_array *op_array, HashTable *insert_ht, zend_bool by_opcode) {
466 	/* ignore RECV_* opcodes */
467 	zend_op *cur = op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC);
468 	zend_op *end = op_array->opcodes + op_array->last;
469 
470 	zend_long insert_idx;
471 	zval zero;
472 	ZVAL_LONG(&zero, 0);
473 
474 	/* ignore autogenerated return (well, not too precise with finally branches, but that's okay) */
475 	if (op_array->last >= 1 && (((end - 1)->opcode == ZEND_RETURN || (end - 1)->opcode == ZEND_RETURN_BY_REF || (end - 1)->opcode == ZEND_GENERATOR_RETURN)
476 	 && ((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))
477 	  || op_array->function_name == NULL || (end - 1)->extended_value == -1))) {
478 		end--;
479 	}
480 
481 	for (; cur < end; cur++) {
482 		zend_uchar opcode = cur->opcode;
483 		if (phpdbg_is_ignored_opcode(opcode)) {
484 			continue;
485 		}
486 
487 		if (by_opcode) {
488 			insert_idx = cur - op_array->opcodes;
489 		} else {
490 			insert_idx = cur->lineno;
491 		}
492 
493 		if (opcode == ZEND_NEW && cur[1].opcode == ZEND_DO_FCALL) {
494 			cur++;
495 		}
496 
497 		zend_hash_index_update(insert_ht, insert_idx, &zero);
498 	}
499 }
500 
phpdbg_add_empty_array(HashTable * ht,zend_string * name)501 static inline HashTable* phpdbg_add_empty_array(HashTable *ht, zend_string *name) {
502 	zval *ht_zv = zend_hash_find(ht, name);
503 	if (!ht_zv) {
504 		zval zv;
505 		array_init(&zv);
506 		ht_zv = zend_hash_add_new(ht, name, &zv);
507 	}
508 	return Z_ARR_P(ht_zv);
509 }
510 
511 /* {{{ proto void phpdbg_get_executable() */
PHP_FUNCTION(phpdbg_get_executable)512 static PHP_FUNCTION(phpdbg_get_executable)
513 {
514 	HashTable *options = NULL;
515 	zval *option_buffer;
516 	zend_bool by_function = 0;
517 	zend_bool by_opcode = 0;
518 	HashTable *insert_ht;
519 
520 	zend_function *func;
521 	zend_class_entry *ce;
522 	zend_string *name;
523 	HashTable *files = &PHPDBG_G(file_sources);
524 	HashTable files_tmp;
525 
526 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
527 		return;
528 	}
529 
530 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
531 		by_function = zend_is_true(option_buffer);
532 	}
533 
534 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
535 		if (by_function) {
536 			by_opcode = zend_is_true(option_buffer);
537 		}
538 	}
539 
540 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("files")))) {
541 		ZVAL_DEREF(option_buffer);
542 		if (Z_TYPE_P(option_buffer) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(option_buffer)) > 0) {
543 			zval *filename;
544 
545 			files = &files_tmp;
546 			zend_hash_init(files, 0, NULL, NULL, 0);
547 
548 			ZEND_HASH_FOREACH_VAL(Z_ARR_P(option_buffer), filename) {
549 				zend_hash_add_empty_element(files, zval_get_string(filename));
550 			} ZEND_HASH_FOREACH_END();
551 		} else {
552 			GC_REFCOUNT(files)++;
553 		}
554 	} else {
555 		GC_REFCOUNT(files)++;
556 	}
557 
558 	array_init(return_value);
559 
560 	ZEND_HASH_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
561 		if (func->type == ZEND_USER_FUNCTION) {
562 			if (zend_hash_exists(files, func->op_array.filename)) {
563 				insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
564 
565 				if (by_function) {
566 					insert_ht = phpdbg_add_empty_array(insert_ht, name);
567 				}
568 
569 				phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
570 			}
571 		}
572 	} ZEND_HASH_FOREACH_END();
573 
574 	ZEND_HASH_FOREACH_STR_KEY_PTR(EG(class_table), name, ce) {
575 		if (ce->type == ZEND_USER_CLASS) {
576 			if (zend_hash_exists(files, ce->info.user.filename)) {
577 				ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
578 					if (func->type == ZEND_USER_FUNCTION && zend_hash_exists(files, func->op_array.filename)) {
579 						insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
580 
581 						if (by_function) {
582 							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));
583 							insert_ht = phpdbg_add_empty_array(insert_ht, fn_name);
584 							zend_string_release(fn_name);
585 						}
586 
587 						phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
588 					}
589 				} ZEND_HASH_FOREACH_END();
590 			}
591 		}
592 	} ZEND_HASH_FOREACH_END();
593 
594 	ZEND_HASH_FOREACH_STR_KEY(files, name) {
595 		phpdbg_file_source *source = zend_hash_find_ptr(&PHPDBG_G(file_sources), name);
596 		if (source) {
597 			phpdbg_oplog_fill_executable(
598 				&source->op_array,
599 				phpdbg_add_empty_array(Z_ARR_P(return_value), source->op_array.filename),
600 				by_opcode);
601 		}
602 	} ZEND_HASH_FOREACH_END();
603 
604 	if (!--GC_REFCOUNT(files)) {
605 		zend_hash_destroy(files);
606 	}
607 }
608 
609 /* {{{ proto void phpdbg_end_oplog() */
PHP_FUNCTION(phpdbg_end_oplog)610 static PHP_FUNCTION(phpdbg_end_oplog)
611 {
612 	phpdbg_oplog_entry *cur;
613 	phpdbg_oplog_list *prev;
614 
615 	HashTable *options = NULL;
616 	zval *option_buffer;
617 	zend_bool by_function = 0;
618 	zend_bool by_opcode = 0;
619 
620 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
621 		return;
622 	}
623 
624 	if (!PHPDBG_G(oplog_list)) {
625 		zend_error(E_WARNING, "Can not end an oplog without starting it");
626 		return;
627 	}
628 
629 	cur = PHPDBG_G(oplog_list)->start;
630 	prev = PHPDBG_G(oplog_list)->prev;
631 
632 	efree(PHPDBG_G(oplog_list));
633 	PHPDBG_G(oplog_list) = prev;
634 
635 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
636 		by_function = zend_is_true(option_buffer);
637 	}
638 
639 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
640 		if (by_function) {
641 			by_opcode = zend_is_true(option_buffer);
642 		}
643 	}
644 
645 	array_init(return_value);
646 
647 	{
648 		zend_string *last_file = NULL;
649 		HashTable *file_ht;
650 		zend_string *last_function = (void *)~(uintptr_t)0;
651 		zend_class_entry *last_scope = NULL;
652 
653 		HashTable *insert_ht;
654 		zend_long insert_idx;
655 
656 		do {
657 			zval zero;
658 			ZVAL_LONG(&zero, 0);
659 
660 			if (cur->filename != last_file) {
661 				last_file = cur->filename;
662 				file_ht = insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), last_file);
663 			}
664 
665 			if (by_function) {
666 				if (cur->function_name == NULL) {
667 					if (last_function != NULL) {
668 						insert_ht = file_ht;
669 					}
670 					last_function = NULL;
671 				} else if (cur->function_name != last_function || cur->scope != last_scope) {
672 					zend_string *fn_name;
673 					last_function = cur->function_name;
674 					last_scope = cur->scope;
675 					if (last_scope == NULL) {
676 						fn_name = zend_string_copy(last_function);
677 					} else {
678 						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));
679 					}
680 					insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), fn_name);
681 					zend_string_release(fn_name);
682 				}
683 			}
684 
685 			if (by_opcode) {
686 				insert_idx = cur->op - cur->opcodes;
687 			} else {
688 				if (phpdbg_is_ignored_opcode(cur->op->opcode)) {
689 					continue;
690 				}
691 
692 				insert_idx = cur->op->lineno;
693 			}
694 
695 			{
696 				zval *num = zend_hash_index_find(insert_ht, insert_idx);
697 				if (!num) {
698 					num = zend_hash_index_add_new(insert_ht, insert_idx, &zero);
699 				}
700 				Z_LVAL_P(num)++;
701 			}
702 
703 		} while ((cur = cur->next));
704 	}
705 
706 	if (!prev) {
707 		zend_arena_destroy(PHPDBG_G(oplog_arena));
708 	}
709 }
710 
711 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_next_arginfo, 0, 0, 0)
712 ZEND_END_ARG_INFO()
713 
714 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_file_arginfo, 0, 0, 2)
715 	ZEND_ARG_INFO(0, file)
716 	ZEND_ARG_INFO(0, line)
717 ZEND_END_ARG_INFO()
718 
719 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_method_arginfo, 0, 0, 2)
720 	ZEND_ARG_INFO(0, class)
721 	ZEND_ARG_INFO(0, method)
722 ZEND_END_ARG_INFO()
723 
724 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_function_arginfo, 0, 0, 1)
725 	ZEND_ARG_INFO(0, function)
726 ZEND_END_ARG_INFO()
727 
728 ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0)
729 	ZEND_ARG_INFO(0, element)
730 	ZEND_ARG_INFO(0, color)
731 ZEND_END_ARG_INFO()
732 
733 ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0)
734 	ZEND_ARG_INFO(0, string)
735 ZEND_END_ARG_INFO()
736 
737 ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0)
738 	ZEND_ARG_INFO(0, context)
739 ZEND_END_ARG_INFO()
740 
741 ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0)
742 ZEND_END_ARG_INFO()
743 
744 ZEND_BEGIN_ARG_INFO_EX(phpdbg_start_oplog_arginfo, 0, 0, 0)
745 ZEND_END_ARG_INFO()
746 
747 ZEND_BEGIN_ARG_INFO_EX(phpdbg_end_oplog_arginfo, 0, 0, 0)
748 	ZEND_ARG_INFO(0, options)
749 ZEND_END_ARG_INFO()
750 
751 ZEND_BEGIN_ARG_INFO_EX(phpdbg_get_executable_arginfo, 0, 0, 0)
752 	ZEND_ARG_INFO(0, options)
753 ZEND_END_ARG_INFO()
754 
755 zend_function_entry phpdbg_user_functions[] = {
756 	PHP_FE(phpdbg_clear, phpdbg_clear_arginfo)
757 	PHP_FE(phpdbg_break_next, phpdbg_break_next_arginfo)
758 	PHP_FE(phpdbg_break_file, phpdbg_break_file_arginfo)
759 	PHP_FE(phpdbg_break_method, phpdbg_break_method_arginfo)
760 	PHP_FE(phpdbg_break_function, phpdbg_break_function_arginfo)
761 	PHP_FE(phpdbg_exec,  phpdbg_exec_arginfo)
762 	PHP_FE(phpdbg_color, phpdbg_color_arginfo)
763 	PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo)
764 	PHP_FE(phpdbg_start_oplog, phpdbg_start_oplog_arginfo)
765 	PHP_FE(phpdbg_end_oplog, phpdbg_end_oplog_arginfo)
766 	PHP_FE(phpdbg_get_executable, phpdbg_get_executable_arginfo)
767 #ifdef  PHP_FE_END
768 	PHP_FE_END
769 #else
770 	{NULL,NULL,NULL}
771 #endif
772 };
773 
774 static zend_module_entry sapi_phpdbg_module_entry = {
775 	STANDARD_MODULE_HEADER,
776 	PHPDBG_NAME,
777 	phpdbg_user_functions,
778 	PHP_MINIT(phpdbg),
779 	NULL,
780 	PHP_RINIT(phpdbg),
781 	PHP_RSHUTDOWN(phpdbg),
782 	NULL,
783 	PHPDBG_VERSION,
784 	STANDARD_MODULE_PROPERTIES
785 };
786 
php_sapi_phpdbg_module_startup(sapi_module_struct * module)787 static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
788 {
789 	if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
790 		return FAILURE;
791 	}
792 
793 	phpdbg_booted=1;
794 
795 	return SUCCESS;
796 } /* }}} */
797 
php_sapi_phpdbg_read_cookies(void)798 static char* php_sapi_phpdbg_read_cookies(void) /* {{{ */
799 {
800 	return NULL;
801 } /* }}} */
802 
php_sapi_phpdbg_header_handler(sapi_header_struct * h,sapi_header_op_enum op,sapi_headers_struct * s)803 static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) /* {{{ */
804 {
805 	return 0;
806 }
807 /* }}} */
808 
php_sapi_phpdbg_send_headers(sapi_headers_struct * sapi_headers)809 static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
810 {
811 	/* We do nothing here, this function is needed to prevent that the fallback
812 	 * header handling is called. */
813 	return SAPI_HEADER_SENT_SUCCESSFULLY;
814 }
815 /* }}} */
816 
php_sapi_phpdbg_send_header(sapi_header_struct * sapi_header,void * server_context)817 static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context) /* {{{ */
818 {
819 }
820 /* }}} */
821 
php_sapi_phpdbg_log_message(char * message)822 static void php_sapi_phpdbg_log_message(char *message) /* {{{ */
823 {
824 	/*
825 	* We must not request TSRM before being booted
826 	*/
827 	if (phpdbg_booted) {
828 		if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) {
829 			phpdbg_error("eval", "msg=\"%s\"", "%s", message);
830 			return;
831 		}
832 
833 		phpdbg_error("php", "msg=\"%s\"", "%s", message);
834 
835 		if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
836 			return;
837 		}
838 
839 		switch (PG(last_error_type)) {
840 			case E_ERROR:
841 			case E_CORE_ERROR:
842 			case E_COMPILE_ERROR:
843 			case E_USER_ERROR:
844 			case E_PARSE:
845 			case E_RECOVERABLE_ERROR: {
846 				const char *file_char = zend_get_executed_filename();
847 				zend_string *file = zend_string_init(file_char, strlen(file_char), 0);
848 				phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
849 				zend_string_release(file);
850 
851 				if (!phpdbg_fully_started) {
852 					return;
853 				}
854 
855 				do {
856 					switch (phpdbg_interactive(1, NULL)) {
857 						case PHPDBG_LEAVE:
858 						case PHPDBG_FINISH:
859 						case PHPDBG_UNTIL:
860 						case PHPDBG_NEXT:
861 							return;
862 					}
863 				} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
864 			}
865 		}
866 	} else {
867 		fprintf(stdout, "%s\n", message);
868 	}
869 }
870 /* }}} */
871 
php_sapi_phpdbg_deactivate(void)872 static int php_sapi_phpdbg_deactivate(void) /* {{{ */
873 {
874 	fflush(stdout);
875 	if (SG(request_info).argv0) {
876 		free(SG(request_info).argv0);
877 		SG(request_info).argv0 = NULL;
878 	}
879 
880 	return SUCCESS;
881 }
882 /* }}} */
883 
php_sapi_phpdbg_register_vars(zval * track_vars_array)884 static void php_sapi_phpdbg_register_vars(zval *track_vars_array) /* {{{ */
885 {
886 	size_t len;
887 	char  *docroot = "";
888 
889 	/* In phpdbg mode, we consider the environment to be a part of the server variables
890 	*/
891 	php_import_environment_variables(track_vars_array);
892 
893 	if (PHPDBG_G(exec)) {
894 		len = PHPDBG_G(exec_len);
895 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
896 			php_register_variable("PHP_SELF", PHPDBG_G(exec), track_vars_array);
897 		}
898 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
899 			php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), track_vars_array);
900 		}
901 
902 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
903 			php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), track_vars_array);
904 		}
905 		if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
906 			php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), track_vars_array);
907 		}
908 	}
909 
910 	/* any old docroot will do */
911 	len = 0;
912 	if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) {
913 		php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array);
914 	}
915 }
916 /* }}} */
917 
php_sapi_phpdbg_ub_write(const char * message,size_t length)918 static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length) /* {{{ */
919 {
920 	if (PHPDBG_G(socket_fd) != -1 && !(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
921 		send(PHPDBG_G(socket_fd), message, length, 0);
922 	}
923 	return phpdbg_script(P_STDOUT, "%.*s", (int) length, message);
924 } /* }}} */
925 
926 /* beginning of struct, see main/streams/plain_wrapper.c line 111 */
927 typedef struct {
928 	FILE *file;
929 	int fd;
930 } php_stdio_stream_data;
931 
phpdbg_stdiop_write(php_stream * stream,const char * buf,size_t count)932 static size_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count) {
933 	php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
934 
935 	while (data->fd >= 0) {
936 		struct stat stat[3];
937 		memset(stat, 0, sizeof(stat));
938 		if (((fstat(fileno(stderr), &stat[2]) < 0) & (fstat(fileno(stdout), &stat[0]) < 0)) | (fstat(data->fd, &stat[1]) < 0)) {
939 			break;
940 		}
941 
942 		if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) {
943 			phpdbg_script(P_STDOUT, "%.*s", (int) count, buf);
944 			return count;
945 		}
946 		if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) {
947 			phpdbg_script_ex(PHPDBG_G(io)[PHPDBG_STDERR].fd, P_STDERR, "%.*s", (int) count, buf);
948 			return count;
949 		}
950 		break;
951 	}
952 
953 	return PHPDBG_G(php_stdiop_write)(stream, buf, count);
954 }
955 
php_sapi_phpdbg_flush(void * context)956 static inline void php_sapi_phpdbg_flush(void *context)  /* {{{ */
957 {
958 	if (!phpdbg_active_sigsafe_mem()) {
959 		fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
960 	}
961 } /* }}} */
962 
963 /* copied from sapi/cli/php_cli.c cli_register_file_handles */
phpdbg_register_file_handles(void)964 void phpdbg_register_file_handles(void) /* {{{ */
965 {
966 	zval zin, zout, zerr;
967 	php_stream *s_in, *s_out, *s_err;
968 	php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
969 	zend_constant ic, oc, ec;
970 
971 	s_in  = php_stream_open_wrapper_ex("php://stdin",  "rb", 0, NULL, sc_in);
972 	s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
973 	s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
974 
975 	if (s_in==NULL || s_out==NULL || s_err==NULL) {
976 		if (s_in) php_stream_close(s_in);
977 		if (s_out) php_stream_close(s_out);
978 		if (s_err) php_stream_close(s_err);
979 		return;
980 	}
981 
982 #if PHP_DEBUG
983 	/* do not close stdout and stderr */
984 	s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
985 	s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
986 #endif
987 
988 	php_stream_to_zval(s_in,  &zin);
989 	php_stream_to_zval(s_out, &zout);
990 	php_stream_to_zval(s_err, &zerr);
991 
992 	ic.value = zin;
993 	ic.flags = CONST_CS;
994 	ic.name = zend_string_init(ZEND_STRL("STDIN"), 0);
995 	ic.module_number = 0;
996 	zend_hash_del(EG(zend_constants), ic.name);
997 	zend_register_constant(&ic);
998 
999 	oc.value = zout;
1000 	oc.flags = CONST_CS;
1001 	oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0);
1002 	oc.module_number = 0;
1003 	zend_hash_del(EG(zend_constants), oc.name);
1004 	zend_register_constant(&oc);
1005 
1006 	ec.value = zerr;
1007 	ec.flags = CONST_CS;
1008 	ec.name = zend_string_init(ZEND_STRL("STDERR"), 0);
1009 	ec.module_number = 0;
1010 	zend_hash_del(EG(zend_constants), ec.name);
1011 	zend_register_constant(&ec);
1012 }
1013 /* }}} */
1014 
1015 /* {{{ sapi_module_struct phpdbg_sapi_module
1016 */
1017 static sapi_module_struct phpdbg_sapi_module = {
1018 	"phpdbg",                       /* name */
1019 	"phpdbg",                       /* pretty name */
1020 
1021 	php_sapi_phpdbg_module_startup, /* startup */
1022 	php_module_shutdown_wrapper,    /* shutdown */
1023 
1024 	NULL,                           /* activate */
1025 	php_sapi_phpdbg_deactivate,     /* deactivate */
1026 
1027 	php_sapi_phpdbg_ub_write,       /* unbuffered write */
1028 	php_sapi_phpdbg_flush,          /* flush */
1029 	NULL,                           /* get uid */
1030 	NULL,                           /* getenv */
1031 
1032 	php_error,                      /* error handler */
1033 
1034 	php_sapi_phpdbg_header_handler, /* header handler */
1035 	php_sapi_phpdbg_send_headers,   /* send headers handler */
1036 	php_sapi_phpdbg_send_header,    /* send header handler */
1037 
1038 	NULL,                           /* read POST data */
1039 	php_sapi_phpdbg_read_cookies,   /* read Cookies */
1040 
1041 	php_sapi_phpdbg_register_vars,  /* register server variables */
1042 	php_sapi_phpdbg_log_message,    /* Log message */
1043 	NULL,                           /* Get request time */
1044 	NULL,                           /* Child terminate */
1045 	STANDARD_SAPI_MODULE_PROPERTIES
1046 };
1047 /* }}} */
1048 
1049 const opt_struct OPTIONS[] = { /* {{{ */
1050 	{'c', 1, "ini path override"},
1051 	{'d', 1, "define ini entry on command line"},
1052 	{'n', 0, "no php.ini"},
1053 	{'z', 1, "load zend_extension"},
1054 	/* phpdbg options */
1055 	{'q', 0, "no banner"},
1056 	{'v', 0, "disable quietness"},
1057 	{'b', 0, "boring colours"},
1058 	{'i', 1, "specify init"},
1059 	{'I', 0, "ignore init"},
1060 	{'O', 1, "opline log"},
1061 	{'r', 0, "run"},
1062 	{'e', 0, "generate ext_stmt opcodes"},
1063 	{'E', 0, "step-through-eval"},
1064 	{'s', 1, "script from stdin"},
1065 	{'S', 1, "sapi-name"},
1066 #ifndef _WIN32
1067 	{'l', 1, "listen"},
1068 	{'a', 1, "address-or-any"},
1069 #endif
1070 	{'x', 0, "xml output"},
1071 	{'p', 2, "show opcodes"},
1072 	{'h', 0, "help"},
1073 	{'V', 0, "version"},
1074 	{'-', 0, NULL}
1075 }; /* }}} */
1076 
1077 const char phpdbg_ini_hardcoded[] =
1078 "html_errors=Off\n"
1079 "register_argc_argv=On\n"
1080 "implicit_flush=On\n"
1081 "display_errors=Off\n"
1082 "log_errors=On\n"
1083 "max_execution_time=0\n"
1084 "max_input_time=-1\n"
1085 "error_log=\n"
1086 "output_buffering=off\n\0";
1087 
1088 /* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
1089 #define INI_DEFAULT(name, value) \
1090 	ZVAL_STRINGL(&tmp, value, sizeof(value) - 1); \
1091 	zend_hash_str_update(configuration_hash, name, sizeof(name) - 1, &tmp);
1092 
phpdbg_ini_defaults(HashTable * configuration_hash)1093 void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
1094 {
1095 	zval tmp;
1096 	INI_DEFAULT("report_zend_debug", "0");
1097 } /* }}} */
1098 
phpdbg_welcome(zend_bool cleaning)1099 static void phpdbg_welcome(zend_bool cleaning) /* {{{ */
1100 {
1101 	/* print blurb */
1102 	if (!cleaning) {
1103 		phpdbg_xml("<intros>");
1104 		phpdbg_notice("intro", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
1105 		phpdbg_writeln("intro", "help=\"help\"", "To get help using phpdbg type \"help\" and press enter");
1106 		phpdbg_notice("intro", "report=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES);
1107 		phpdbg_xml("</intros>");
1108 	} else if (phpdbg_startup_run == 0) {
1109 		if (!(PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
1110 			phpdbg_notice(NULL, NULL, "Clean Execution Environment");
1111 		}
1112 
1113 		phpdbg_write("cleaninfo", "classes=\"%d\" functions=\"%d\" constants=\"%d\" includes=\"%d\"",
1114 			"Classes              %d\n"
1115 			"Functions            %d\n"
1116 			"Constants            %d\n"
1117 			"Includes             %d\n",
1118 			zend_hash_num_elements(EG(class_table)),
1119 			zend_hash_num_elements(EG(function_table)),
1120 			zend_hash_num_elements(EG(zend_constants)),
1121 			zend_hash_num_elements(&EG(included_files)));
1122 	}
1123 } /* }}} */
1124 
phpdbg_sigint_handler(int signo)1125 static inline void phpdbg_sigint_handler(int signo) /* {{{ */
1126 {
1127 
1128 	if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
1129 		/* we quit remote consoles on recv SIGINT */
1130 		if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
1131 			PHPDBG_G(flags) |= PHPDBG_IS_STOPPING;
1132 			zend_bailout();
1133 		}
1134 	} else {
1135 		/* set signalled only when not interactive */
1136 		if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
1137 			char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
1138 
1139 			phpdbg_set_sigsafe_mem(mem);
1140 			zend_try {
1141 				phpdbg_force_interruption();
1142 			} zend_end_try()
1143 			phpdbg_clear_sigsafe_mem();
1144 
1145 			PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
1146 
1147 			if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) {
1148 				zend_bailout();
1149 			}
1150 		} else {
1151 			PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
1152 			if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
1153 				PHPDBG_G(flags) |= PHPDBG_HAS_PAGINATION;
1154 				PHPDBG_G(flags) &= ~PHPDBG_PREVENT_INTERACTIVE;
1155 			}
1156 		}
1157 	}
1158 } /* }}} */
1159 
phpdbg_remote_close(int socket,FILE * stream)1160 static void phpdbg_remote_close(int socket, FILE *stream) {
1161 	if (socket >= 0) {
1162 		phpdbg_close_socket(socket);
1163 	}
1164 
1165 	if (stream) {
1166 		fclose(stream);
1167 	}
1168 }
1169 
1170 /* don't inline this, want to debug it easily, will inline when done */
phpdbg_remote_init(const char * address,unsigned short port,int server,int * socket,FILE ** stream)1171 static int phpdbg_remote_init(const char* address, unsigned short port, int server, int *socket, FILE **stream) {
1172 	phpdbg_remote_close(*socket, *stream);
1173 
1174 	if (server < 0) {
1175 		phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port);
1176 
1177 		return FAILURE;
1178 	}
1179 
1180 	phpdbg_rlog(fileno(stderr), "accepting connections on %s:%u", address, port);
1181 	{
1182 		struct sockaddr_storage address;
1183 		socklen_t size = sizeof(address);
1184 		char buffer[20] = {0};
1185 		/* XXX error checks */
1186 		memset(&address, 0, size);
1187 		*socket = accept(server, (struct sockaddr *) &address, &size);
1188 		inet_ntop(AF_INET, &(((struct sockaddr_in *)&address)->sin_addr), buffer, sizeof(buffer));
1189 
1190 		phpdbg_rlog(fileno(stderr), "connection established from %s", buffer);
1191 	}
1192 
1193 #ifndef _WIN32
1194 	dup2(*socket, fileno(stdout));
1195 	dup2(*socket, fileno(stdin));
1196 
1197 	setbuf(stdout, NULL);
1198 
1199 	*stream = fdopen(*socket, "r+");
1200 
1201 	phpdbg_set_async_io(*socket);
1202 #endif
1203 	return SUCCESS;
1204 }
1205 
1206 #ifndef _WIN32
1207 /* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */
phpdbg_sigio_handler(int sig,siginfo_t * info,void * context)1208 void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */
1209 {
1210 	int flags;
1211 	size_t newlen;
1212 	size_t i/*, last_nl*/;
1213 
1214 //	if (!(info->si_band & POLLIN)) {
1215 //		return; /* Not interested in writeablility etc., just interested in incoming data */
1216 //	}
1217 
1218 	/* only non-blocking reading, avoid non-blocking writing */
1219 	flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0);
1220 	fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK);
1221 
1222 	do {
1223 		char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
1224 		size_t off = 0;
1225 
1226 		if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) {
1227 			break;
1228 		}
1229 		for (i = 0; i < newlen; i++) {
1230 			switch (mem[off + i]) {
1231 				case '\x03': /* ^C char */
1232 					if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
1233 						break; /* or quit ??? */
1234 					}
1235 					if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
1236 						phpdbg_set_sigsafe_mem(mem);
1237 						zend_try {
1238 							phpdbg_force_interruption();
1239 						} zend_end_try();
1240 						phpdbg_clear_sigsafe_mem();
1241 
1242 						PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
1243 
1244 						if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) {
1245 							zend_bailout();
1246 						}
1247 					} else if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
1248 						PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
1249 					}
1250 					break;
1251 /*				case '\n':
1252 					zend_llist_add_element(PHPDBG_G(stdin), strndup()
1253 					last_nl = PHPDBG_G(stdin_buf).len + i;
1254 					break;
1255 */			}
1256 		}
1257 		off += i;
1258 	} while (0);
1259 
1260 
1261 	fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags);
1262 } /* }}} */
1263 
phpdbg_signal_handler(int sig,siginfo_t * info,void * context)1264 void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
1265 {
1266 	int is_handled = FAILURE;
1267 
1268 	switch (sig) {
1269 		case SIGBUS:
1270 		case SIGSEGV:
1271 			if (PHPDBG_G(sigsegv_bailout)) {
1272 				LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
1273 			}
1274 			is_handled = phpdbg_watchpoint_segfault_handler(info, context);
1275 			if (is_handled == FAILURE) {
1276 #ifdef ZEND_SIGNALS
1277 				zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
1278 #else
1279 				sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
1280 #endif
1281 			}
1282 			break;
1283 	}
1284 
1285 } /* }}} */
1286 #endif
1287 
phpdbg_sighup_handler(int sig)1288 void phpdbg_sighup_handler(int sig) /* {{{ */
1289 {
1290 	exit(0);
1291 } /* }}} */
1292 
phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1293 void *phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1294 {
1295 	return _zend_mm_alloc(zend_mm_get_heap(), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1296 } /* }}} */
1297 
phpdbg_free_wrapper(void * p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1298 void phpdbg_free_wrapper(void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1299 {
1300 	zend_mm_heap *heap = zend_mm_get_heap();
1301 	if (UNEXPECTED(heap == p)) {
1302 		/* TODO: heap maybe allocated by mmap(zend_mm_init) or malloc(USE_ZEND_ALLOC=0)
1303 		 * let's prevent it from segfault for now
1304 		 */
1305 	} else {
1306 		phpdbg_watch_efree(p);
1307 		_zend_mm_free(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1308 	}
1309 } /* }}} */
1310 
phpdbg_realloc_wrapper(void * ptr,size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1311 void *phpdbg_realloc_wrapper(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1312 {
1313 	return _zend_mm_realloc(zend_mm_get_heap(), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1314 } /* }}} */
1315 
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)1316 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) /* {{{ */
1317 {
1318 	if (!strncasecmp(path, "php://", 6)) {
1319 		path += 6;
1320 	}
1321 
1322 	if (!strncasecmp(path, "stdin", 6) && PHPDBG_G(stdin_file)) {
1323 		php_stream *stream = php_stream_fopen_from_fd(dup(fileno(PHPDBG_G(stdin_file))), "r", NULL);
1324 #ifdef PHP_WIN32
1325 		zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");
1326 		if (blocking_pipes) {
1327 			convert_to_long(blocking_pipes);
1328 			php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, Z_LVAL_P(blocking_pipes), NULL);
1329 		}
1330 #endif
1331 		return stream;
1332 	}
1333 
1334 	return PHPDBG_G(orig_url_wrap_php)(wrapper, path, mode, options, opened_path, context STREAMS_CC);
1335 } /* }}} */
1336 
main(int argc,char ** argv)1337 int main(int argc, char **argv) /* {{{ */
1338 {
1339 	sapi_module_struct *phpdbg = &phpdbg_sapi_module;
1340 	char *sapi_name;
1341 	char *ini_entries;
1342 	int   ini_entries_len;
1343 	char **zend_extensions = NULL;
1344 	zend_ulong zend_extensions_len = 0L;
1345 	zend_bool ini_ignore;
1346 	char *ini_override;
1347 	char *exec = NULL;
1348 	char *first_command = NULL;
1349 	char *init_file;
1350 	size_t init_file_len;
1351 	zend_bool init_file_default;
1352 	char *oplog_file;
1353 	size_t oplog_file_len;
1354 	uint64_t flags;
1355 	char *php_optarg;
1356 	int php_optind, opt, show_banner = 1;
1357 	long cleaning = -1;
1358 	volatile zend_bool quit_immediately = 0; /* somehow some gcc release builds will play a bit around with order in combination with setjmp..., hence volatile */
1359 	zend_bool remote = 0;
1360 	zend_phpdbg_globals *settings = NULL;
1361 	char *bp_tmp = NULL;
1362 	char *address;
1363 	int listen = -1;
1364 	int server = -1;
1365 	int socket = -1;
1366 	FILE* stream = NULL;
1367 	char *print_opline_func;
1368 	zend_bool ext_stmt = 0;
1369 	zend_bool use_mm_wrappers = 0;
1370 	zend_bool is_exit;
1371 	int exit_status;
1372 	char *read_from_stdin = NULL;
1373 	zend_string *backup_phpdbg_compile = NULL;
1374 	zend_bool show_help = 0, show_version = 0;
1375 
1376 #ifndef _WIN32
1377 	struct sigaction sigio_struct;
1378 	struct sigaction signal_struct;
1379 	signal_struct.sa_sigaction = phpdbg_signal_handler;
1380 	signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
1381 	sigemptyset(&signal_struct.sa_mask);
1382 	sigio_struct.sa_sigaction = phpdbg_sigio_handler;
1383 	sigio_struct.sa_flags = SA_SIGINFO;
1384 	sigemptyset(&sigio_struct.sa_mask);
1385 
1386 	address = strdup("127.0.0.1");
1387 #endif
1388 
1389 #ifdef PHP_WIN32
1390 	_fmode = _O_BINARY;                 /* sets default for file streams to binary */
1391 	setmode(_fileno(stdin), O_BINARY);  /* make the stdio mode be binary */
1392 	setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1393 	setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1394 #endif
1395 
1396 #ifdef ZTS
1397 	tsrm_startup(1, 1, 0, NULL);
1398 	(void)ts_resource(0);
1399 	ZEND_TSRMLS_CACHE_UPDATE();
1400 #endif
1401 
1402 #ifdef ZEND_SIGNALS
1403 	zend_signal_startup();
1404 #endif
1405 
1406 phpdbg_main:
1407 	ini_entries = NULL;
1408 	ini_entries_len = 0;
1409 	ini_ignore = 0;
1410 	ini_override = NULL;
1411 	zend_extensions = NULL;
1412 	zend_extensions_len = 0L;
1413 	init_file = NULL;
1414 	init_file_len = 0;
1415 	init_file_default = 1;
1416 	oplog_file = NULL;
1417 	oplog_file_len = 0;
1418 	flags = PHPDBG_DEFAULT_FLAGS;
1419 	is_exit = 0;
1420 	php_optarg = NULL;
1421 	php_optind = 1;
1422 	opt = 0;
1423 	sapi_name = NULL;
1424 	exit_status = 0;
1425 	if (settings) {
1426 		exec = settings->exec;
1427 	}
1428 
1429 	while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1430 		switch (opt) {
1431 			case 'r':
1432 				if (settings == NULL) {
1433 					phpdbg_startup_run++;
1434 				}
1435 				break;
1436 			case 'n':
1437 				ini_ignore = 1;
1438 				break;
1439 			case 'c':
1440 				if (ini_override) {
1441 					free(ini_override);
1442 				}
1443 				ini_override = strdup(php_optarg);
1444 				break;
1445 			case 'd': {
1446 				int len = strlen(php_optarg);
1447 				char *val;
1448 
1449 				if ((val = strchr(php_optarg, '='))) {
1450 				  val++;
1451 				  if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
1452 					  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
1453 					  memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
1454 					  ini_entries_len += (val - php_optarg);
1455 					  memcpy(ini_entries + ini_entries_len, "\"", 1);
1456 					  ini_entries_len++;
1457 					  memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
1458 					  ini_entries_len += len - (val - php_optarg);
1459 					  memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
1460 					  ini_entries_len += sizeof("\n\0\"") - 2;
1461 				  } else {
1462 					  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
1463 					  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1464 					  memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
1465 					  ini_entries_len += len + sizeof("\n\0") - 2;
1466 				  }
1467 				} else {
1468 				  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
1469 				  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1470 				  memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
1471 				  ini_entries_len += len + sizeof("=1\n\0") - 2;
1472 				}
1473 			} break;
1474 
1475 			case 'z':
1476 				zend_extensions_len++;
1477 				if (zend_extensions) {
1478 					zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
1479 				} else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
1480 				zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
1481 			break;
1482 
1483 			/* begin phpdbg options */
1484 
1485 			case 's': { /* read script from stdin */
1486 				if (settings == NULL) {
1487 					read_from_stdin = strdup(php_optarg);
1488 				}
1489 			} break;
1490 
1491 			case 'S': { /* set SAPI name */
1492 				if (sapi_name) {
1493 					free(sapi_name);
1494 				}
1495 				sapi_name = strdup(php_optarg);
1496 			} break;
1497 
1498 			case 'I': { /* ignore .phpdbginit */
1499 				init_file_default = 0;
1500 			} break;
1501 
1502 			case 'i': { /* set init file */
1503 				if (init_file) {
1504 					free(init_file);
1505 					init_file = NULL;
1506 				}
1507 
1508 				init_file_len = strlen(php_optarg);
1509 				if (init_file_len) {
1510 					init_file = strdup(php_optarg);
1511 				}
1512 			} break;
1513 
1514 			case 'O': { /* set oplog output */
1515 				oplog_file_len = strlen(php_optarg);
1516 				if (oplog_file_len) {
1517 					oplog_file = strdup(php_optarg);
1518 				}
1519 			} break;
1520 
1521 			case 'v': /* set quietness off */
1522 				flags &= ~PHPDBG_IS_QUIET;
1523 			break;
1524 
1525 			case 'e':
1526 				ext_stmt = 1;
1527 			break;
1528 
1529 			case 'E': /* stepping through eval on */
1530 				flags |= PHPDBG_IS_STEPONEVAL;
1531 			break;
1532 
1533 			case 'b': /* set colours off */
1534 				flags &= ~PHPDBG_IS_COLOURED;
1535 			break;
1536 
1537 			case 'q': /* hide banner */
1538 				show_banner = 0;
1539 			break;
1540 
1541 #ifndef _WIN32
1542 			/* if you pass a listen port, we will read and write on listen port */
1543 			case 'l': /* set listen ports */
1544 				if (sscanf(php_optarg, "%d", &listen) != 1) {
1545 					listen = 8000;
1546 				}
1547 			break;
1548 
1549 			case 'a': { /* set bind address */
1550 				free(address);
1551 				if (!php_optarg) {
1552 					address = strdup("*");
1553 				} else address = strdup(php_optarg);
1554 			} break;
1555 #endif
1556 
1557 			case 'x':
1558 				flags |= PHPDBG_WRITE_XML;
1559 			break;
1560 
1561 
1562 			case 'p': {
1563 				print_opline_func = php_optarg;
1564 				show_banner = 0;
1565 				settings = (void *) 0x1;
1566 			} break;
1567 
1568 			case 'h': {
1569 				show_help = 1;
1570 			} break;
1571 
1572 			case 'V': {
1573 				show_version = 1;
1574 			} break;
1575 		}
1576 
1577 		php_optarg = NULL;
1578 	}
1579 
1580 	quit_immediately = phpdbg_startup_run > 1;
1581 
1582 	/* set exec if present on command line */
1583 	if (!read_from_stdin && argc > php_optind) {
1584 		if (!exec && strlen(argv[php_optind])) {
1585 			exec = strdup(argv[php_optind]);
1586 		}
1587 		php_optind++;
1588 	}
1589 
1590 	if (sapi_name) {
1591 		phpdbg->name = sapi_name;
1592 	}
1593 
1594 	phpdbg->ini_defaults = phpdbg_ini_defaults;
1595 	phpdbg->phpinfo_as_text = 1;
1596 	phpdbg->php_ini_ignore_cwd = 1;
1597 
1598 	sapi_startup(phpdbg);
1599 
1600 	phpdbg->executable_location = argv[0];
1601 	phpdbg->phpinfo_as_text = 1;
1602 	phpdbg->php_ini_ignore = ini_ignore;
1603 	phpdbg->php_ini_path_override = ini_override;
1604 
1605 	if (ini_entries) {
1606 		ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded));
1607 		memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1);
1608 		memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2);
1609 	} else {
1610 		ini_entries = malloc(sizeof(phpdbg_ini_hardcoded));
1611 		memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
1612 	}
1613 	ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
1614 
1615 	if (zend_extensions_len) {
1616 		zend_ulong zend_extension = 0L;
1617 
1618 		while (zend_extension < zend_extensions_len) {
1619 			const char *ze = zend_extensions[zend_extension];
1620 			size_t ze_len = strlen(ze);
1621 
1622 			ini_entries = realloc(
1623 				ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
1624 			memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
1625 			ini_entries_len += (sizeof("zend_extension=")-1);
1626 			memcpy(&ini_entries[ini_entries_len], ze, ze_len);
1627 			ini_entries_len += ze_len;
1628 			memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
1629 
1630 			free(zend_extensions[zend_extension]);
1631 			zend_extension++;
1632 		}
1633 
1634 		free(zend_extensions);
1635 	}
1636 
1637 	phpdbg->ini_entries = ini_entries;
1638 
1639 	if (phpdbg->startup(phpdbg) == SUCCESS) {
1640 		zend_mm_heap *mm_heap;
1641 #ifdef _WIN32
1642     EXCEPTION_POINTERS *xp;
1643     __try {
1644 #endif
1645 		void* (*_malloc)(size_t);
1646 		void (*_free)(void*);
1647 		void* (*_realloc)(void*, size_t);
1648 
1649 		/* set flags from command line */
1650 		PHPDBG_G(flags) = flags;
1651 
1652 		/* set default colors */
1653 		phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT,  PHPDBG_STRL("white-bold"));
1654 		phpdbg_set_color_ex(PHPDBG_COLOR_ERROR,   PHPDBG_STRL("red-bold"));
1655 		phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE,  PHPDBG_STRL("green"));
1656 
1657 		/* set default prompt */
1658 		phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
1659 
1660 		if (show_version || show_help) {
1661 			/* It ain't gonna proceed to real execution anyway,
1662 				but the correct descriptor is needed already. */
1663 			PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1664 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1665 			if (show_help) {
1666 				phpdbg_do_help_cmd(exec);
1667 			} else if (show_version) {
1668 				phpdbg_out(
1669 					"phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2017 The PHP Group\n%s",
1670 					PHPDBG_VERSION,
1671 					__DATE__,
1672 					__TIME__,
1673 					PHP_VERSION,
1674 					get_zend_version()
1675 				);
1676 			}
1677 			sapi_deactivate();
1678 			sapi_shutdown();
1679 			if (ini_entries) {
1680 				free(ini_entries);
1681 			}
1682 			if (ini_override) {
1683 				free(ini_override);
1684 			}
1685 			if (exec) {
1686 				free(exec);
1687 			}
1688 			if (oplog_file) {
1689 				free(oplog_file);
1690 			}
1691 			if (init_file) {
1692 				free(init_file);
1693 			}
1694 			goto free_and_return;
1695 		}
1696 
1697 		if (settings > (zend_phpdbg_globals *) 0x2) {
1698 #ifdef ZTS
1699 			*((zend_phpdbg_globals *) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings;
1700 #else
1701 			phpdbg_globals = *settings;
1702 #endif
1703 			free(settings);
1704 		}
1705 
1706 		/* setup remote server if necessary */
1707 		if (cleaning <= 0 && listen > 0) {
1708 			server = phpdbg_open_socket(address, listen);
1709 			if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream) == FAILURE) {
1710 				exit(0);
1711 			}
1712 
1713 #ifndef _WIN32
1714 			sigaction(SIGIO, &sigio_struct, NULL);
1715 #endif
1716 
1717 			/* set remote flag to stop service shutting down upon quit */
1718 			remote = 1;
1719 #ifndef _WIN32
1720 		} else {
1721 
1722 			signal(SIGHUP, phpdbg_sighup_handler);
1723 #endif
1724 		}
1725 
1726 		mm_heap = zend_mm_get_heap();
1727 		zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc);
1728 
1729 		use_mm_wrappers = !_malloc && !_realloc && !_free;
1730 
1731 		phpdbg_init_list();
1732 
1733 		PHPDBG_G(original_free_function) = _free;
1734 		_free = phpdbg_watch_efree;
1735 
1736 		if (use_mm_wrappers) {
1737 #if ZEND_DEBUG
1738 			zend_mm_set_custom_debug_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1739 #else
1740 			zend_mm_set_custom_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1741 #endif
1742 		} else {
1743 			zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
1744 		}
1745 
1746 		phpdbg_setup_watchpoints();
1747 
1748 #if defined(ZEND_SIGNALS) && !defined(_WIN32)
1749 		zend_try {
1750 			zend_signal_activate();
1751 		} zend_end_try();
1752 #endif
1753 
1754 #if defined(ZEND_SIGNALS) && !defined(_WIN32)
1755 		zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1756 		zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1757 #elif !defined(_WIN32)
1758 		sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
1759 		sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
1760 #endif
1761 
1762 		PHPDBG_G(sapi_name_ptr) = sapi_name;
1763 
1764 		if (exec) { /* set execution context */
1765 			PHPDBG_G(exec) = phpdbg_resolve_path(exec);
1766 			PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
1767 
1768 			free(exec);
1769 			exec = NULL;
1770 		}
1771 
1772 		php_output_activate();
1773 		php_output_deactivate();
1774 
1775 		if (SG(sapi_headers).mimetype) {
1776 			efree(SG(sapi_headers).mimetype);
1777 			SG(sapi_headers).mimetype = NULL;
1778 		}
1779 
1780 		php_output_activate();
1781 
1782 		{
1783 			int i;
1784 
1785 			SG(request_info).argc = argc - php_optind + 1;
1786 			SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1787 			for (i = SG(request_info).argc; --i;) {
1788 				SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1789 			}
1790 			SG(request_info).argv[0] = PHPDBG_G(exec) ? estrdup(PHPDBG_G(exec)) : estrdup("");
1791 		}
1792 
1793 		if (php_request_startup() == FAILURE) {
1794 			PUTS("Could not startup");
1795 			return 1;
1796 		}
1797 
1798 		/* do not install sigint handlers for remote consoles */
1799 		/* sending SIGINT then provides a decent way of shutting down the server */
1800 #ifndef _WIN32
1801 		if (listen < 0) {
1802 #endif
1803 #if defined(ZEND_SIGNALS) && !defined(_WIN32)
1804 			zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
1805 #else
1806 			signal(SIGINT, phpdbg_sigint_handler);
1807 #endif
1808 #ifndef _WIN32
1809 		}
1810 
1811 		/* setup io here */
1812 		if (remote) {
1813 			PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
1814 
1815 			signal(SIGPIPE, SIG_IGN);
1816 		}
1817 		PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1818 		PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1819 		PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1820 		PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1821 #else
1822 		/* XXX this is a complete mess here with FILE/fd/SOCKET,
1823 			we should let only one to survive probably. Need
1824 			a clean separation whether it's a remote or local
1825 			prompt. And what is supposed to go as user interaction,
1826 			error log, etc. */
1827 		if (remote) {
1828 			PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1829 			PHPDBG_G(io)[PHPDBG_STDIN].fd = socket;
1830 			PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1831 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = socket;
1832 		} else {
1833 			PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1834 			PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1835 			PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1836 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1837 		}
1838 #endif
1839 		PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr;
1840 		PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
1841 
1842 #ifndef _WIN32
1843 		PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
1844 		php_stream_stdio_ops.write = phpdbg_stdiop_write;
1845 #endif
1846 
1847 		if (oplog_file) { /* open oplog */
1848 			PHPDBG_G(oplog) = fopen(oplog_file, "w+");
1849 			if (!PHPDBG_G(oplog)) {
1850 				phpdbg_error("oplog", "path=\"%s\"", "Failed to open oplog %s", oplog_file);
1851 			}
1852 			free(oplog_file);
1853 			oplog_file = NULL;
1854 		}
1855 
1856 		{
1857 			php_stream_wrapper *wrapper = zend_hash_str_find_ptr(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1858 			PHPDBG_G(orig_url_wrap_php) = wrapper->wops->stream_opener;
1859 			wrapper->wops->stream_opener = phpdbg_stream_url_wrap_php;
1860 		}
1861 
1862 		/* Make stdin, stdout and stderr accessible from PHP scripts */
1863 		phpdbg_register_file_handles();
1864 
1865 		phpdbg_list_update();
1866 
1867 		if (show_banner && cleaning < 2) {
1868 			/* print blurb */
1869 			phpdbg_welcome(cleaning == 1);
1870 		}
1871 
1872 		cleaning = -1;
1873 
1874 		if (ext_stmt) {
1875 			CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1876 		}
1877 
1878 		/* initialize from file */
1879 		PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1880 		zend_try {
1881 			phpdbg_init(init_file, init_file_len, init_file_default);
1882 		} zend_end_try();
1883 		PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1884 
1885 		/* quit if init says so */
1886 		if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1887 			goto phpdbg_out;
1888 		}
1889 
1890 		/* auto compile */
1891 		if (read_from_stdin) {
1892 			if (!read_from_stdin[0]) {
1893 				if (!quit_immediately) {
1894 					phpdbg_error("error", "", "Impossible to not specify a stdin delimiter without -rr");
1895 					PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1896 					goto phpdbg_out;
1897 				}
1898 			}
1899 			if (show_banner || read_from_stdin[0]) {
1900 				phpdbg_notice("stdin", "delimiter=\"%s\"", "Reading input from stdin; put '%s' followed by a newline on an own line after code to end input", read_from_stdin);
1901 			}
1902 
1903 			if (phpdbg_startup_run > 0) {
1904 				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1905 			}
1906 
1907 			zend_try {
1908 				phpdbg_param_t cmd;
1909 				cmd.str = read_from_stdin;
1910 				cmd.len = strlen(read_from_stdin);
1911 				PHPDBG_COMMAND_HANDLER(stdin)(&cmd);
1912 			} zend_end_try();
1913 
1914 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1915 		} else if (PHPDBG_G(exec)) {
1916 			if (settings || phpdbg_startup_run > 0) {
1917 				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1918 			}
1919 
1920 			zend_try {
1921 				if (backup_phpdbg_compile) {
1922 					phpdbg_compile_stdin(backup_phpdbg_compile);
1923 				} else {
1924 					phpdbg_compile();
1925 				}
1926 			} zend_end_try();
1927 			backup_phpdbg_compile = NULL;
1928 
1929 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1930 		}
1931 
1932 		if (bp_tmp) {
1933 			PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT | PHPDBG_IS_INITIALIZING;
1934 			phpdbg_string_init(bp_tmp);
1935 			free(bp_tmp);
1936 			bp_tmp = NULL;
1937 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT & ~PHPDBG_IS_INITIALIZING;
1938 		}
1939 
1940 		if (settings == (void *) 0x1) {
1941 			if (PHPDBG_G(ops)) {
1942 				phpdbg_print_opcodes(print_opline_func);
1943 			} else {
1944 				zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("No opcodes could be compiled | No file specified or compilation failed?\n"));
1945 			}
1946 			goto phpdbg_out;
1947 		}
1948 
1949 		PG(during_request_startup) = 0;
1950 
1951 		phpdbg_fully_started = 1;
1952 
1953 /* #ifndef for making compiler shutting up */
1954 #ifndef _WIN32
1955 phpdbg_interact:
1956 #endif
1957 		/* phpdbg main() */
1958 		do {
1959 			zend_try {
1960 				if (phpdbg_startup_run) {
1961 					phpdbg_startup_run = 0;
1962 					if (quit_immediately) {
1963 						PHPDBG_G(flags) = (PHPDBG_G(flags) & ~PHPDBG_HAS_PAGINATION) | PHPDBG_IS_INTERACTIVE | PHPDBG_PREVENT_INTERACTIVE;
1964 					} else {
1965 						PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
1966 					}
1967 					zend_try {
1968 						if (first_command) {
1969 							phpdbg_interactive(1, estrdup(first_command));
1970 						} else {
1971 							PHPDBG_COMMAND_HANDLER(run)(NULL);
1972 						}
1973 					} zend_end_try();
1974 					if (quit_immediately) {
1975 						/* if -r is on the command line more than once just quit */
1976 						EG(bailout) = __orig_bailout; /* reset zend_try */
1977 						exit_status = EG(exit_status);
1978 						break;
1979 					}
1980 				}
1981 
1982 				CG(unclean_shutdown) = 0;
1983 				phpdbg_interactive(1, NULL);
1984 			} zend_catch {
1985 				if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1986 					char *bp_tmp_str;
1987 					PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1988 					phpdbg_export_breakpoints_to_string(&bp_tmp_str);
1989 					PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1990 					if (bp_tmp_str) {
1991 						bp_tmp = strdup(bp_tmp_str);
1992 						efree(bp_tmp_str);
1993 					}
1994 					cleaning = 1;
1995 				} else {
1996 					cleaning = 0;
1997 				}
1998 
1999 #ifndef _WIN32
2000 				if (!cleaning) {
2001 					/* remote client disconnected */
2002 					if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
2003 
2004 						if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
2005 							/* renegociate connections */
2006 							phpdbg_remote_init(address, listen, server, &socket, &stream);
2007 
2008 							/* set streams */
2009 							if (stream) {
2010 								PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
2011 							}
2012 
2013 							/* this must be forced */
2014 							CG(unclean_shutdown) = 0;
2015 						} else {
2016 							/* local consoles cannot disconnect, ignore EOF */
2017 							PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
2018 						}
2019 					}
2020 				}
2021 #endif
2022 			} zend_end_try();
2023 		} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
2024 
2025 
2026 #ifndef _WIN32
2027 phpdbg_out:
2028 		if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
2029 			PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
2030 			goto phpdbg_interact;
2031 		}
2032 #endif
2033 
2034 #ifdef _WIN32
2035 	} __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
2036 		phpdbg_error("segfault", "", "Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
2037 	}
2038 phpdbg_out:
2039 #endif
2040 
2041 		if (first_command) {
2042 			free(first_command);
2043 			first_command = NULL;
2044 		}
2045 
2046 		if (cleaning <= 0) {
2047 			PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
2048 			cleaning = -1;
2049 		}
2050 
2051 		{
2052 			int i;
2053 			/* free argv */
2054 			for (i = SG(request_info).argc; i--;) {
2055 				efree(SG(request_info).argv[i]);
2056 			}
2057 			efree(SG(request_info).argv);
2058 		}
2059 
2060 		if (ini_entries) {
2061 			free(ini_entries);
2062 		}
2063 
2064 		if (ini_override) {
2065 			free(ini_override);
2066 		}
2067 
2068 		/* In case we aborted during script execution, we may not reset CG(unclean_shutdown) */
2069 		if (!(PHPDBG_G(flags) & PHPDBG_IS_RUNNING)) {
2070 			is_exit = !PHPDBG_G(in_execution);
2071 			CG(unclean_shutdown) = is_exit || PHPDBG_G(unclean_eval);
2072 		}
2073 
2074 		if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
2075 			php_free_shutdown_functions();
2076 			zend_objects_store_mark_destructed(&EG(objects_store));
2077 		}
2078 
2079 		if (PHPDBG_G(exec) && !memcmp("-", PHPDBG_G(exec), 2)) { /* i.e. execution context has been read from stdin - back it up */
2080 			phpdbg_file_source *data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), PHPDBG_G(exec), PHPDBG_G(exec_len));
2081 			backup_phpdbg_compile = zend_string_alloc(data->len + 2, 1);
2082 			sprintf(ZSTR_VAL(backup_phpdbg_compile), "?>%.*s", (int) data->len, data->buf);
2083 		}
2084 
2085 		/* backup globals when cleaning */
2086 		if ((cleaning > 0 || remote) && !quit_immediately) {
2087 			settings = calloc(1, sizeof(zend_phpdbg_globals));
2088 
2089 			php_phpdbg_globals_ctor(settings);
2090 
2091 			if (PHPDBG_G(exec)) {
2092 				settings->exec = zend_strndup(PHPDBG_G(exec), PHPDBG_G(exec_len));
2093 				settings->exec_len = PHPDBG_G(exec_len);
2094 			}
2095 			settings->oplog = PHPDBG_G(oplog);
2096 			settings->prompt[0] = PHPDBG_G(prompt)[0];
2097 			settings->prompt[1] = PHPDBG_G(prompt)[1];
2098 			memcpy(settings->colors, PHPDBG_G(colors), sizeof(settings->colors));
2099 			settings->eol = PHPDBG_G(eol);
2100 			settings->input_buflen = PHPDBG_G(input_buflen);
2101 			memcpy(settings->input_buffer, PHPDBG_G(input_buffer), settings->input_buflen);
2102 			settings->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK;
2103 			first_command = PHPDBG_G(cur_command);
2104 		} else {
2105 			if (PHPDBG_G(prompt)[0]) {
2106 				free(PHPDBG_G(prompt)[0]);
2107 			}
2108 			if (PHPDBG_G(prompt)[1]) {
2109 				free(PHPDBG_G(prompt)[1]);
2110 			}
2111 			if (PHPDBG_G(cur_command)) {
2112 				free(PHPDBG_G(cur_command));
2113 			}
2114 		}
2115 
2116 		/* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
2117 		if (use_mm_wrappers) {
2118 			/* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
2119 			*(int *) mm_heap = 0;
2120 		}
2121 		zend_try {
2122 			php_request_shutdown(NULL);
2123 		} zend_end_try();
2124 
2125 		if (exit_status == 0) {
2126 			exit_status = EG(exit_status);
2127 		}
2128 
2129 		if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
2130 			if (PHPDBG_G(in_execution) || is_exit) {
2131 				if (!quit_immediately && !phpdbg_startup_run) {
2132 					phpdbg_notice("stop", "type=\"normal\"", "Script ended normally");
2133 					cleaning++;
2134 				}
2135 			}
2136 		}
2137 		php_output_deactivate();
2138 
2139 		{
2140 			php_stream_wrapper *wrapper = zend_hash_str_find_ptr(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
2141 			wrapper->wops->stream_opener = PHPDBG_G(orig_url_wrap_php);
2142 		}
2143 
2144 		zend_try {
2145 			php_module_shutdown();
2146 		} zend_end_try();
2147 
2148 #ifndef _WIN32
2149 		/* reset it... else we risk a stack overflow upon next run (when clean'ing) */
2150 		php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
2151 #endif
2152 
2153 		sapi_shutdown();
2154 	}
2155 
2156 free_and_return:
2157 	if (read_from_stdin) {
2158 		free(read_from_stdin);
2159 		read_from_stdin = NULL;
2160 	}
2161 
2162 	if ((cleaning > 0 || remote) && !quit_immediately) {
2163 		/* reset internal php_getopt state */
2164 		php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0);
2165 
2166 		goto phpdbg_main;
2167 	}
2168 
2169 #ifdef ZTS
2170 	/* bugggy */
2171 	/* tsrm_shutdown(); */
2172 #endif
2173 
2174 	if (backup_phpdbg_compile) {
2175 		zend_string_free(backup_phpdbg_compile);
2176 	}
2177 
2178 #ifndef _WIN32
2179 	if (address) {
2180 		free(address);
2181 	}
2182 #endif
2183 
2184 	if (PHPDBG_G(sapi_name_ptr)) {
2185 		free(PHPDBG_G(sapi_name_ptr));
2186 	}
2187 
2188 	/* usually 0; just for -rr */
2189 	return exit_status;
2190 } /* }}} */
2191