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