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