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