#include "config.h" #include #include #include #include #include #ifdef __powerpc__ #include #endif #include "breakpoint.h" #include "common.h" #include "proc.h" #include "library.h" void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) { assert(bp != NULL); if (bp->cbs != NULL && bp->cbs->on_hit != NULL) (bp->cbs->on_hit)(bp, proc); } void breakpoint_on_continue(struct breakpoint *bp, struct Process *proc) { assert(bp != NULL); if (bp->cbs != NULL && bp->cbs->on_continue != NULL) (bp->cbs->on_continue)(bp, proc); else continue_after_breakpoint(proc, bp); } /*****************************************************************************/ struct breakpoint * address2bpstruct(Process *proc, void *addr) { assert(proc != NULL); assert(proc->breakpoints != NULL); assert(proc->leader == proc); debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr); return dict_find_entry(proc->breakpoints, addr); } #ifndef ARCH_HAVE_BREAKPOINT_DATA int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) { return 0; } void arch_breakpoint_destroy(struct breakpoint *sbp) { } #endif int breakpoint_init(struct breakpoint *bp, struct Process *proc, target_address_t addr, struct library_symbol *libsym) { bp->cbs = NULL; bp->addr = addr; memset(bp->orig_value, 0, sizeof(bp->orig_value)); bp->enabled = 0; bp->libsym = libsym; return arch_breakpoint_init(proc, bp); } void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs) { if (bp->cbs != NULL) assert(bp->cbs == NULL); bp->cbs = cbs; } void breakpoint_destroy(struct breakpoint *bp) { if (bp == NULL) return; /* XXX I'm not convinced that we need on_destroy. We already * have arch_breakpoint_destroy, which is necessary as a * counterpart of arch_breakpoint_init in any case. */ if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) (bp->cbs->on_destroy) (bp); arch_breakpoint_destroy(bp); } struct breakpoint * insert_breakpoint(struct Process *proc, void *addr, struct library_symbol *libsym) { Process *leader = proc->leader; /* Only the group leader should be getting the breakpoints and * thus have ->breakpoint initialized. */ assert(leader != NULL); assert(leader->breakpoints != NULL); debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL"); debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); if (addr == 0) { /* XXX we need a better way to deal with this. For * now, just abuse errno to carry the error * information. */ errno = EINVAL; return NULL; } struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); if (sbp == NULL) { sbp = malloc(sizeof(*sbp)); if (sbp == NULL || breakpoint_init(sbp, proc, addr, libsym) < 0 || dict_enter(leader->breakpoints, addr, sbp) < 0) { free(sbp); return NULL; } } sbp->enabled++; if (sbp->enabled == 1) { assert(proc->pid != 0); enable_breakpoint(proc, sbp); } return sbp; } void delete_breakpoint(Process *proc, void *addr) { struct breakpoint *sbp; debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); Process * leader = proc->leader; assert(leader != NULL); sbp = dict_find_entry(leader->breakpoints, addr); assert(sbp); /* FIXME: remove after debugging has been done. */ /* This should only happen on out-of-memory conditions. */ if (sbp == NULL) return; sbp->enabled--; if (sbp->enabled == 0) disable_breakpoint(proc, sbp); assert(sbp->enabled >= 0); } const char * breakpoint_name(const struct breakpoint *bp) { assert(bp != NULL); return bp->libsym != NULL ? bp->libsym->name : NULL; } static void enable_bp_cb(void *addr, void *sbp, void *proc) { debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); if (((struct breakpoint *)sbp)->enabled) enable_breakpoint(proc, sbp); } void enable_all_breakpoints(Process *proc) { debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); debug(1, "Enabling breakpoints for pid %u...", proc->pid); if (proc->breakpoints) { dict_apply_to_all(proc->breakpoints, enable_bp_cb, proc); } #ifdef __mips__ { /* * I'm sure there is a nicer way to do this. We need to * insert breakpoints _after_ the child has been started. */ struct library_symbol *sym; struct library_symbol *new_sym; sym=proc->list_of_symbols; while(sym){ void *addr= sym2addr(proc,sym); if(!addr){ sym=sym->next; continue; } if(dict_find_entry(proc->breakpoints,addr)){ sym=sym->next; continue; } debug(2,"inserting bp %p %s",addr,sym->name); new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); new_sym->next=proc->list_of_symbols; proc->list_of_symbols=new_sym; insert_breakpoint(proc, addr, new_sym); sym=sym->next; } } #endif } static void disable_bp_cb(void *addr, void *sbp, void *proc) { debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); if (((struct breakpoint *)sbp)->enabled) disable_breakpoint(proc, sbp); } void disable_all_breakpoints(Process *proc) { debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); assert(proc->leader == proc); dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); } static void entry_callback_hit(struct breakpoint *bp, struct Process *proc) { fprintf(stderr, "entry_callback_hit\n"); if (proc == NULL || proc->leader == NULL) return; delete_breakpoint(proc, bp->addr); // xxx enable_all_breakpoints(proc); linkmap_init(proc); } int breakpoints_init(Process *proc, int enable) { fprintf(stderr, "breakpoints_init %d enable=%d\n", proc->pid, enable); debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); /* XXX breakpoint dictionary should be initialized * outside. Here we just put in breakpoints. */ assert(proc->breakpoints != NULL); /* Only the thread group leader should hold the breakpoints. */ assert(proc->leader == proc); if (options.libcalls && proc->filename) { struct library *lib = ltelf_read_main_binary(proc, proc->filename); switch (lib != NULL) { fail: proc_remove_library(proc, lib); library_destroy(lib); case 0: return -1; } proc_add_library(proc, lib); fprintf(stderr, "note: symbols in %s were not filtered.\n", lib->name); struct breakpoint *entry_bp = insert_breakpoint(proc, lib->entry, NULL); if (entry_bp == NULL) { error(0, errno, "couldn't insert entry breakpoint"); goto fail; } fprintf(stderr, "setting entry_callbacks by hand, fix it\n"); static struct bp_callbacks entry_callbacks = { .on_hit = entry_callback_hit, }; entry_bp->cbs = &entry_callbacks; } proc->callstack_depth = 0; return 0; }