xref: /PHP-8.4/Zend/zend_gdb.c (revision 3b83d7e3)
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 #if defined(__FreeBSD__) && __FreeBSD_version >= 1100000
29 # include <sys/user.h>
30 # include <libutil.h>
31 #endif
32 
33 enum {
34 	ZEND_GDBJIT_NOACTION,
35 	ZEND_GDBJIT_REGISTER,
36 	ZEND_GDBJIT_UNREGISTER
37 };
38 
39 typedef struct _zend_gdbjit_code_entry {
40 	struct _zend_gdbjit_code_entry *next_entry;
41 	struct _zend_gdbjit_code_entry *prev_entry;
42 	const char                     *symfile_addr;
43 	uint64_t                        symfile_size;
44 } zend_gdbjit_code_entry;
45 
46 typedef struct _zend_gdbjit_descriptor {
47 	uint32_t                         version;
48 	uint32_t                         action_flag;
49 	struct _zend_gdbjit_code_entry *relevant_entry;
50 	struct _zend_gdbjit_code_entry *first_entry;
51 } zend_gdbjit_descriptor;
52 
53 ZEND_API zend_gdbjit_descriptor __jit_debug_descriptor = {
54 	1, ZEND_GDBJIT_NOACTION, NULL, NULL
55 };
56 
__jit_debug_register_code(void)57 ZEND_API zend_never_inline void __jit_debug_register_code(void)
58 {
59 	__asm__ __volatile__("");
60 }
61 
zend_gdb_register_code(const void * object,size_t size)62 ZEND_API bool zend_gdb_register_code(const void *object, size_t size)
63 {
64 	zend_gdbjit_code_entry *entry;
65 
66 	entry = malloc(sizeof(zend_gdbjit_code_entry) + size);
67 	if (entry == NULL) {
68 		return 0;
69 	}
70 
71 	entry->symfile_addr = ((char*)entry) + sizeof(zend_gdbjit_code_entry);
72 	entry->symfile_size = size;
73 
74 	memcpy((char *)entry->symfile_addr, object, size);
75 
76 	entry->prev_entry = NULL;
77 	entry->next_entry = __jit_debug_descriptor.first_entry;
78 
79 	if (entry->next_entry) {
80 		entry->next_entry->prev_entry = entry;
81 	}
82 	__jit_debug_descriptor.first_entry = entry;
83 
84 	/* Notify GDB */
85 	__jit_debug_descriptor.relevant_entry = entry;
86 	__jit_debug_descriptor.action_flag = ZEND_GDBJIT_REGISTER;
87 	__jit_debug_register_code();
88 
89 	return 1;
90 }
91 
zend_gdb_unregister_all(void)92 ZEND_API void zend_gdb_unregister_all(void)
93 {
94 	zend_gdbjit_code_entry *entry;
95 
96 	__jit_debug_descriptor.action_flag = ZEND_GDBJIT_UNREGISTER;
97 	while ((entry = __jit_debug_descriptor.first_entry)) {
98 		__jit_debug_descriptor.first_entry = entry->next_entry;
99 		if (entry->next_entry) {
100 			entry->next_entry->prev_entry = NULL;
101 		}
102 		/* Notify GDB */
103 		__jit_debug_descriptor.relevant_entry = entry;
104 		__jit_debug_register_code();
105 
106 		free(entry);
107 	}
108 }
109 
zend_gdb_present(void)110 ZEND_API bool zend_gdb_present(void)
111 {
112 	bool ret = 0;
113 #if defined(__linux__) /* netbsd while having this procfs part, does not hold the tracer pid */
114 	int fd = open("/proc/self/status", O_RDONLY);
115 
116 	if (fd >= 0) {
117 		char buf[1024];
118 		ssize_t n = read(fd, buf, sizeof(buf) - 1);
119 		char *s;
120 		pid_t pid;
121 
122 		if (n > 0) {
123 			buf[n] = 0;
124 			s = strstr(buf, "TracerPid:");
125 			if (s) {
126 				s += sizeof("TracerPid:") - 1;
127 				while (*s == ' ' || *s == '\t') {
128 					s++;
129 				}
130 				pid = atoi(s);
131 				if (pid) {
132 					char out[1024];
133 					snprintf(buf, sizeof(buf), "/proc/%d/exe", (int)pid);
134 					if (readlink(buf, out, sizeof(out) - 1) > 0) {
135 						if (strstr(out, "gdb")) {
136 							ret = 1;
137 						}
138 					}
139 				}
140 			}
141 		}
142 
143 		close(fd);
144 	}
145 #elif defined(__FreeBSD__) && __FreeBSD_version >= 1100000
146     struct kinfo_proc *proc = kinfo_getproc(getpid());
147 
148     if (proc) {
149         if ((proc->ki_flag & P_TRACED) != 0) {
150             struct kinfo_proc *dbg = kinfo_getproc(proc->ki_tracer);
151 
152             ret = (dbg && strstr(dbg->ki_comm, "gdb"));
153         }
154     }
155 #endif
156 
157 	return ret;
158 }
159