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