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