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