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; }
static enum callback_status cb_enable_breakpoint_sym(struct library_symbol *libsym, void *data) { struct Process *proc = data; arch_addr_t bp_addr; if (!libsym->arch.gotonly) return CBS_CONT; /* Update state. */ bp_addr = sym2addr(proc, libsym); /* XXX The cast to uintptr_t should be removed when * arch_addr_t becomes integral type. keywords: double cast. */ libsym->arch.resolved_addr = (uintptr_t) bp_addr; if (libsym->arch.resolved_addr == 0) /* FIXME: What does this mean? */ return CBS_CONT; libsym->arch.type = MIPS_PLT_RESOLVED; /* Now, activate the symbol causing a breakpoint to be added. */ if (proc_activate_delayed_symbol(proc, libsym) < 0) { fprintf(stderr, "Failed to activate delayed sym %s\n", libsym->name); } return CBS_CONT; }
int linkmap_init(Process *proc, struct ltelf *lte) { void *dbg_addr = NULL, *dyn_addr = GELF_ADDR_CAST(lte->dyn_addr); struct r_debug *rdbg = NULL; struct cb_data data; debug(DEBUG_FUNCTION, "linkmap_init()"); if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, &dbg_addr) == -1) { debug(2, "Couldn't find debug structure!"); return -1; } proc->debug = dbg_addr; if (!(rdbg = load_debug_struct(proc))) { debug(2, "No debug structure or no memory to allocate one!"); return -1; } data.lte = lte; add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0); insert_breakpoint(proc, sym2addr(proc, library_symbols), library_symbols, 1); crawl_linkmap(proc, rdbg, hook_libdl_cb, &data); free(rdbg); return 0; }
enum plt_status arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, const char *a_name, GElf_Rela *rela, size_t ndx, struct library_symbol **ret) { char *name = NULL; int sym_index = ndx + lte->arch.mips_gotsym; struct library_symbol *libsym = malloc(sizeof(*libsym)); if (libsym == NULL) return plt_fail; GElf_Addr addr = arch_plt_sym_val(lte, sym_index, 0); name = strdup(a_name); if (name == NULL) { fprintf(stderr, "%s: failed %s(%#llx): %s\n", __func__, name, addr, strerror(errno)); goto fail; } /* XXX The double cast should be removed when * arch_addr_t becomes integral type. */ if (library_symbol_init(libsym, (arch_addr_t) (uintptr_t) addr, name, 1, LS_TOPLT_EXEC) < 0) { fprintf(stderr, "%s: failed %s : %llx\n", __func__, name, addr); goto fail; } arch_addr_t bp_addr = sym2addr(proc, libsym); /* XXX This cast should be removed when * arch_addr_t becomes integral type. keywords: double cast. */ libsym->arch.stub_addr = (uintptr_t) bp_addr; if (bp_addr == 0) { /* Function pointers without PLT entries. */ libsym->plt_type = LS_TOPLT_NONE; libsym->arch.gotonly = 1; libsym->arch.type = MIPS_PLT_UNRESOLVED; /* Delay breakpoint activation until the symbol gets * resolved. */ libsym->delayed = 1; } else if (mips_elf_is_cpic(lte->ehdr.e_flags)) { libsym->arch.pltalways = 1; } *ret = libsym; return plt_ok; fail: free(name); free(libsym); return plt_fail; }
static void linkmap_add_cb(void *data) { //const char *lib_name, ElfW(Addr) addr) { size_t i = 0; struct cb_data *lm_add = data; struct ltelf lte; struct opt_x_t *xptr; debug(DEBUG_FUNCTION, "linkmap_add_cb"); /* XXX iterate through library[i]'s to see if this lib is in the list. if not, add it */ for(;i < library_num;i++) { if (strcmp(library[i], lm_add->lib_name) == 0) { /* found it, so its not new */ return; } } /* new library linked! */ debug(2, "New libdl loaded library found: %s\n", lm_add->lib_name); if (library_num < MAX_LIBRARIES) { library[library_num++] = strdup(lm_add->lib_name); memset(<e, 0, sizeof(struct ltelf)); lte.base_addr = lm_add->addr; do_init_elf(<e, library[library_num-1]); /* add bps */ for (xptr = opt_x; xptr; xptr = xptr->next) { if (xptr->found) continue; GElf_Sym sym; GElf_Addr addr; if (in_load_libraries(xptr->name, <e, 1, &sym)) { debug(2, "found symbol %s @ %#" PRIx64 ", adding it.", xptr->name, sym.st_value); addr = sym.st_value; add_library_symbol(addr, xptr->name, &library_symbols, LS_TOPLT_NONE, 0); xptr->found = 1; insert_breakpoint(lm_add->proc, sym2addr(lm_add->proc, library_symbols), library_symbols, 1); } } do_close_elf(<e); } }
/* When functions return we check if the symbol needs an updated breakpoint with the resolved address. */ void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym) { struct breakpoint *bp; arch_addr_t resolved_addr; struct Process *leader = proc->leader; /* Only deal with unresolved symbols. */ if (libsym->arch.type != MIPS_PLT_UNRESOLVED) return; /* Get out if we are always using the PLT. */ if (libsym->arch.pltalways) return; resolved_addr = sym2addr(proc, libsym); libsym->arch.resolved_addr = (uintptr_t) resolved_addr; libsym->arch.type = MIPS_PLT_RESOLVED; if (libsym->arch.stub_addr == libsym->arch.resolved_addr) { /* Prelinked symbol. No need to add new breakpoint. */ return; } bp = malloc(sizeof (*bp)); if (bp == NULL) { fprintf(stderr, "Failed to allocate bp for %s\n", libsym->name); return; } if (breakpoint_init(bp, leader, resolved_addr, libsym) < 0) goto err; if (proc_add_breakpoint(leader, bp) < 0) { breakpoint_destroy(bp); goto err; } if (breakpoint_turn_on(bp, leader) < 0) { proc_remove_breakpoint(leader, bp); breakpoint_destroy(bp); goto err; } return; err: free(bp); }
void breakpoints_init(Process *proc) { struct library_symbol *sym; debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); if (proc->breakpoints) { /* let's remove that struct */ dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); dict_clear(proc->breakpoints); proc->breakpoints = NULL; } proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int); if (options.libcalls && proc->filename) { /* FIXME: memory leak when called by exec(): */ proc->list_of_symbols = read_elf(proc); if (opt_e) { struct library_symbol **tmp1 = &(proc->list_of_symbols); while (*tmp1) { struct opt_e_t *tmp2 = opt_e; int keep = !opt_e_enable; while (tmp2) { if (!strcmp((*tmp1)->name, tmp2->name)) { keep = opt_e_enable; } tmp2 = tmp2->next; } if (!keep) { *tmp1 = (*tmp1)->next; } else { tmp1 = &((*tmp1)->next); } } } } else { proc->list_of_symbols = NULL; } for (sym = proc->list_of_symbols; sym; sym = sym->next) { /* proc->pid==0 delays enabling. */ insert_breakpoint(proc, sym2addr(proc, sym), sym); } proc->callstack_depth = 0; proc->breakpoints_enabled = -1; }
void reinitialize_breakpoints(Process *proc) { struct library_symbol *sym; debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid); sym = proc->list_of_symbols; while (sym) { if (sym->needs_init) { insert_breakpoint(proc, sym2addr(proc, sym), sym); if (sym->needs_init && !sym->is_weak) { fprintf(stderr, "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", sym->name, proc->filename); exit(1); } } sym = sym->next; } }
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; }