breakpoints.c revision 17bcdd38264a9adc161d6cdee834969efc803f57
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 (((struct breakpoint *)sbp)->enabled) 120 enable_breakpoint(proc, sbp); 121} 122 123void 124enable_all_breakpoints(Process *proc) 125{ 126 debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); 127 128 debug(1, "Enabling breakpoints for pid %u...", proc->pid); 129 if (proc->breakpoints) { 130 dict_apply_to_all(proc->breakpoints, enable_bp_cb, 131 proc); 132 } 133#ifdef __mips__ 134 { 135 /* 136 * I'm sure there is a nicer way to do this. We need to 137 * insert breakpoints _after_ the child has been started. 138 */ 139 struct library_symbol *sym; 140 struct library_symbol *new_sym; 141 sym=proc->list_of_symbols; 142 while(sym){ 143 void *addr= sym2addr(proc,sym); 144 if(!addr){ 145 sym=sym->next; 146 continue; 147 } 148 if(dict_find_entry(proc->breakpoints,addr)){ 149 sym=sym->next; 150 continue; 151 } 152 debug(2,"inserting bp %p %s",addr,sym->name); 153 new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); 154 memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); 155 new_sym->next=proc->list_of_symbols; 156 proc->list_of_symbols=new_sym; 157 insert_breakpoint(proc, addr, new_sym); 158 sym=sym->next; 159 } 160 } 161#endif 162} 163 164static void 165disable_bp_cb(void *addr, void *sbp, void *proc) 166{ 167 debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); 168 if (((struct breakpoint *)sbp)->enabled) 169 disable_breakpoint(proc, sbp); 170} 171 172void 173disable_all_breakpoints(Process *proc) { 174 debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); 175 assert(proc->leader == proc); 176 dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); 177} 178 179static void 180free_bp_cb(void *addr, void *sbp, void *data) { 181 debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp); 182 assert(sbp); 183 free(sbp); 184} 185 186static void 187entry_callback_hit(struct breakpoint *bp, struct Process *proc) 188{ 189 if (proc == NULL || proc->leader == NULL) 190 return; 191 delete_breakpoint(proc, bp->addr); // xxx 192 reinitialize_breakpoints(proc->leader); 193} 194 195int 196breakpoints_init(Process *proc, int enable) 197{ 198 debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); 199 if (proc->breakpoints) { /* let's remove that struct */ 200 dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); 201 dict_clear(proc->breakpoints); 202 proc->breakpoints = NULL; 203 } 204 205 /* Only the thread group leader should hold the breakpoints. 206 * (N.B. PID may be set to 0 temporarily when called by 207 * handle_exec). */ 208 assert(proc->leader == proc); 209 210 proc->breakpoints = dict_init(dict_key2hash_int, 211 dict_key_cmp_int); 212 213 destroy_library_symbol_chain(proc->list_of_symbols); 214 proc->list_of_symbols = NULL; 215 216 GElf_Addr entry; 217 if (options.libcalls && proc->filename) { 218 proc->list_of_symbols = read_elf(proc, &entry); 219 if (proc->list_of_symbols == NULL) { 220 fail: 221 /* XXX leak breakpoints */ 222 return -1; 223 } 224 225 if (opt_e) { 226 struct library_symbol **tmp1 = &proc->list_of_symbols; 227 while (*tmp1) { 228 struct opt_e_t *tmp2 = opt_e; 229 int keep = !opt_e_enable; 230 231 while (tmp2) { 232 if (!strcmp((*tmp1)->name, 233 tmp2->name)) { 234 keep = opt_e_enable; 235 } 236 tmp2 = tmp2->next; 237 } 238 if (!keep) { 239 *tmp1 = (*tmp1)->next; 240 } else { 241 tmp1 = &((*tmp1)->next); 242 } 243 } 244 } 245 } 246 247 struct breakpoint *entry_bp 248 = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); 249 if (entry_bp == NULL) { 250 fprintf(stderr, "fail!\n"); 251 goto fail; 252 } 253 254 static struct bp_callbacks entry_callbacks = { 255 .on_hit = entry_callback_hit, 256 }; 257 entry_bp->cbs = &entry_callbacks; 258 259 proc->callstack_depth = 0; 260 return 0; 261} 262 263void 264reinitialize_breakpoints(Process *proc) { 265 struct library_symbol *sym; 266 267 debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid); 268 269 sym = proc->list_of_symbols; 270 271 while (sym) { 272 if (sym->needs_init) { 273 insert_breakpoint(proc, sym2addr(proc, sym), sym, 1); 274 if (sym->needs_init && !sym->is_weak) { 275 fprintf(stderr, 276 "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", 277 sym->name, proc->filename); 278 exit(1); 279 } 280 } 281 sym = sym->next; 282 } 283} 284