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 12extern int ps_pglobal_lookup (void *, const char *obj, const char *name, void **sym_addr); 13extern pid_t ps_getpid(struct ps_prochandle *ph); 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 */ 19enum { 20 SYM_TD_CREATE, 21 SYM_THREAD_LIST, 22 NUM_SYMS 23}; 24 25static char const * gSymbols[] = { 26 [SYM_TD_CREATE] = "_thread_created_hook", 27 NULL 28}; 29 30 31char const ** 32td_symbol_list(void) 33{ 34 return gSymbols; 35} 36 37 38td_err_e 39td_ta_new(struct ps_prochandle * proc_handle, td_thragent_t ** agent_out) 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 48 agent->pid = ps_getpid(proc_handle); 49 agent->ph = proc_handle; 50 *agent_out = agent; 51 52 return TD_OK; 53} 54 55 56td_err_e 57td_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 67td_err_e 68td_ta_set_event(td_thragent_t const * agent, td_thr_events_t * events) 69{ 70 return TD_OK; 71} 72 73 74/* NOTE: not used by gdb 7.0 */ 75static td_thrhandle_t gEventMsgHandle; 76 77/* NOTE: not used by gdb 7.0 */ 78 79static int 80_event_getmsg_helper(td_thrhandle_t const * handle, void * bkpt_addr) 81{ 82 void * pc; 83 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 } 102#elif defined(__arm__) 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 } 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 } 121#endif 122 return 0; 123} 124 125/* NOTE: not used by gdb 7.0 */ 126 127td_err_e 128td_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 150td_err_e 151td_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 160td_err_e 161td_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 171/* NOTE: not used by gdb 7.0 */ 172 173td_err_e 174td_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 181/* NOTE: not used by gdb 7.0 */ 182 183td_err_e 184td_ta_event_addr(td_thragent_t const * agent, td_event_e event, td_notify_t * notify_out) 185{ 186 int32_t err; 187 188 /* 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 206td_err_e 207td_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 215td_err_e 216td_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); 237 if (func(&handle, cookie) != 0) { 238 err = TD_DBERR; 239 break; 240 } 241 } 242 243 closedir(dir); 244 245 return err; 246} 247 248td_err_e 249td_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} 254