void delete_breakpoint(struct process *proc, void *addr) { debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); struct process *leader = proc->leader; assert(leader != NULL); struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); assert(sbp != NULL); /* This should only happen on out-of-memory conditions. */ if (sbp == NULL) return; if (breakpoint_turn_off(sbp, proc) < 0) { fprintf(stderr, "Couldn't turn off the breakpoint %s@%p\n", breakpoint_name(sbp), sbp->addr); return; } if (sbp->enabled == 0) { proc_remove_breakpoint(leader, sbp); breakpoint_destroy(sbp); free(sbp); } }
void insert_breakpoint(Process *proc, void *addr, struct library_symbol *libsym) { Breakpoint *sbp; 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) return; if (libsym) libsym->needs_init = 0; sbp = dict_find_entry(proc->breakpoints, addr); if (!sbp) { sbp = calloc(1, sizeof(Breakpoint)); if (!sbp) { return; /* TODO FIXME XXX: error_mem */ } dict_enter(proc->breakpoints, addr, sbp); sbp->addr = addr; sbp->libsym = libsym; } #ifdef __arm__ sbp->thumb_mode = proc->thumb_mode; proc->thumb_mode = 0; #endif sbp->enabled++; if (sbp->enabled == 1 && proc->pid) enable_breakpoint(proc->pid, sbp); }
const char * my_demangle(const char *function_name) { const char *tmp, *fn_copy; #ifdef USE_CXA_DEMANGLE extern char *__cxa_demangle(const char *, char *, size_t *, int *); #endif debug(DEBUG_FUNCTION, "my_demangle(name=%s)", function_name); if (!d) d = dict_init(dict_key2hash_string, dict_key_cmp_string); tmp = dict_find_entry(d, (void *)function_name); if (!tmp) { fn_copy = strdup(function_name); #ifdef HAVE_LIBIBERTY tmp = cplus_demangle(function_name, DMGL_ANSI | DMGL_PARAMS); #elif defined USE_CXA_DEMANGLE int status = 0; tmp = __cxa_demangle(function_name, NULL, NULL, &status); #endif if (!tmp) tmp = fn_copy; if (tmp) dict_enter(d, (void *)fn_copy, (void *)tmp); } return tmp; }
void enable_all_breakpoints(Process *proc) { debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); if (proc->breakpoints_enabled <= 0) { #ifdef __powerpc__ unsigned long a; /* * PPC HACK! (XXX FIXME TODO) * If the dynamic linker hasn't populated the PLT then * dont enable the breakpoints */ if (options.libcalls) { a = ptrace(PTRACE_PEEKTEXT, proc->pid, sym2addr(proc, proc->list_of_symbols), 0); if (a == 0x0) return; } #endif 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 } proc->breakpoints_enabled = 1; }
struct breakpoint * address2bpstruct(struct 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); }
void delete_breakpoint(Process *proc, void *addr) { Breakpoint *sbp; debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); sbp = dict_find_entry(proc->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->pid, sbp); assert(sbp->enabled >= 0); }
struct breakpoint * insert_breakpoint(struct process *proc, void *addr, struct library_symbol *libsym) { struct 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"); assert(addr != 0); /* XXX what we need to do instead is have a list of * breakpoints that are enabled at this address. The * following works if every breakpoint is the same and there's * no extra data, but that doesn't hold anymore. For now it * will suffice, about the only realistic case where we need * to have more than one breakpoint per address is return from * a recursive library call. */ 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) { free(sbp); return NULL; } if (proc_add_breakpoint(leader, sbp) < 0) { fail: breakpoint_destroy(sbp); free(sbp); return NULL; } } if (breakpoint_turn_on(sbp, proc) < 0) { proc_remove_breakpoint(leader, sbp); goto fail; } return sbp; }
int proc_add_breakpoint(struct Process *proc, struct breakpoint *bp) { debug(DEBUG_FUNCTION, "proc_add_breakpoint(pid=%d, %s@%p)", proc->pid, breakpoint_name(bp), bp->addr); check_leader(proc); /* XXX We might merge bp->libsym instead of the following * assert, but that's not necessary right now. Read the * comment in breakpoint_for_symbol. */ assert(dict_find_entry(proc->breakpoints, bp->addr) == NULL); if (dict_enter(proc->breakpoints, bp->addr, bp) < 0) { fprintf(stderr, "couldn't enter breakpoint %s@%p to dictionary: %s\n", breakpoint_name(bp), bp->addr, strerror(errno)); return -1; } return 0; }
static void * breakpoint_clone(void * bp, void * data) { Breakpoint * b; Dict * map = data; debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); b = malloc(sizeof(Breakpoint)); if (!b) { perror("malloc()"); exit(1); } memcpy(b, bp, sizeof(Breakpoint)); if (b->libsym != NULL) { struct library_symbol * sym = dict_find_entry(map, b->libsym); if (b->libsym == NULL) { fprintf(stderr, "Can't find cloned symbol %s.\n", b->libsym->name); return NULL; } b->libsym = sym; } return b; }
Breakpoint * address2bpstruct(Process *proc, void *addr) { debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr); return dict_find_entry(proc->breakpoints, addr); }
static void handle_breakpoint(Event *event) { int i, j; Breakpoint *sbp; Process *leader = event->proc->leader; /* The leader has terminated. */ if (leader == NULL) { continue_process(event->proc->pid); return; } debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); #ifdef __powerpc__ /* Need to skip following NOP's to prevent a fake function from being stacked. */ long stub_addr = (long) get_count_register(event->proc); Breakpoint *stub_bp = NULL; char nop_instruction[] = PPC_NOP; stub_bp = address2bpstruct(leader, event->e_un.brk_addr); if (stub_bp) { unsigned char *bp_instruction = stub_bp->orig_value; if (memcmp(bp_instruction, nop_instruction, PPC_NOP_LENGTH) == 0) { if (stub_addr != (long) event->e_un.brk_addr) { set_instruction_pointer (event->proc, event->e_un.brk_addr + 4); continue_process(event->proc->pid); return; } } } #endif for (i = event->proc->callstack_depth - 1; i >= 0; i--) { if (event->e_un.brk_addr == event->proc->callstack[i].return_addr) { #ifdef __powerpc__ /* * PPC HACK! (XXX FIXME TODO) * The PLT gets modified during the first call, * so be sure to re-enable the breakpoint. */ unsigned long a; struct library_symbol *libsym = event->proc->callstack[i].c_un.libfunc; void *addr = sym2addr(event->proc, libsym); if (libsym->plt_type != LS_TOPLT_POINT) { unsigned char break_insn[] = BREAKPOINT_VALUE; sbp = address2bpstruct(leader, addr); assert(sbp); a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, addr); if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { sbp->enabled--; insert_breakpoint(event->proc, addr, libsym, 1); } } else { sbp = dict_find_entry(leader->breakpoints, addr); /* On powerpc, the breakpoint address may end up being actual entry point of the library symbol, not the PLT address we computed. In that case, sbp is NULL. */ if (sbp == NULL || addr != sbp->addr) { insert_breakpoint(event->proc, addr, libsym, 1); } } #elif defined(__mips__) void *addr = NULL; struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; struct library_symbol *new_sym; assert(sym); addr = sym2addr(event->proc, sym); sbp = dict_find_entry(leader->breakpoints, addr); if (sbp) { if (addr != sbp->addr) { insert_breakpoint(event->proc, addr, sym, 1); } } else { new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); new_sym->next = leader->list_of_symbols; leader->list_of_symbols = new_sym; insert_breakpoint(event->proc, addr, new_sym, 1); } #endif for (j = event->proc->callstack_depth - 1; j > i; j--) { callstack_pop(event->proc); } if (event->proc->state != STATE_IGNORED) { if (opt_T || options.summary) { calc_time_spent(event->proc); } } event->proc->return_addr = event->e_un.brk_addr; if (event->proc->state != STATE_IGNORED) { mock_return(LT_TOF_FUNCTIONR, event->proc, event->proc->callstack[i].c_un.libfunc->name); output_right(LT_TOF_FUNCTIONR, event->proc, event->proc->callstack[i].c_un.libfunc->name); } callstack_pop(event->proc); sbp = address2bpstruct(leader, event->e_un.brk_addr); continue_after_breakpoint(event->proc, sbp); return; } } if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { if (sbp->libsym == NULL) { continue_after_breakpoint(event->proc, sbp); return; } if (strcmp(sbp->libsym->name, "") == 0) { debug(DEBUG_PROCESS, "Hit _dl_debug_state breakpoint!\n"); arch_check_dbg(leader); } if (event->proc->state != STATE_IGNORED) { event->proc->stack_pointer = get_stack_pointer(event->proc); event->proc->return_addr = get_return_addr(event->proc, event->proc->stack_pointer); callstack_push_symfunc(event->proc, sbp->libsym); output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); } #ifdef PLT_REINITALISATION_BP if (event->proc->need_to_reinitialize_breakpoints && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == 0)) reinitialize_breakpoints(leader); #endif continue_after_breakpoint(event->proc, sbp); return; } if (event->proc->state != STATE_IGNORED && !options.no_plt) { output_line(event->proc, "unexpected breakpoint at %p", (void *)event->e_un.brk_addr); } continue_process(event->proc->pid); }
static int breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc) { arch_addr_t bp_addr; assert(proc->leader == proc); /* Don't enable latent or delayed symbols. */ if (libsym->latent || libsym->delayed) { debug(DEBUG_FUNCTION, "delayed and/or latent breakpoint pid=%d, %s@%p", proc->pid, libsym->name, libsym->enter_addr); return 0; } bp_addr = sym2addr(proc, libsym); /* If there is an artificial breakpoint on the same address, * its libsym will be NULL, and we can smuggle our libsym * there. That artificial breakpoint is there presumably for * the callbacks, which we don't touch. If there is a real * breakpoint, then this is a bug. ltrace-elf.c should filter * symbols and ignore extra symbol aliases. * * The other direction is more complicated and currently not * supported. If a breakpoint has custom callbacks, it might * be also custom-allocated, and we would really need to swap * the two: delete the one now in the dictionary, swap values * around, and put the new breakpoint back in. */ struct breakpoint *bp = dict_find_entry(proc->breakpoints, bp_addr); if (bp != NULL) { /* MIPS backend makes duplicate requests. This is * likely a bug in the backend. Currently there's no * point assigning more than one symbol to a * breakpoint, because when it hits, we won't know * what to print out. But it's easier to fix it here * before someone who understands MIPS has the time to * look into it. So turn the sanity check off on * MIPS. References: * * http://lists.alioth.debian.org/pipermail/ltrace-devel/2012-November/000764.html * http://lists.alioth.debian.org/pipermail/ltrace-devel/2012-November/000770.html */ #ifndef __mips__ assert(bp->libsym == NULL); bp->libsym = libsym; #endif return 0; } bp = malloc(sizeof(*bp)); if (bp == NULL || breakpoint_init(bp, proc, bp_addr, libsym) < 0) { fail: free(bp); return -1; } if (proc_add_breakpoint(proc, bp) < 0) { breakpoint_destroy(bp); goto fail; } if (breakpoint_turn_on(bp, proc) < 0) { proc_remove_breakpoint(proc, bp); breakpoint_destroy(bp); goto fail; } return 0; }