| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 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 |  | 
|  | 12 | extern int ps_pglobal_lookup (void *, const char *obj, const char *name, void **sym_addr); | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 13 | extern pid_t ps_getpid(struct ps_prochandle *ph); | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 14 |  | 
|  | 15 | /* | 
|  | 16 | * This is the list of "special" symbols we care about whose addresses are | 
|  | 17 | * cached by gdbserver from the host at init time. | 
|  | 18 | */ | 
|  | 19 | enum { | 
|  | 20 | SYM_TD_CREATE, | 
|  | 21 | SYM_THREAD_LIST, | 
|  | 22 | NUM_SYMS | 
|  | 23 | }; | 
|  | 24 |  | 
|  | 25 | static char const * gSymbols[] = { | 
|  | 26 | [SYM_TD_CREATE] = "_thread_created_hook", | 
|  | 27 | NULL | 
|  | 28 | }; | 
|  | 29 |  | 
|  | 30 |  | 
|  | 31 | char const ** | 
|  | 32 | td_symbol_list(void) | 
|  | 33 | { | 
|  | 34 | return gSymbols; | 
|  | 35 | } | 
|  | 36 |  | 
|  | 37 |  | 
|  | 38 | td_err_e | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 39 | td_ta_new(struct ps_prochandle * proc_handle, td_thragent_t ** agent_out) | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 40 | { | 
|  | 41 | td_thragent_t * agent; | 
|  | 42 |  | 
|  | 43 | agent = (td_thragent_t *)malloc(sizeof(td_thragent_t)); | 
|  | 44 | if (!agent) { | 
|  | 45 | return TD_MALLOC; | 
|  | 46 | } | 
|  | 47 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 48 | agent->pid = ps_getpid(proc_handle); | 
|  | 49 | agent->ph = proc_handle; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 50 | *agent_out = agent; | 
|  | 51 |  | 
|  | 52 | return TD_OK; | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 |  | 
|  | 56 | td_err_e | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 57 | td_ta_delete(td_thragent_t * ta) | 
|  | 58 | { | 
|  | 59 | free(ta); | 
|  | 60 | // FIXME: anything else to do? | 
|  | 61 | return TD_OK; | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 |  | 
|  | 65 | /* NOTE: not used by gdb 7.0 */ | 
|  | 66 |  | 
|  | 67 | td_err_e | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 68 | td_ta_set_event(td_thragent_t const * agent, td_thr_events_t * events) | 
|  | 69 | { | 
|  | 70 | return TD_OK; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 74 | /* NOTE: not used by gdb 7.0 */ | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 75 | static td_thrhandle_t gEventMsgHandle; | 
|  | 76 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 77 | /* NOTE: not used by gdb 7.0 */ | 
|  | 78 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 79 | static int | 
|  | 80 | _event_getmsg_helper(td_thrhandle_t const * handle, void * bkpt_addr) | 
|  | 81 | { | 
|  | 82 | void * pc; | 
|  | 83 |  | 
| Bruce Beare | 8e551a6 | 2011-03-28 09:47:35 -0700 | [diff] [blame] | 84 | #ifdef __i386__ | 
|  | 85 | /* Get the eip from offset 12*4 = 48 as defined in the struct | 
|  | 86 | * user_regs_struct in user_32.h | 
|  | 87 | */ | 
|  | 88 | pc = (void *)ptrace(PTRACE_PEEKUSR, handle->tid, (void *)48 /* eip */, NULL); | 
|  | 89 | /* FIXME - pc is a non-decremented breakpoint address, hence the | 
|  | 90 | * addition of 1 on test.  This seems to work for the thread hook | 
|  | 91 | * function in libc.so but should be properly fixed. | 
|  | 92 | */ | 
|  | 93 | if (pc == ((int)bkpt_addr + 1)) { | 
|  | 94 | /* The hook function takes the id of the new thread as it's first | 
|  | 95 | * param, so grab it from ecx at offset 4 in struct user_regs_struct | 
|  | 96 | * (using fastcall convention for x86) | 
|  | 97 | */ | 
|  | 98 | gEventMsgHandle.pid = ptrace(PTRACE_PEEKUSR, handle->tid, (void *)4 /* ecx */, NULL); | 
|  | 99 | gEventMsgHandle.tid = gEventMsgHandle.pid; | 
|  | 100 | return 0x42; | 
|  | 101 | } | 
| Chris Dearman | 726800e | 2012-05-01 17:45:53 -0700 | [diff] [blame] | 102 | #elif defined(__arm__) | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 103 | pc = (void *)ptrace(PTRACE_PEEKUSR, handle->tid, (void *)60 /* r15/pc */, NULL); | 
|  | 104 |  | 
|  | 105 | if (pc == bkpt_addr) { | 
|  | 106 | // The hook function takes the id of the new thread as it's first param, | 
|  | 107 | // so grab it from r0. | 
|  | 108 | gEventMsgHandle.pid = ptrace(PTRACE_PEEKUSR, handle->tid, (void *)0 /* r0 */, NULL); | 
|  | 109 | gEventMsgHandle.tid = gEventMsgHandle.pid; | 
|  | 110 | return 0x42; | 
|  | 111 | } | 
| Chris Dearman | 726800e | 2012-05-01 17:45:53 -0700 | [diff] [blame] | 112 | #elif defined(__mips__) | 
|  | 113 | pc = (void *)ptrace(PTRACE_PEEKUSR, handle->tid, (void *)(64*4) /* pc */, NULL); | 
|  | 114 | if (pc == bkpt_addr) { | 
|  | 115 | // The hook function takes the id of the new thread as it's first param, | 
|  | 116 | // so grab it from a0 | 
|  | 117 | gEventMsgHandle.pid = ptrace(PTRACE_PEEKUSR, handle->tid, (void *)(4*4) /* a0 */, NULL); | 
|  | 118 | gEventMsgHandle.tid = gEventMsgHandle.pid; | 
|  | 119 | return 0x42; | 
|  | 120 | } | 
| Bruce Beare | 8e551a6 | 2011-03-28 09:47:35 -0700 | [diff] [blame] | 121 | #endif | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 122 | return 0; | 
|  | 123 | } | 
|  | 124 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 125 | /* NOTE: not used by gdb 7.0 */ | 
|  | 126 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 127 | td_err_e | 
|  | 128 | td_ta_event_getmsg(td_thragent_t const * agent, td_event_msg_t * event) | 
|  | 129 | { | 
|  | 130 | td_err_e err; | 
|  | 131 | void * bkpt_addr; | 
|  | 132 |  | 
|  | 133 | err = ps_pglobal_lookup(NULL, NULL, gSymbols[SYM_TD_CREATE], &bkpt_addr); | 
|  | 134 | if (err) { | 
|  | 135 | return err; | 
|  | 136 | } | 
|  | 137 |  | 
|  | 138 | err = td_ta_thr_iter(agent, _event_getmsg_helper, bkpt_addr, 0, 0, NULL, 0); | 
|  | 139 | if (err != 0x42) { | 
|  | 140 | return TD_NOMSG; | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | event->event = TD_CREATE; | 
|  | 144 | event->th_p = &gEventMsgHandle; // Nasty hack, but it's the only way! | 
|  | 145 |  | 
|  | 146 | return TD_OK; | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 |  | 
|  | 150 | td_err_e | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 151 | td_ta_map_lwp2thr(td_thragent_t const * agent, lwpid_t lwpid, | 
|  | 152 | td_thrhandle_t *th) | 
|  | 153 | { | 
|  | 154 | th->pid = ps_getpid(agent->ph); | 
|  | 155 | th->tid = lwpid; | 
|  | 156 | return TD_OK; | 
|  | 157 | } | 
|  | 158 |  | 
|  | 159 |  | 
|  | 160 | td_err_e | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 161 | td_thr_get_info(td_thrhandle_t const * handle, td_thrinfo_t * info) | 
|  | 162 | { | 
|  | 163 | info->ti_tid = handle->tid; | 
|  | 164 | info->ti_lid = handle->tid; // Our pthreads uses kernel ids for tids | 
|  | 165 | info->ti_state = TD_THR_SLEEP; /* XXX this needs to be read from /proc/<pid>/task/<tid>. | 
|  | 166 | This is only used to see if the thread is a zombie or not */ | 
|  | 167 | return TD_OK; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 171 | /* NOTE: not used by gdb 7.0 */ | 
|  | 172 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 173 | td_err_e | 
|  | 174 | td_thr_event_enable(td_thrhandle_t const * handle, td_event_e event) | 
|  | 175 | { | 
|  | 176 | // I don't think we need to do anything here... | 
|  | 177 | return TD_OK; | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 181 | /* NOTE: not used by gdb 7.0 */ | 
|  | 182 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 183 | td_err_e | 
|  | 184 | td_ta_event_addr(td_thragent_t const * agent, td_event_e event, td_notify_t * notify_out) | 
|  | 185 | { | 
|  | 186 | int32_t err; | 
|  | 187 |  | 
| Bruce Beare | 8e551a6 | 2011-03-28 09:47:35 -0700 | [diff] [blame] | 188 | /* | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 189 | * This is nasty, ps_pglobal_lookup is implemented in gdbserver and looks up | 
|  | 190 | * the symbol from it's cache, which is populated at start time with the | 
|  | 191 | * symbols returned from td_symbol_list via calls back to the host. | 
|  | 192 | */ | 
|  | 193 |  | 
|  | 194 | switch (event) { | 
|  | 195 | case TD_CREATE: | 
|  | 196 | err = ps_pglobal_lookup(NULL, NULL, gSymbols[SYM_TD_CREATE], ¬ify_out->u.bptaddr); | 
|  | 197 | if (err) { | 
|  | 198 | return TD_NOEVENT; | 
|  | 199 | } | 
|  | 200 | return TD_OK; | 
|  | 201 | } | 
|  | 202 | return TD_NOEVENT; | 
|  | 203 | } | 
|  | 204 |  | 
|  | 205 |  | 
|  | 206 | td_err_e | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 207 | td_ta_clear_event(const td_thragent_t * ta_arg, td_thr_events_t * event) | 
|  | 208 | { | 
|  | 209 | /* Given that gdb 7.0 doesn't use thread events, | 
|  | 210 | there's nothing we need to do here.  */ | 
|  | 211 | return TD_OK; | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 |  | 
|  | 215 | td_err_e | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 216 | td_ta_thr_iter(td_thragent_t const * agent, td_thr_iter_f * func, void * cookie, | 
|  | 217 | td_thr_state_e state, int32_t prio, sigset_t * sigmask, uint32_t user_flags) | 
|  | 218 | { | 
|  | 219 | td_err_e err = TD_OK; | 
|  | 220 | char path[32]; | 
|  | 221 | DIR * dir; | 
|  | 222 | struct dirent * entry; | 
|  | 223 | td_thrhandle_t handle; | 
|  | 224 |  | 
|  | 225 | snprintf(path, sizeof(path), "/proc/%d/task/", agent->pid); | 
|  | 226 | dir = opendir(path); | 
|  | 227 | if (!dir) { | 
|  | 228 | return TD_NOEVENT; | 
|  | 229 | } | 
|  | 230 |  | 
|  | 231 | handle.pid = agent->pid; | 
|  | 232 | while ((entry = readdir(dir)) != NULL) { | 
|  | 233 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { | 
|  | 234 | continue; | 
|  | 235 | } | 
|  | 236 | handle.tid = atoi(entry->d_name); | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 237 | if (func(&handle, cookie) != 0) { | 
|  | 238 | err = TD_DBERR; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 239 | break; | 
|  | 240 | } | 
|  | 241 | } | 
|  | 242 |  | 
|  | 243 | closedir(dir); | 
|  | 244 |  | 
|  | 245 | return err; | 
|  | 246 | } | 
|  | 247 |  | 
| David 'Digit' Turner | 3d4edfc | 2010-03-19 16:01:28 -0700 | [diff] [blame] | 248 | td_err_e | 
|  | 249 | td_thr_tls_get_addr(const td_thrhandle_t * th, | 
|  | 250 | psaddr_t map_address, size_t offset, psaddr_t * address) | 
|  | 251 | { | 
|  | 252 | return TD_NOAPLIC; // FIXME: TODO | 
|  | 253 | } |