1 /*
2 * Copyright 2006 The Android Open Source Project
3 */
4
5 #include <dirent.h>
6 #include <sys/ptrace.h>
7 #include <stdint.h>
8 #include <thread_db.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <errno.h>
14
15 #define DEBUG 0
16 #if DEBUG
17 # include <string.h> /* for strerror() */
18 # define D(...) fprintf(stderr, "libthread_db:%s: ", __FUNCTION__), fprintf(stderr, __VA_ARGS__)
19 #else
20 # define D(...) do{}while(0)
21 #endif
22
23 extern int ps_pglobal_lookup (void *, const char *obj, const char *name, void **sym_addr);
24
25 struct ps_prochandle
26 {
27 pid_t pid;
28 };
29
30
31 /*
32 * This is the list of "special" symbols we care about whose addresses are
33 * cached by gdbserver from the host at init time.
34 */
35 enum {
36 SYM_TD_CREATE,
37 SYM_THREAD_LIST,
38 NUM_SYMS
39 };
40
41 static char const * gSymbols[] = {
42 [SYM_TD_CREATE] = "_thread_created_hook",
43 NULL
44 };
45
46
47 char const **
td_symbol_list(void)48 td_symbol_list(void)
49 {
50 return gSymbols;
51 }
52
53
54 /* Extract the permitted capabilities of a given task */
55 static int
_get_task_permitted_caps(int pid,int tid,uint64_t * cap)56 _get_task_permitted_caps(int pid, int tid, uint64_t *cap)
57 {
58 char path[64];
59 char buff[1024];
60 int len;
61 int fd;
62 int result = -1;
63 char* perm;
64 char* end;
65
66 /* Open task status file */
67 snprintf(path, sizeof path, "/proc/%d/task/%d/status", pid, tid);
68 fd = open(path, O_RDONLY);
69 if (fd < 0) {
70 D("Could not open %s: %s\n", path, strerror(errno));
71 return -1;
72 }
73
74 /* Read its content, up to sizeof buff-1, then zero-terminate */
75 do {
76 len = read(fd, buff, sizeof buff-1);
77 } while (len < 0 && errno == EINTR);
78
79 if (len < 0) {
80 D("Could not read %s: %s\n", path, strerror(errno));
81 goto EXIT;
82 }
83
84 buff[len] = 0;
85
86 /* Look for "CapPrm: " in it */
87 perm = strstr(buff, "CapPrm:");
88 if (perm == NULL) {
89 D("Could not find CapPrm in %s!\n---- cut here ----\n%.*s\n----- cut here -----\n",
90 path, len, buff);
91 errno = EINVAL;
92 goto EXIT;
93 }
94
95 /* Now read the hexadecimal value after 'CapPrm: ' */
96 errno = 0;
97 *cap = (uint64_t) strtoull(perm+8, &end, 16);
98 if (errno == 0) {
99 D("Found CapPerm of %lld in %s\n", *cap, path);
100 result = 0;
101 } else {
102 D("Cannot read CapPerm from %s: '%.*s'\n", path, 24, perm);
103 }
104 EXIT:
105 close(fd);
106 return result;
107 }
108
109 td_err_e
td_ta_new(struct ps_prochandle const * proc_handle,td_thragent_t ** agent_out)110 td_ta_new(struct ps_prochandle const * proc_handle, td_thragent_t ** agent_out)
111 {
112 td_thragent_t * agent;
113
114 /* Platforms before Android 2.3 contain a system bug that prevents
115 * gdbserver to attach to all threads in a target process when
116 * it is run as the same userID than the target (works fine if
117 * run as root).
118 *
119 * Due to the way gdbserver is coded, this makes gdbserver exit()
120 * immediately (see linux_attach_lwp in linux-low.c). Even if we
121 * modify the source code to not exit(), then signals will not
122 * be properly rerouted to gdbserver, preventing breakpoints from
123 * working correctly.
124 *
125 * The following code is here to test for this problematic condition.
126 * If it is detected, we return TD_NOLIBTHREAD to indicate that there
127 * are no threads to attach to (gdbserver will attach to the main thread
128 * though).
129 */
130 do {
131 char path[64];
132 DIR* dir;
133 struct dirent *entry;
134 pid_t my_pid = getpid();
135 int target_pid = proc_handle->pid;
136 uint64_t my_caps, tid_caps;
137
138 D("Probing system for platform bug.\n");
139
140 /* nothing to do if we run as root */
141 if (geteuid() == 0) {
142 D("Running as root, nothing to do.\n");
143 break;
144 }
145
146 /* First, get our own permitted capabilities */
147 if (_get_task_permitted_caps(my_pid, my_pid, &my_caps) < 0) {
148 /* something is really fishy here */
149 D("Could not get gdbserver permitted caps!\n");
150 return TD_NOLIBTHREAD;
151 }
152
153 /* Now, for each thread in the target process, compare the
154 * permitted capabilities set to our own. If they differ,
155 * the thread attach will fail. Booo...
156 */
157 snprintf(path, sizeof path, "/proc/%d/task", target_pid);
158 dir = opendir(path);
159 if (!dir) {
160 D("Could not open %s: %s\n", path, strerror(errno));
161 break;
162 }
163 while ((entry = readdir(dir)) != NULL) {
164 int tid;
165
166 if (entry->d_name[0] == '.') /* skip . and .. */
167 continue;
168
169 tid = atoi(entry->d_name);
170 if (tid == 0) /* should not happen - be safe */
171 continue;
172
173 if (_get_task_permitted_caps(target_pid, tid, &tid_caps) < 0) {
174 /* again, something is fishy */
175 D("Could not get permitted caps for thread %d\n", tid);
176 closedir(dir);
177 return TD_NOLIBTHREAD;
178 }
179
180 if (tid_caps != my_caps) {
181 /* AAAARGH !! The permitted capabilities set differ. */
182 D("AAAAAH, Can't debug threads!\n");
183 fprintf(stderr, "Thread debugging is unsupported on this Android platform!\n");
184 closedir(dir);
185 return TD_NOLIBTHREAD;
186 }
187 }
188 closedir(dir);
189 D("Victory: We can debug threads!\n");
190 } while (0);
191
192 /* We now return to our regularly scheduled program */
193
194 agent = (td_thragent_t *)malloc(sizeof(td_thragent_t));
195 if (!agent) {
196 return TD_MALLOC;
197 }
198
199 agent->pid = proc_handle->pid;
200 *agent_out = agent;
201
202 return TD_OK;
203 }
204
205
206 td_err_e
td_ta_set_event(td_thragent_t const * agent,td_thr_events_t * events)207 td_ta_set_event(td_thragent_t const * agent, td_thr_events_t * events)
208 {
209 return TD_OK;
210 }
211
212
213 static td_thrhandle_t gEventMsgHandle;
214
215 static int
_event_getmsg_helper(td_thrhandle_t const * handle,void * bkpt_addr)216 _event_getmsg_helper(td_thrhandle_t const * handle, void * bkpt_addr)
217 {
218 #if defined(__arm__)
219 void* pc = (void *)ptrace(PTRACE_PEEKUSR, handle->tid, (void *)60 /* r15/pc */, NULL);
220 if (pc == bkpt_addr) {
221 // The hook function takes the id of the new thread as it's first param,
222 // so grab it from r0.
223 gEventMsgHandle.pid = ptrace(PTRACE_PEEKUSR, handle->tid, (void *)0 /* r0 */, NULL);
224 gEventMsgHandle.tid = gEventMsgHandle.pid;
225 return 0x42;
226 }
227 #elif defined(__i386__)
228 // Get the eip from offset 12*4 = 48 as defined in the struct
229 // user_regs_struct in user_32.h
230 void* pc = (void *)ptrace(PTRACE_PEEKUSR, handle->tid, (void *)48 /* eip */, NULL);
231 // FIXME - pc is a non-decremented breakpoint address, hence the
232 // addition of 1 on test. This seems to work for the thread hook
233 // function in libc.so but should be properly fixed.
234 if (pc == ((int)bkpt_addr + 1)) {
235 // The hook function takes the id of the new thread as it's first
236 // param, so grab it from ecx at offset 4 in struct user_regs_struct
237 // (using fastcall convention for x86)
238 gEventMsgHandle.pid = ptrace(PTRACE_PEEKUSR, handle->tid, (void *)4 /* ecx */, NULL);
239 gEventMsgHandle.tid = gEventMsgHandle.pid;
240 return 0x42;
241 }
242 #elif defined(__mips__)
243 void* pc = (void *)ptrace(PTRACE_PEEKUSR, handle->tid, (void *)(64*4) /* pc */, NULL);
244 if (pc == bkpt_addr) {
245 // The hook function takes the id of the new thread as it's first param,
246 // so grab it from a0
247 gEventMsgHandle.pid = ptrace(PTRACE_PEEKUSR, handle->tid, (void *)(4*4) /* a0 */, NULL);
248 gEventMsgHandle.tid = gEventMsgHandle.pid;
249 return 0x42;
250 }
251 #endif
252 return 0;
253 }
254
255 td_err_e
td_ta_event_getmsg(td_thragent_t const * agent,td_event_msg_t * event)256 td_ta_event_getmsg(td_thragent_t const * agent, td_event_msg_t * event)
257 {
258 td_err_e err;
259 void * bkpt_addr;
260
261 err = ps_pglobal_lookup(NULL, NULL, gSymbols[SYM_TD_CREATE], &bkpt_addr);
262 if (err) {
263 return err;
264 }
265
266 err = td_ta_thr_iter(agent, _event_getmsg_helper, bkpt_addr, 0, 0, NULL, 0);
267 if (err != 0x42) {
268 return TD_NOMSG;
269 }
270
271 event->event = TD_CREATE;
272 event->th_p = &gEventMsgHandle; // Nasty hack, but it's the only way!
273
274 return TD_OK;
275 }
276
277
278 td_err_e
td_thr_get_info(td_thrhandle_t const * handle,td_thrinfo_t * info)279 td_thr_get_info(td_thrhandle_t const * handle, td_thrinfo_t * info)
280 {
281 info->ti_tid = handle->tid;
282 info->ti_lid = handle->tid; // Our pthreads uses kernel ids for tids
283 info->ti_state = TD_THR_SLEEP; /* XXX this needs to be read from /proc/<pid>/task/<tid>.
284 This is only used to see if the thread is a zombie or not */
285 return TD_OK;
286 }
287
288
289 td_err_e
td_thr_event_enable(td_thrhandle_t const * handle,td_event_e event)290 td_thr_event_enable(td_thrhandle_t const * handle, td_event_e event)
291 {
292 // I don't think we need to do anything here...
293 return TD_OK;
294 }
295
296
297 td_err_e
td_ta_event_addr(td_thragent_t const * agent,td_event_e event,td_notify_t * notify_out)298 td_ta_event_addr(td_thragent_t const * agent, td_event_e event, td_notify_t * notify_out)
299 {
300 int32_t err;
301
302 /*
303 * This is nasty, ps_pglobal_lookup is implemented in gdbserver and looks up
304 * the symbol from it's cache, which is populated at start time with the
305 * symbols returned from td_symbol_list via calls back to the host.
306 */
307
308 switch (event) {
309 case TD_CREATE:
310 err = ps_pglobal_lookup(NULL, NULL, gSymbols[SYM_TD_CREATE], ¬ify_out->u.bptaddr);
311 if (err) {
312 return TD_NOEVENT;
313 }
314 return TD_OK;
315 }
316 return TD_NOEVENT;
317 }
318
319
320 td_err_e
td_ta_thr_iter(td_thragent_t const * agent,td_thr_iter_f * func,void * cookie,td_thr_state_e state,int32_t prio,sigset_t * sigmask,uint32_t user_flags)321 td_ta_thr_iter(td_thragent_t const * agent, td_thr_iter_f * func, void * cookie,
322 td_thr_state_e state, int32_t prio, sigset_t * sigmask, uint32_t user_flags)
323 {
324 td_err_e err = TD_OK;
325 char path[32];
326 DIR * dir;
327 struct dirent * entry;
328 td_thrhandle_t handle;
329
330 snprintf(path, sizeof(path), "/proc/%d/task/", agent->pid);
331 dir = opendir(path);
332 if (!dir) {
333 return TD_NOEVENT;
334 }
335
336 handle.pid = agent->pid;
337 while ((entry = readdir(dir)) != NULL) {
338 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
339 continue;
340 }
341 handle.tid = atoi(entry->d_name);
342 err = func(&handle, cookie);
343 if (err) {
344 break;
345 }
346 }
347
348 closedir(dir);
349
350 return err;
351 }
352
353