diff --git a/breakpoint.h b/breakpoint.h new file mode 100644 index 0000000..ce6f501 --- /dev/null +++ b/breakpoint.h @@ -0,0 +1,106 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef BREAKPOINT_H +#define BREAKPOINT_H + +/* XXX This is currently a very weak abstraction. We would like to + * much expand this to allow things like breakpoints on SDT probes and + * such. + * + * In particular, we would like to add a tracepoint abstraction. + * Tracepoint is a traceable feature--e.g. an exact address, a DWARF + * symbol, an ELF symbol, a PLT entry, or an SDT probe. Tracepoints + * are named and the user can configure which of them he wants to + * enable. Realized tracepoints enable breakpoints, which are a + * low-level realization of high-level tracepoint. + * + * Tracepoints are provided by the main binary as well as by any + * opened libraries: every time an ELF file is mapped into the address + * space, a new set of tracepoints is extracted, and filtered + * according to user settings. Those tracepoints that are left are + * then realized, and the tracing starts. + * + * A scheme like this would take care of gradually introducing + * breakpoints when the library is mapped, and therefore ready, and + * would avoid certain hacks. For example on PPC64, we don't actually + * add breakpoints to PLT. Instead, we read the PLT (which contains + * addresses, not code), to figure out where to put the breakpoints. + * In prelinked code, that address is non-zero, and points to an + * address that's not yet mapped. ptrace then fails when we try to + * add the breakpoint. + * + * Ideally, return breakpoints would be just a special kind of + * tracepoint that has attached some magic. Or a feature of a + * tracepoint. Service breakpoints like the handling of dlopen would + * be a low-level breakpoint, likely without tracepoint attached. + * + * So that's for sometimes. + */ + +#include "arch.h" + +struct Process; +struct breakpoint; + +struct bp_callbacks { + void (*on_hit) (struct breakpoint *bp, struct Process *proc); + void (*on_destroy) (struct breakpoint *bp); +}; + +struct breakpoint { + struct bp_callbacks *cbs; + void *addr; + unsigned char orig_value[BREAKPOINT_LENGTH]; + int enabled; + struct library_symbol *libsym; +#ifdef __arm__ + int thumb_mode; +#endif +}; + +/* Call on-hit handler of BP, if any is set. */ +void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc); + +/* Call on-destroy handler of BP, if any is set. */ +void breakpoint_on_destroy(struct breakpoint *bp); + +/* This is actually three functions rolled in one: + * - breakpoint_init + * - proc_insert_breakpoint + * - breakpoint_enable + * XXX I think it should be broken up somehow. */ +struct breakpoint *insert_breakpoint(struct Process *proc, void *addr, + struct library_symbol *libsym, int enable); + +/* */ +void delete_breakpoint(struct Process *proc, void *addr); + +/* XXX some of the following belongs to proc.h/proc.c. */ +struct breakpoint *address2bpstruct(struct Process *proc, void *addr); +void enable_all_breakpoints(struct Process *proc); +void disable_all_breakpoints(struct Process *proc); +int breakpoints_init(struct Process *proc, int enable); + +void reinitialize_breakpoints(struct Process *proc); + + +#endif /* BREAKPOINT_H */ diff --git a/breakpoints.c b/breakpoints.c index 387b2a5..5713fe4 100644 --- a/breakpoints.c +++ b/breakpoints.c @@ -8,12 +8,30 @@ #include #endif +#include "breakpoint.h" #include "common.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_destroy(struct breakpoint *bp) +{ + assert(bp != NULL); + if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) + (bp->cbs->on_destroy) (bp); +} + /*****************************************************************************/ -Breakpoint * -address2bpstruct(Process *proc, void *addr) { +struct breakpoint * +address2bpstruct(Process *proc, void *addr) +{ assert(proc != NULL); assert(proc->breakpoints != NULL); assert(proc->leader == proc); @@ -21,10 +39,11 @@ address2bpstruct(Process *proc, void *addr) { return dict_find_entry(proc->breakpoints, addr); } -void +struct breakpoint * insert_breakpoint(Process *proc, void *addr, - struct library_symbol *libsym, int enable) { - Breakpoint *sbp; + struct library_symbol *libsym, int enable) +{ + struct breakpoint *sbp; Process * leader = proc->leader; @@ -43,16 +62,16 @@ insert_breakpoint(Process *proc, void *addr, debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); if (!addr) - return; + return NULL; if (libsym) libsym->needs_init = 0; sbp = dict_find_entry(leader->breakpoints, addr); - if (!sbp) { - sbp = calloc(1, sizeof(Breakpoint)); - if (!sbp) { - return; /* TODO FIXME XXX: error_mem */ + if (sbp == NULL) { + sbp = calloc(1, sizeof(*sbp)); + if (sbp == NULL) { + return NULL; /* TODO FIXME XXX: error_mem */ } dict_enter(leader->breakpoints, addr, sbp); sbp->addr = addr; @@ -67,11 +86,14 @@ insert_breakpoint(Process *proc, void *addr, assert(proc->pid != 0); enable_breakpoint(proc, sbp); } + + return sbp; } void -delete_breakpoint(Process *proc, void *addr) { - Breakpoint *sbp; +delete_breakpoint(Process *proc, void *addr) +{ + struct breakpoint *sbp; debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); @@ -91,89 +113,67 @@ delete_breakpoint(Process *proc, void *addr) { } static void -enable_bp_cb(void *addr, void *sbp, void *proc) { +enable_bp_cb(void *addr, void *sbp, void *proc) +{ debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); - if (((Breakpoint *)sbp)->enabled) { + if (((struct breakpoint *)sbp)->enabled) enable_breakpoint(proc, sbp); - } } void -enable_all_breakpoints(Process *proc) { +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; + debug(1, "Enabling breakpoints for pid %u...", proc->pid); + if (proc->breakpoints) { + dict_apply_to_all(proc->breakpoints, enable_bp_cb, + proc); + } +#ifdef __mips__ + { /* - * PPC HACK! (XXX FIXME TODO) - * If the dynamic linker hasn't populated the PLT then - * dont enable the breakpoints + * I'm sure there is a nicer way to do this. We need to + * insert breakpoints _after_ the child has been started. */ - 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); + 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; +#endif } static void -disable_bp_cb(void *addr, void *sbp, void *proc) { +disable_bp_cb(void *addr, void *sbp, void *proc) +{ debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); - if (((Breakpoint *)sbp)->enabled) { + 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); - if (proc->breakpoints_enabled) { - debug(1, "Disabling breakpoints for pid %u...", proc->pid); - dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); - } - proc->breakpoints_enabled = 0; + dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); } static void @@ -183,11 +183,18 @@ free_bp_cb(void *addr, void *sbp, void *data) { free(sbp); } +static void +entry_callback_hit(struct breakpoint *bp, struct Process *proc) +{ + if (proc == NULL || proc->leader == NULL) + return; + delete_breakpoint(proc, bp->addr); // xxx + reinitialize_breakpoints(proc->leader); +} + int breakpoints_init(Process *proc, int enable) { - 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); @@ -206,9 +213,11 @@ breakpoints_init(Process *proc, int enable) destroy_library_symbol_chain(proc->list_of_symbols); proc->list_of_symbols = NULL; + GElf_Addr entry; if (options.libcalls && proc->filename) { - proc->list_of_symbols = read_elf(proc); + proc->list_of_symbols = read_elf(proc, &entry); if (proc->list_of_symbols == NULL) { + fail: /* XXX leak breakpoints */ return -1; } @@ -235,11 +244,19 @@ breakpoints_init(Process *proc, int enable) } } - for (sym = proc->list_of_symbols; sym; sym = sym->next) - insert_breakpoint(proc, sym2addr(proc, sym), sym, enable); + struct breakpoint *entry_bp + = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); + if (entry_bp == NULL) { + fprintf(stderr, "fail!\n"); + goto fail; + } + + static struct bp_callbacks entry_callbacks = { + .on_hit = entry_callback_hit, + }; + entry_bp->cbs = &entry_callbacks; proc->callstack_depth = 0; - proc->breakpoints_enabled = -1; return 0; } diff --git a/common.h b/common.h index 715898d..a47cd89 100644 --- a/common.h +++ b/common.h @@ -26,17 +26,6 @@ extern char * command; extern int exiting; /* =1 if we have to exit ASAP */ -typedef struct Breakpoint Breakpoint; -struct Breakpoint { - void * addr; - unsigned char orig_value[BREAKPOINT_LENGTH]; - int enabled; - struct library_symbol * libsym; -#ifdef __arm__ - int thumb_mode; -#endif -}; - enum arg_type { ARGTYPE_UNKNOWN = -1, ARGTYPE_VOID, @@ -187,11 +176,10 @@ struct Process { pid_t pid; /* Dictionary of breakpoints (which is a mapping - * address->Breakpoint). This is NULL for non-leader + * address->breakpoint). This is NULL for non-leader * processes. */ Dict * breakpoints; - int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ unsigned int personality; int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ @@ -296,14 +284,7 @@ extern void destroy_event_handler(Process * proc); extern pid_t execute_program(const char * command, char ** argv); extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); -extern Breakpoint * address2bpstruct(Process * proc, void * addr); -extern int breakpoints_init(Process * proc, int enable); -extern void insert_breakpoint(Process * proc, void * addr, - struct library_symbol * libsym, int enable); -extern void delete_breakpoint(Process * proc, void * addr); -extern void enable_all_breakpoints(Process * proc); extern void disable_all_breakpoints(Process * proc); -extern void reinitialize_breakpoints(Process *); extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); extern void open_pid(pid_t pid); @@ -322,6 +303,8 @@ extern struct library_symbol * clone_library_symbol(struct library_symbol * s); extern void destroy_library_symbol(struct library_symbol * s); extern void destroy_library_symbol_chain(struct library_symbol * chain); +struct breakpoint; + /* Arch-dependent stuff: */ extern char * pid2name(pid_t pid); extern pid_t process_leader(pid_t pid); @@ -329,6 +312,7 @@ extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); extern int process_stopped(pid_t pid); extern enum process_status process_status(pid_t pid); extern void trace_set_options(Process * proc, pid_t pid); +extern void wait_for_proc(pid_t pid); extern void trace_me(void); extern int trace_pid(pid_t pid); extern void untrace_pid(pid_t pid); @@ -338,13 +322,13 @@ extern void set_instruction_pointer(Process * proc, void * addr); extern void * get_stack_pointer(Process * proc); extern void * get_return_addr(Process * proc, void * stack_pointer); extern void set_return_addr(Process * proc, void * addr); -extern void enable_breakpoint(Process * proc, Breakpoint * sbp); -extern void disable_breakpoint(Process * proc, Breakpoint * sbp); +extern void enable_breakpoint(Process * proc, struct breakpoint *sbp); +extern void disable_breakpoint(Process * proc, struct breakpoint *sbp); extern int syscall_p(Process * proc, int status, int * sysnum); extern void continue_process(pid_t pid); extern void continue_after_signal(pid_t pid, int signum); extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); -extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); +extern void continue_after_breakpoint(Process * proc, struct breakpoint *sbp); extern void continue_after_vfork(Process * proc); extern void ltrace_exiting(void); extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); diff --git a/execute_program.c b/execute_program.c index 47f514d..859f32c 100644 --- a/execute_program.c +++ b/execute_program.c @@ -89,6 +89,8 @@ execute_program(const char * command, char **argv) _exit(1); } + wait_for_proc(pid); + debug(1, "PID=%d", pid); return pid; diff --git a/handle_event.c b/handle_event.c index 203459c..c146eb9 100644 --- a/handle_event.c +++ b/handle_event.c @@ -1,6 +1,6 @@ +#define _GNU_SOURCE #include "config.h" -#define _GNU_SOURCE #include #include #include @@ -9,12 +9,13 @@ #include #include -#include "common.h" - #ifdef __powerpc__ #include #endif +#include "common.h" +#include "breakpoint.h" + static void handle_signal(Event *event); static void handle_exit(Event *event); static void handle_exit_signal(Event *event); @@ -155,19 +156,18 @@ address_clone(void * addr, void * data) } static void * -breakpoint_clone(void * bp, void * data) +breakpoint_clone(void *bp, void *data) { - Breakpoint * b; - Dict * map = data; + Dict *map = data; debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); - b = malloc(sizeof(Breakpoint)); + struct breakpoint *b = malloc(sizeof(*b)); if (!b) { perror("malloc()"); exit(1); } - memcpy(b, bp, sizeof(Breakpoint)); + memcpy(b, bp, sizeof(*b)); if (b->libsym != NULL) { - struct library_symbol * sym = dict_find_entry(map, b->libsym); + 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); @@ -452,9 +452,6 @@ handle_syscall(Event *event) { output_left(LT_TOF_SYSCALL, event->proc, sysname(event->proc, event->e_un.sysnum)); } - if (event->proc->breakpoints_enabled == 0) { - enable_all_breakpoints(event->proc); - } } continue_after_syscall(event->proc, event->e_un.sysnum, 0); } @@ -462,7 +459,6 @@ handle_syscall(Event *event) { static void handle_exec(Event * event) { Process * proc = event->proc; - pid_t saved_pid; debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); if (proc->state == STATE_IGNORED) { @@ -476,10 +472,7 @@ handle_exec(Event * event) { proc->arch_ptr = NULL; free(proc->filename); proc->filename = pid2name(proc->pid); - saved_pid = proc->pid; - proc->pid = 0; breakpoints_init(proc, 0); - proc->pid = saved_pid; proc->callstack_depth = 0; continue_process(proc->pid); } @@ -493,9 +486,6 @@ handle_arch_syscall(Event *event) { output_left(LT_TOF_SYSCALL, event->proc, arch_sysname(event->proc, event->e_un.sysnum)); } - if (event->proc->breakpoints_enabled == 0) { - enable_all_breakpoints(event->proc); - } } continue_process(event->proc->pid); } @@ -559,14 +549,11 @@ handle_arch_sysret(Event *event) { continue_process(event->proc->pid); } -#ifdef __powerpc__ -void *get_count_register (Process *proc); -#endif - static void -handle_breakpoint(Event *event) { +handle_breakpoint(Event *event) +{ int i, j; - Breakpoint *sbp; + struct breakpoint *sbp; Process *leader = event->proc->leader; /* The leader has terminated. */ @@ -578,28 +565,6 @@ handle_breakpoint(Event *event) { 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) { @@ -679,6 +644,8 @@ handle_breakpoint(Event *event) { } if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { + breakpoint_on_hit(sbp, event->proc); + if (sbp->libsym == NULL) { continue_after_breakpoint(event->proc, sbp); return; @@ -696,12 +663,6 @@ handle_breakpoint(Event *event) { 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; diff --git a/ltrace-elf.c b/ltrace-elf.c index 8dbc298..f7fc239 100644 --- a/ltrace-elf.c +++ b/ltrace-elf.c @@ -662,7 +662,8 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) { } struct library_symbol * -read_elf(Process *proc) { +read_elf(Process *proc, GElf_Addr *entryp) +{ struct ltelf lte[MAX_LIBRARIES + 1]; size_t i; struct opt_x_t *xptr; @@ -700,7 +701,6 @@ read_elf(Process *proc) { #ifdef __mips__ // MIPS doesn't use the PLT and the GOT entries get changed // on startup. - proc->need_to_reinitialize_breakpoints = 1; for(i=lte->mips_gotsym; idynsym_count;i++){ GElf_Sym sym; const char *name; @@ -745,11 +745,6 @@ read_elf(Process *proc) { "Couldn't get relocation from \"%s\"", proc->filename); -#ifdef PLT_REINITALISATION_BP - if (!sym.st_value && PLTs_initialized_by_here) - proc->need_to_reinitialize_breakpoints = 1; -#endif - name = lte->dynstr + sym.st_name; count = library_num ? library_num+1 : 0; @@ -772,30 +767,6 @@ read_elf(Process *proc) { } } #endif // !__mips__ -#ifdef PLT_REINITALISATION_BP - struct opt_x_t *main_cheat; - - if (proc->need_to_reinitialize_breakpoints) { - /* Add "PLTs_initialized_by_here" to opt_x list, if not - already there. */ - main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); - if (main_cheat == NULL) - error(EXIT_FAILURE, 0, "Couldn't allocate memory"); - main_cheat->next = opt_x_loc; - main_cheat->found = 0; - main_cheat->name = PLTs_initialized_by_here; - - for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 - && main_cheat) { - free(main_cheat); - main_cheat = NULL; - break; - } - if (main_cheat) - opt_x_loc = main_cheat; - } -#endif } else { lib_tail = &library_symbols; } @@ -850,16 +821,17 @@ read_elf(Process *proc) { } } + if (lte->ehdr.e_entry != 0) { + *entryp = opd2addr(lte, lte->ehdr.e_entry); + } else { + } + for (xptr = opt_x_loc; xptr; xptr = xptr->next) if ( ! xptr->found) { char *badthing = "WARNING"; #ifdef PLT_REINITALISATION_BP if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) { if (lte->ehdr.e_entry) { - add_library_symbol ( - opd2addr (lte, lte->ehdr.e_entry), - PLTs_initialized_by_here, - lib_tail, 1, 0); fprintf (stderr, "WARNING: Using e_ent" "ry from elf header (%p) for " "address of \"%s\"\n", (void*) diff --git a/ltrace-elf.h b/ltrace-elf.h index 3b675c5..4da8a0a 100644 --- a/ltrace-elf.h +++ b/ltrace-elf.h @@ -45,7 +45,7 @@ extern size_t library_num; extern char *library[MAX_LIBRARIES]; extern int open_elf(struct ltelf *lte, const char *filename); -extern struct library_symbol *read_elf(Process *); +extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp); extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); diff --git a/proc.c b/proc.c index f4d3396..5febc3f 100644 --- a/proc.c +++ b/proc.c @@ -14,6 +14,7 @@ #include #include "common.h" +#include "breakpoint.h" Process * open_program(char *filename, pid_t pid, int enable) { @@ -26,7 +27,6 @@ open_program(char *filename, pid_t pid, int enable) { } proc->filename = strdup(filename); - proc->breakpoints_enabled = -1; proc->pid = pid; #if defined(HAVE_LIBUNWIND) proc->unwind_priv = _UPT_create(pid); @@ -39,13 +39,15 @@ open_program(char *filename, pid_t pid, int enable) { return NULL; } - if (proc->leader == proc) + if (proc->leader == proc) { + trace_set_options(proc, proc->pid); if (breakpoints_init(proc, enable)) { fprintf(stderr, "failed to init breakpoints %d\n", proc->pid); remove_process(proc); return NULL; } + } return proc; } @@ -73,11 +75,10 @@ open_one_pid(pid_t pid) return 0; } -enum pcb_status +static enum pcb_status start_one_pid(Process * proc, void * data) { continue_process(proc->pid); - proc->breakpoints_enabled = 1; return pcb_cont; } @@ -116,7 +117,7 @@ open_pid(pid_t pid) if (process_tasks(pid, &tasks, &ntasks) < 0) { fprintf(stderr, "Cannot obtain tasks of pid %u: %s\n", pid, strerror(errno)); - goto start; + break; } have_all = 1; @@ -135,7 +136,6 @@ open_pid(pid_t pid) /* Done. Now initialize breakpoints and then continue * everyone. */ Process * leader; -start: leader = pid2proc(pid)->leader; enable_all_breakpoints(leader); diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c index 4e17940..493f973 100644 --- a/sysdeps/linux-gnu/arm/breakpoint.c +++ b/sysdeps/linux-gnu/arm/breakpoint.c @@ -27,7 +27,8 @@ #include "common.h" void -arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { +arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) +{ unsigned int i, j; const unsigned char break_insn[] = BREAKPOINT_VALUE; const unsigned char thumb_break_insn[] = THUMB_BREAKPOINT_VALUE; @@ -59,7 +60,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { } void -arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { +arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) +{ unsigned int i, j; debug(1, "arch_disable_breakpoint(%d,%p)", pid, sbp->addr); diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c index 5a49e9d..b98374b 100644 --- a/sysdeps/linux-gnu/breakpoint.c +++ b/sysdeps/linux-gnu/breakpoint.c @@ -5,12 +5,13 @@ #include "common.h" #include "arch.h" +#include "breakpoint.h" #ifdef ARCH_HAVE_ENABLE_BREAKPOINT -extern void arch_enable_breakpoint(pid_t, Breakpoint *); +extern void arch_enable_breakpoint(pid_t, struct breakpoint *); #else /* ARCH_HAVE_ENABLE_BREAKPOINT */ void -arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) +arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) { static unsigned char break_insn[] = BREAKPOINT_VALUE; unsigned int i, j; @@ -38,7 +39,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ void -enable_breakpoint(Process * proc, Breakpoint *sbp) { +enable_breakpoint(Process *proc, struct breakpoint *sbp) +{ if (sbp->libsym) { debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); } else { @@ -48,10 +50,10 @@ enable_breakpoint(Process * proc, Breakpoint *sbp) { } #ifdef ARCH_HAVE_DISABLE_BREAKPOINT -extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); +extern void arch_disable_breakpoint(pid_t, const struct breakpoint *sbp); #else /* ARCH_HAVE_DISABLE_BREAKPOINT */ void -arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) +arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) { unsigned int i, j; @@ -78,7 +80,8 @@ arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ void -disable_breakpoint(Process * proc, Breakpoint *sbp) { +disable_breakpoint(Process *proc, struct breakpoint *sbp) +{ if (sbp->libsym) { debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); } else { diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c index 021192f..3174c1a 100644 --- a/sysdeps/linux-gnu/events.c +++ b/sysdeps/linux-gnu/events.c @@ -12,6 +12,7 @@ #include #include "common.h" +#include "breakpoint.h" static Event event; @@ -166,19 +167,9 @@ next_event(void) } get_arch_dep(event.proc); debug(3, "event from pid %u", pid); - if (event.proc->breakpoints_enabled == -1) - trace_set_options(event.proc, event.proc->pid); Process *leader = event.proc->leader; if (leader == event.proc) { - if (event.proc->breakpoints_enabled == -1) { - event.type = EVENT_NONE; - enable_all_breakpoints(event.proc); - continue_process(event.proc->pid); - debug(DEBUG_EVENT, - "event: NONE: pid=%d (enabling breakpoints)", - pid); - return &event; - } else if (!event.proc->libdl_hooked) { + if (!event.proc->libdl_hooked) { /* debug struct may not have been written yet.. */ if (linkmap_init(event.proc, &main_lte) == 0) { event.proc->libdl_hooked = 1; diff --git a/sysdeps/linux-gnu/ia64/breakpoint.c b/sysdeps/linux-gnu/ia64/breakpoint.c index 45ee11e..a0bfaf9 100644 --- a/sysdeps/linux-gnu/ia64/breakpoint.c +++ b/sysdeps/linux-gnu/ia64/breakpoint.c @@ -150,7 +150,8 @@ union bundle_t { }; void -arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { +arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) +{ unsigned long addr = (unsigned long)sbp->addr; union bundle_t bundle; @@ -187,7 +188,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { } void -arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { +arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) +{ unsigned long addr = (unsigned long)sbp->addr; int slotnum = (int)(addr & 0x0f) & 0x3; diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c index a99593c..3350117 100644 --- a/sysdeps/linux-gnu/proc.c +++ b/sysdeps/linux-gnu/proc.c @@ -1,6 +1,5 @@ #define _GNU_SOURCE /* For getline. */ #include "config.h" -#include "common.h" #include #include @@ -17,6 +16,8 @@ #include #include +#include "common.h" +#include "breakpoint.h" /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' * couldn't open it to find the executable. So it may be necessary to diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index db18df0..b4d8fd1 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -11,6 +11,7 @@ #include "common.h" #include "config.h" +#include "breakpoint.h" /* If the system headers did not provide the constants, hard-code the normal values. */ @@ -77,6 +78,29 @@ trace_me(void) { } } +/* There's a (hopefully) brief period of time after the child process + * exec's when we can't trace it yet. Here we wait for kernel to + * prepare the process. */ +void +wait_for_proc(pid_t pid) +{ + size_t i; + for (i = 0; i < 100; ++i) { + /* We read from memory address 0, but that shouldn't + * be a problem: the reading will just fail. We are + * looking for a particular reason of failure. */ + if (ptrace(PTRACE_PEEKTEXT, pid, 0, 0) != -1 + || errno != ESRCH) + return; + + usleep(1000); + } + + fprintf(stderr, "\ +I consistently fail to read a word from the freshly launched process.\n\ +I'll now try to proceed with tracing, but this shouldn't be happening.\n"); +} + int trace_pid(pid_t pid) { debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); @@ -193,7 +217,7 @@ struct process_stopping_handler Process * task_enabling_breakpoint; /* The pointer being re-enabled. */ - Breakpoint * breakpoint_being_enabled; + struct breakpoint *breakpoint_being_enabled; enum { /* We are waiting for everyone to land in t/T. */ @@ -364,7 +388,7 @@ static void ugly_workaround(Process * proc) { void * ip = get_instruction_pointer(proc); - Breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip); + struct breakpoint *sbp = dict_find_entry(proc->leader->breakpoints, ip); if (sbp != NULL) enable_breakpoint(proc, sbp); else @@ -578,7 +602,7 @@ process_stopping_on_event(Event_Handler * super, Event * event) struct process_stopping_handler * self = (void *)super; Process * task = event->proc; Process * leader = task->leader; - Breakpoint * sbp = self->breakpoint_being_enabled; + struct breakpoint *sbp = self->breakpoint_being_enabled; Process * teb = self->task_enabling_breakpoint; debug(DEBUG_PROCESS, @@ -687,7 +711,7 @@ process_stopping_destroy(Event_Handler * super) } void -continue_after_breakpoint(Process *proc, Breakpoint *sbp) +continue_after_breakpoint(Process *proc, struct breakpoint *sbp) { set_instruction_pointer(proc, sbp->addr); if (sbp->enabled == 0) { @@ -851,7 +875,7 @@ static Event * process_vfork_on_event(Event_Handler * super, Event * event) { struct process_vfork_handler * self = (void *)super; - Breakpoint * sbp; + struct breakpoint *sbp; assert(self != NULL); switch (event->type) {