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