breakpoints.c revision 61196a4a81e77322bf1f3dc609007f5d35a5103a
1#include "config.h" 2 3#include <stdlib.h> 4#include <string.h> 5#include <assert.h> 6 7#ifdef __powerpc__ 8#include <sys/ptrace.h> 9#endif 10 11#include "breakpoint.h" 12#include "common.h" 13 14void 15breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) 16{ 17 assert(bp != NULL); 18 if (bp->cbs != NULL && bp->cbs->on_hit != NULL) 19 (bp->cbs->on_hit) (bp, proc); 20} 21 22void 23breakpoint_on_destroy(struct breakpoint *bp) 24{ 25 assert(bp != NULL); 26 if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) 27 (bp->cbs->on_destroy) (bp); 28} 29 30/*****************************************************************************/ 31 32struct breakpoint * 33address2bpstruct(Process *proc, void *addr) 34{ 35 assert(proc != NULL); 36 assert(proc->breakpoints != NULL); 37 assert(proc->leader == proc); 38 debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr); 39 return dict_find_entry(proc->breakpoints, addr); 40} 41 42struct breakpoint * 43insert_breakpoint(Process *proc, void *addr, 44 struct library_symbol *libsym, int enable) 45{ 46 struct breakpoint *sbp; 47 48 Process * leader = proc->leader; 49 50 /* Only the group leader should be getting the breakpoints and 51 * thus have ->breakpoint initialized. */ 52 assert(leader != NULL); 53 assert(leader->breakpoints != NULL); 54 55#ifdef __arm__ 56 int thumb_mode = (int)addr & 1; 57 if (thumb_mode) 58 addr = (void *)((int)addr & ~1); 59#endif 60 61 debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL"); 62 debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); 63 64 if (!addr) 65 return NULL; 66 67 if (libsym) 68 libsym->needs_init = 0; 69 70 sbp = dict_find_entry(leader->breakpoints, addr); 71 if (sbp == NULL) { 72 sbp = calloc(1, sizeof(*sbp)); 73 if (sbp == NULL) { 74 return NULL; /* TODO FIXME XXX: error_mem */ 75 } 76 dict_enter(leader->breakpoints, addr, sbp); 77 sbp->addr = addr; 78 sbp->libsym = libsym; 79 } 80#ifdef __arm__ 81 sbp->thumb_mode = thumb_mode | proc->thumb_mode; 82 proc->thumb_mode = 0; 83#endif 84 sbp->enabled++; 85 if (sbp->enabled == 1 && enable) { 86 assert(proc->pid != 0); 87 enable_breakpoint(proc, sbp); 88 } 89 90 return sbp; 91} 92 93void 94delete_breakpoint(Process *proc, void *addr) 95{ 96 struct breakpoint *sbp; 97 98 debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); 99 100 Process * leader = proc->leader; 101 assert(leader != NULL); 102 103 sbp = dict_find_entry(leader->breakpoints, addr); 104 assert(sbp); /* FIXME: remove after debugging has been done. */ 105 /* This should only happen on out-of-memory conditions. */ 106 if (sbp == NULL) 107 return; 108 109 sbp->enabled--; 110 if (sbp->enabled == 0) 111 disable_breakpoint(proc, sbp); 112 assert(sbp->enabled >= 0); 113} 114 115static void 116enable_bp_cb(void *addr, void *sbp, void *proc) 117{ 118 debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); 119 if (((Breakpoint *)sbp)->enabled) { 120 enable_breakpoint(proc, sbp); 121 } 122} 123 124void 125enable_all_breakpoints(Process *proc) { 126 debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); 127#ifdef __powerpc__ 128 unsigned long a; 129 130 /* 131 * PPC HACK! (XXX FIXME TODO) 132 * If the dynamic linker hasn't populated the PLT then 133 * dont enable the breakpoints 134 */ 135 if (options.libcalls) { 136 a = ptrace(PTRACE_PEEKTEXT, proc->pid, 137 sym2addr(proc, proc->list_of_symbols), 138 0); 139 if (a == 0x0) 140 return; 141 } 142#endif 143 144 debug(1, "Enabling breakpoints for pid %u...", proc->pid); 145 if (proc->breakpoints) { 146 dict_apply_to_all(proc->breakpoints, enable_bp_cb, 147 proc); 148 } 149#ifdef __mips__ 150 { 151 /* 152 * I'm sure there is a nicer way to do this. We need to 153 * insert breakpoints _after_ the child has been started. 154 */ 155 struct library_symbol *sym; 156 struct library_symbol *new_sym; 157 sym=proc->list_of_symbols; 158 while(sym){ 159 void *addr= sym2addr(proc,sym); 160 if(!addr){ 161 sym=sym->next; 162 continue; 163 } 164 if(dict_find_entry(proc->breakpoints,addr)){ 165 sym=sym->next; 166 continue; 167 } 168 debug(2,"inserting bp %p %s",addr,sym->name); 169 new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); 170 memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); 171 new_sym->next=proc->list_of_symbols; 172 proc->list_of_symbols=new_sym; 173 insert_breakpoint(proc, addr, new_sym); 174 sym=sym->next; 175 } 176 } 177#endif 178} 179 180static void 181disable_bp_cb(void *addr, void *sbp, void *proc) 182{ 183 debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); 184 if (((Breakpoint *)sbp)->enabled) { 185 disable_breakpoint(proc, sbp); 186 } 187} 188 189void 190disable_all_breakpoints(Process *proc) { 191 debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); 192 assert(proc->leader == proc); 193 dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); 194} 195 196static void 197free_bp_cb(void *addr, void *sbp, void *data) { 198 debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp); 199 assert(sbp); 200 free(sbp); 201} 202 203static void 204entry_callback_hit(struct breakpoint *bp, struct Process *proc) 205{ 206 if (proc == NULL || proc->leader == NULL) 207 return; 208 delete_breakpoint(proc, bp->addr); // xxx 209 reinitialize_breakpoints(proc->leader); 210} 211 212int 213breakpoints_init(Process *proc, int enable) 214{ 215 debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); 216 if (proc->breakpoints) { /* let's remove that struct */ 217 dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); 218 dict_clear(proc->breakpoints); 219 proc->breakpoints = NULL; 220 } 221 222 /* Only the thread group leader should hold the breakpoints. 223 * (N.B. PID may be set to 0 temporarily when called by 224 * handle_exec). */ 225 assert(proc->leader == proc); 226 227 proc->breakpoints = dict_init(dict_key2hash_int, 228 dict_key_cmp_int); 229 230 destroy_library_symbol_chain(proc->list_of_symbols); 231 proc->list_of_symbols = NULL; 232 233 GElf_Addr entry; 234 if (options.libcalls && proc->filename) { 235 proc->list_of_symbols = read_elf(proc, &entry); 236 if (proc->list_of_symbols == NULL) { 237 fail: 238 /* XXX leak breakpoints */ 239 return -1; 240 } 241 242 if (opt_e) { 243 struct library_symbol **tmp1 = &proc->list_of_symbols; 244 while (*tmp1) { 245 struct opt_e_t *tmp2 = opt_e; 246 int keep = !opt_e_enable; 247 248 while (tmp2) { 249 if (!strcmp((*tmp1)->name, 250 tmp2->name)) { 251 keep = opt_e_enable; 252 } 253 tmp2 = tmp2->next; 254 } 255 if (!keep) { 256 *tmp1 = (*tmp1)->next; 257 } else { 258 tmp1 = &((*tmp1)->next); 259 } 260 } 261 } 262 } 263 264 struct breakpoint *entry_bp 265 = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); 266 if (entry_bp == NULL) { 267 fprintf(stderr, "fail!\n"); 268 goto fail; 269 } 270 271 static struct bp_callbacks entry_callbacks = { 272 .on_hit = entry_callback_hit, 273 }; 274 entry_bp->cbs = &entry_callbacks; 275 276 proc->callstack_depth = 0; 277 return 0; 278} 279 280void 281reinitialize_breakpoints(Process *proc) { 282 struct library_symbol *sym; 283 284 debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid); 285 286 sym = proc->list_of_symbols; 287 288 while (sym) { 289 if (sym->needs_init) { 290 insert_breakpoint(proc, sym2addr(proc, sym), sym, 1); 291 if (sym->needs_init && !sym->is_weak) { 292 fprintf(stderr, 293 "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", 294 sym->name, proc->filename); 295 exit(1); 296 } 297 } 298 sym = sym->next; 299 } 300} 301