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