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