1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
16    |          Xinchen Hui <xinchen.h@zend.com>                            |
17    +----------------------------------------------------------------------+
18 */
19 
20 #include "zend.h"
21 #include "zend_gdb.h"
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 
28 enum {
29 	ZEND_GDBJIT_NOACTION,
30 	ZEND_GDBJIT_REGISTER,
31 	ZEND_GDBJIT_UNREGISTER
32 };
33 
34 typedef struct _zend_gdbjit_code_entry {
35 	struct _zend_gdbjit_code_entry *next_entry;
36 	struct _zend_gdbjit_code_entry *prev_entry;
37 	const char                     *symfile_addr;
38 	uint64_t                        symfile_size;
39 } zend_gdbjit_code_entry;
40 
41 typedef struct _zend_gdbjit_descriptor {
42 	uint32_t                         version;
43 	uint32_t                         action_flag;
44 	struct _zend_gdbjit_code_entry *relevant_entry;
45 	struct _zend_gdbjit_code_entry *first_entry;
46 } zend_gdbjit_descriptor;
47 
48 ZEND_API zend_gdbjit_descriptor __jit_debug_descriptor = {
49 	1, ZEND_GDBJIT_NOACTION, NULL, NULL
50 };
51 
__jit_debug_register_code(void)52 ZEND_API zend_never_inline void __jit_debug_register_code(void)
53 {
54 	__asm__ __volatile__("");
55 }
56 
zend_gdb_register_code(const void * object,size_t size)57 ZEND_API bool zend_gdb_register_code(const void *object, size_t size)
58 {
59 	zend_gdbjit_code_entry *entry;
60 
61 	entry = malloc(sizeof(zend_gdbjit_code_entry) + size);
62 	if (entry == NULL) {
63 		return 0;
64 	}
65 
66 	entry->symfile_addr = ((char*)entry) + sizeof(zend_gdbjit_code_entry);
67 	entry->symfile_size = size;
68 
69 	memcpy((char *)entry->symfile_addr, object, size);
70 
71 	entry->prev_entry = NULL;
72 	entry->next_entry = __jit_debug_descriptor.first_entry;
73 
74 	if (entry->next_entry) {
75 		entry->next_entry->prev_entry = entry;
76 	}
77 	__jit_debug_descriptor.first_entry = entry;
78 
79 	/* Notify GDB */
80 	__jit_debug_descriptor.relevant_entry = entry;
81 	__jit_debug_descriptor.action_flag = ZEND_GDBJIT_REGISTER;
82 	__jit_debug_register_code();
83 
84 	return 1;
85 }
86 
zend_gdb_unregister_all(void)87 ZEND_API void zend_gdb_unregister_all(void)
88 {
89 	zend_gdbjit_code_entry *entry;
90 
91 	__jit_debug_descriptor.action_flag = ZEND_GDBJIT_UNREGISTER;
92 	while ((entry = __jit_debug_descriptor.first_entry)) {
93 		__jit_debug_descriptor.first_entry = entry->next_entry;
94 		if (entry->next_entry) {
95 			entry->next_entry->prev_entry = NULL;
96 		}
97 		/* Notify GDB */
98 		__jit_debug_descriptor.relevant_entry = entry;
99 		__jit_debug_register_code();
100 
101 		free(entry);
102 	}
103 }
104 
zend_gdb_present(void)105 ZEND_API bool zend_gdb_present(void)
106 {
107 	bool ret = 0;
108 	int fd = open("/proc/self/status", O_RDONLY);
109 
110 	if (fd > 0) {
111 		char buf[1024];
112 		ssize_t n = read(fd, buf, sizeof(buf) - 1);
113 		char *s;
114 		pid_t pid;
115 
116 		if (n > 0) {
117 			buf[n] = 0;
118 			s = strstr(buf, "TracerPid:");
119 			if (s) {
120 				s += sizeof("TracerPid:") - 1;
121 				while (*s == ' ' || *s == '\t') {
122 					s++;
123 				}
124 				pid = atoi(s);
125 				if (pid) {
126 					char out[1024];
127 					sprintf(buf, "/proc/%d/exe", (int)pid);
128 					if (readlink(buf, out, sizeof(out) - 1) > 0) {
129 						if (strstr(out, "gdb")) {
130 							ret = 1;
131 						}
132 					}
133 				}
134 			}
135 		}
136 
137 		close(fd);
138 	}
139 
140 	return ret;
141 }
142