int breakpoints_init(struct process *proc) { 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); /* N.B. the following used to be conditional on this, and * maybe it still needs to be. */ assert(proc->filename != NULL); struct library *lib = ltelf_read_main_binary(proc, proc->filename); struct entry_breakpoint *entry_bp = NULL; int bp_state = 0; int result = -1; switch ((int)(lib != NULL)) { fail: switch (bp_state) { case 2: proc_remove_library(proc, lib); proc_remove_breakpoint(proc, &entry_bp->super); case 1: breakpoint_destroy(&entry_bp->super); } library_destroy(lib); free(entry_bp); case 0: return result; } entry_bp = malloc(sizeof(*entry_bp)); if (entry_bp == NULL || (entry_breakpoint_init(proc, entry_bp, lib->entry, lib)) < 0) { fprintf(stderr, "Couldn't initialize entry breakpoint for PID %d.\n" "Some tracing events may be missed.\n", proc->pid); free(entry_bp); } else { ++bp_state; if ((result = proc_add_breakpoint(proc, &entry_bp->super)) < 0) goto fail; ++bp_state; if ((result = breakpoint_turn_on(&entry_bp->super, proc)) < 0) goto fail; } proc_add_library(proc, lib); proc->callstack_depth = 0; return 0; }
/* 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); }
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; }
static void clone_single_bp(void *key, void *value, void *u) { struct breakpoint *bp = value; struct clone_single_bp_data *data = u; /* Don't bother if there were errors anyway. */ if (data->error != 0) return; struct breakpoint *clone = malloc(sizeof(*clone)); if (clone == NULL || breakpoint_clone(clone, data->new_proc, bp, data->old_proc) < 0) { fail: free(clone); data->error = -1; } if (proc_add_breakpoint(data->new_proc->leader, clone) < 0) { breakpoint_destroy(clone); goto fail; } }
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; }