From 2b46cfc1127d390eddd9593fe5ce5399c1f68130 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 18 Feb 2012 11:17:29 +0100 Subject: The first crude version of tracing across libraries - the patch will be sliced later --- Makefile.am | 6 +- breakpoint.h | 21 +- breakpoints.c | 179 ++++++------ common.h | 34 +-- defs.h | 2 - handle_event.c | 104 +++---- libltrace.c | 6 +- library.c | 207 ++++++++++++++ library.h | 149 ++++++++++ ltrace-elf.c | 543 ++++++++----------------------------- ltrace-elf.h | 34 ++- options.c | 12 +- output.h | 9 +- proc.c | 297 +++++++++++++++++--- proc.h | 93 ++++++- sysdeps/linux-gnu/arm/arch.h | 5 + sysdeps/linux-gnu/arm/breakpoint.c | 11 + sysdeps/linux-gnu/arm/regs.c | 6 + sysdeps/linux-gnu/breakpoint.c | 1 + sysdeps/linux-gnu/events.c | 12 +- sysdeps/linux-gnu/ppc/plt.c | 3 + sysdeps/linux-gnu/proc.c | 206 ++++++-------- sysdeps/linux-gnu/trace.c | 52 ++-- sysdeps/linux-gnu/x86_64/plt.c | 1 + 24 files changed, 1117 insertions(+), 876 deletions(-) create mode 100644 library.c create mode 100644 library.h diff --git a/Makefile.am b/Makefile.am index 6c299d8..7c9faf6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,8 @@ libltrace_la_SOURCES = \ output.c \ proc.c \ read_config_file.c \ - summary.c + summary.c \ + library.c libltrace_la_LIBADD = \ $(libelf_LIBS) \ @@ -56,7 +57,8 @@ noinst_HEADERS = \ ltrace.h \ options.h \ output.h \ - read_config_file.h + read_config_file.h \ + library.h dist_man1_MANS = \ ltrace.1 diff --git a/breakpoint.h b/breakpoint.h index ce6f501..53cdbf2 100644 --- a/breakpoint.h +++ b/breakpoint.h @@ -57,6 +57,7 @@ */ #include "arch.h" +#include "library.h" struct Process; struct breakpoint; @@ -66,15 +67,18 @@ struct bp_callbacks { void (*on_destroy) (struct breakpoint *bp); }; +#ifndef ARCH_HAVE_BREAKPOINT_DATA +struct arch_breakpoint_data { +}; +#endif + 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 + struct arch_breakpoint_data arch; }; /* Call on-hit handler of BP, if any is set. */ @@ -83,6 +87,14 @@ 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); +/* Initialize a breakpoint structure. That doesn't actually realize + * the breakpoint. The breakpoint is initially assumed to be + * disabled. orig_value has to be set separately. CBS may be + * NULL. */ +int breakpoint_init(struct breakpoint *bp, struct Process *proc, + target_address_t addr, struct library_symbol *libsym, + struct bp_callbacks *cbs); + /* This is actually three functions rolled in one: * - breakpoint_init * - proc_insert_breakpoint @@ -100,7 +112,4 @@ 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 5a473a9..f0f8db4 100644 --- a/breakpoints.c +++ b/breakpoints.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #ifdef __powerpc__ #include @@ -11,6 +13,7 @@ #include "breakpoint.h" #include "common.h" #include "proc.h" +#include "library.h" void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) @@ -40,12 +43,33 @@ address2bpstruct(Process *proc, void *addr) return dict_find_entry(proc->breakpoints, addr); } +#ifdef ARCH_HAVE_BREAKPOINT_DATA +int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp); +#else +int +arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) +{ + return 0; +} +#endif + +int +breakpoint_init(struct breakpoint *bp, struct Process *proc, + target_address_t addr, struct library_symbol *libsym, + struct bp_callbacks *cbs) +{ + bp->cbs = cbs; + bp->addr = addr; + memset(bp->orig_value, 0, sizeof(bp->orig_value)); + bp->enabled = 0; + bp->libsym = libsym; + return arch_breakpoint_init(proc, bp); +} + struct breakpoint * insert_breakpoint(Process *proc, void *addr, struct library_symbol *libsym, int enable) { - struct breakpoint *sbp; - Process * leader = proc->leader; /* Only the group leader should be getting the breakpoints and @@ -53,12 +77,6 @@ insert_breakpoint(Process *proc, void *addr, assert(leader != NULL); assert(leader->breakpoints != NULL); -#ifdef __arm__ - int thumb_mode = (int)addr & 1; - if (thumb_mode) - addr = (void *)((int)addr & ~1); -#endif - 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); @@ -68,20 +86,17 @@ insert_breakpoint(Process *proc, void *addr, if (libsym) libsym->needs_init = 0; - sbp = dict_find_entry(leader->breakpoints, addr); + struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); if (sbp == NULL) { - sbp = calloc(1, sizeof(*sbp)); - if (sbp == NULL) { - return NULL; /* TODO FIXME XXX: error_mem */ + sbp = malloc(sizeof(*sbp)); + if (sbp == NULL + || breakpoint_init(sbp, proc, addr, libsym, NULL) < 0 + || dict_enter(leader->breakpoints, addr, sbp) < 0) { + free(sbp); + return NULL; } - dict_enter(leader->breakpoints, addr, sbp); - sbp->addr = addr; - sbp->libsym = libsym; } -#ifdef __arm__ - sbp->thumb_mode = thumb_mode | proc->thumb_mode; - proc->thumb_mode = 0; -#endif + sbp->enabled++; if (sbp->enabled == 1 && enable) { assert(proc->pid != 0); @@ -177,108 +192,80 @@ disable_all_breakpoints(Process *proc) { dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); } -static void -free_bp_cb(void *addr, void *sbp, void *data) { - debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp); - assert(sbp); - free(sbp); +static enum callback_status +reinitialize_breakpoints(struct Process *proc, struct library *library, + void *data) +{ + debug(DEBUG_FUNCTION, "reinitialize_breakpoints_in(pid=%d, %s)", + proc->pid, library->name); + + struct library_symbol *sym; + for (sym = library->symbols; sym != NULL; sym = sym->next) + if (sym->needs_init) { + target_address_t addr = sym2addr(proc, sym); + if (insert_breakpoint(proc, addr, sym, 1) == NULL + || (sym->needs_init && !sym->is_weak)) + fprintf(stderr, + "could not re-initialize breakpoint " + "for \"%s\" in file \"%s\"\n", + sym->name, proc->filename); + } + + return CBS_CONT; } 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 - reinitialize_breakpoints(proc->leader); + + linkmap_init(proc); + proc_each_library(proc->leader, NULL, reinitialize_breakpoints, NULL); } 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); - 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; - } - /* Only the thread group leader should hold the breakpoints. - * (N.B. PID may be set to 0 temporarily when called by - * handle_exec). */ - assert(proc->leader == proc); - - proc->breakpoints = dict_init(dict_key2hash_int, - dict_key_cmp_int); + /* XXX breakpoint dictionary should be initialized + * outside. Here we just put in breakpoints. */ + assert(proc->breakpoints != NULL); - destroy_library_symbol_chain(proc->list_of_symbols); - proc->list_of_symbols = NULL; + /* Only the thread group leader should hold the breakpoints. */ + assert(proc->leader == proc); - GElf_Addr entry; if (options.libcalls && proc->filename) { - proc->list_of_symbols = read_elf(proc, &entry); - if (proc->list_of_symbols == NULL) { + struct library *lib = ltelf_read_main_binary(proc, proc->filename); + switch (lib != NULL) { fail: - /* XXX leak breakpoints */ + proc_remove_library(proc, lib); + library_destroy(lib); + case 0: return -1; } - - 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); - } - } + 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, 1); + if (entry_bp == NULL) { + error(0, errno, "couldn't insert entry breakpoint"); + goto fail; } - } - struct breakpoint *entry_bp - = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); - if (entry_bp == NULL) { - fprintf(stderr, "fail!\n"); - 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; } - static struct bp_callbacks entry_callbacks = { - .on_hit = entry_callback_hit, - }; - entry_bp->cbs = &entry_callbacks; - proc->callstack_depth = 0; return 0; } - -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, 1); - 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; - } -} diff --git a/common.h b/common.h index 0d56a2b..087b2bc 100644 --- a/common.h +++ b/common.h @@ -14,6 +14,7 @@ #include "debug.h" #include "ltrace-elf.h" #include "read_config_file.h" +#include "proc.h" #if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__ # define USE_DEMANGLE @@ -113,24 +114,9 @@ struct Function { Function * next; }; -enum toplt { - LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */ - LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ - LS_TOPLT_POINT /* PLT for this symbol is a non-executable. */ -}; - extern Function * list_of_functions; extern char *PLTs_initialized_by_here; -struct library_symbol { - const char *name; - void * enter_addr; - char needs_init; - enum toplt plt_type; - char is_weak; - struct library_symbol * next; -}; - struct opt_c_struct { int count; struct timeval tv; @@ -174,22 +160,8 @@ extern void disable_all_breakpoints(Process * proc); extern void show_summary(void); extern arg_type_info * lookup_prototype(enum arg_type at); -extern int do_init_elf(struct ltelf *lte, const char *filename); -extern void do_close_elf(struct ltelf *lte); -extern int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym); -extern struct library_symbol *library_symbols; -extern void library_symbol_init(struct library_symbol *libsym, - GElf_Addr addr, const char *name, - enum toplt type_of_plt, int is_weak); -extern void add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, - enum toplt type_of_plt, int is_weak); - -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; +struct library_symbol; /* Arch-dependent stuff: */ extern char * pid2name(pid_t pid); @@ -223,7 +195,7 @@ extern int umovelong (Process * proc, void * addr, long * result, arg_type_info extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count); extern int ffcheck(void * maddr); extern void * sym2addr(Process *, struct library_symbol *); -extern int linkmap_init(Process *, struct ltelf *); +extern int linkmap_init(struct Process *); extern void arch_check_dbg(Process *proc); extern int task_kill (pid_t pid, int sig); diff --git a/defs.h b/defs.h index dea000b..1eadb47 100644 --- a/defs.h +++ b/defs.h @@ -14,5 +14,3 @@ #ifndef DEFAULT_ARRAYLEN #define DEFAULT_ARRAYLEN 4 /* default maximum # array elements */ #endif /* (-A switch) */ - -#define MAX_LIBRARIES 200 diff --git a/handle_event.c b/handle_event.c index afabd96..bdd9af9 100644 --- a/handle_event.c +++ b/handle_event.c @@ -16,6 +16,7 @@ #include "common.h" #include "breakpoint.h" #include "proc.h" +#include "library.h" static void handle_signal(Event *event); static void handle_exit(Event *event); @@ -149,37 +150,6 @@ handle_event(Event *event) } } -/* TODO */ -static void * -address_clone(void * addr, void * data) -{ - debug(DEBUG_FUNCTION, "address_clone(%p)", addr); - return addr; -} - -static void * -breakpoint_clone(void *bp, void *data) -{ - Dict *map = data; - debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); - struct breakpoint *b = malloc(sizeof(*b)); - if (!b) { - perror("malloc()"); - exit(1); - } - memcpy(b, bp, sizeof(*b)); - 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; -} - typedef struct Pending_New Pending_New; struct Pending_New { pid_t pid; @@ -241,6 +211,7 @@ pending_new_remove(pid_t pid) { } } +#if 0 static int clone_breakpoints(Process * proc, Process * orig_proc) { @@ -274,50 +245,48 @@ clone_breakpoints(Process * proc, Process * orig_proc) dict_clear(map); return 0; } +#endif static void -handle_clone(Event * event) { - Process *p; - +handle_clone(Event *event) +{ debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); - p = malloc(sizeof(Process)); - if (!p) { + struct Process *proc = malloc(sizeof(*proc)); + if (proc == NULL) { + fail: + free(proc); + /* XXX proper error handling here, please. */ perror("malloc()"); exit(1); } - memcpy(p, event->proc, sizeof(Process)); - p->pid = event->e_un.newpid; - p->parent = event->proc; + + if (process_clone(proc, event->proc, event->e_un.newpid) < 0) + goto fail; + proc->parent = event->proc; /* We save register values to the arch pointer, and these need to be per-thread. */ - p->arch_ptr = NULL; - - if (pending_new(p->pid)) { - pending_new_remove(p->pid); - if (p->event_handler != NULL) - destroy_event_handler(p); - if (event->proc->state == STATE_ATTACHED && options.follow) { - p->state = STATE_ATTACHED; - } else { - p->state = STATE_IGNORED; - } - continue_process(p->pid); - add_process(p); + proc->arch_ptr = NULL; + + if (pending_new(proc->pid)) { + pending_new_remove(proc->pid); + /* XXX this used to be destroy_event_handler call, but + * I don't think we want to call that on a shared + * state. */ + proc->event_handler = NULL; + if (event->proc->state == STATE_ATTACHED && options.follow) + proc->state = STATE_ATTACHED; + else + proc->state = STATE_IGNORED; + continue_process(proc->pid); } else { - p->state = STATE_BEING_CREATED; - add_process(p); + proc->state = STATE_BEING_CREATED; } - - if (p->leader == p) - clone_breakpoints(p, event->proc->leader); - else - /* Thread groups share breakpoints. */ - p->breakpoints = NULL; + add_process(proc); if (event->type == EVENT_VFORK) - continue_after_vfork(p); + continue_after_vfork(proc); else continue_process(event->proc->pid); } @@ -676,17 +645,8 @@ handle_breakpoint(Event *event) if ((sbp = address2bpstruct(leader, brk_addr))) { breakpoint_on_hit(sbp, event->proc); - 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) { + if (event->proc->state != STATE_IGNORED + && sbp->libsym != NULL) { event->proc->stack_pointer = get_stack_pointer(event->proc); event->proc->return_addr = get_return_addr(event->proc, event->proc->stack_pointer); diff --git a/libltrace.c b/libltrace.c index dcd5537..2295901 100644 --- a/libltrace.c +++ b/libltrace.c @@ -16,8 +16,8 @@ char *command = NULL; int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */ -static enum pcb_status -stop_non_p_processes (Process * proc, void * data) +static enum callback_status +stop_non_p_processes(Process *proc, void *data) { int stop = 1; @@ -39,7 +39,7 @@ stop_non_p_processes (Process * proc, void * data) kill(proc->pid, SIGSTOP); } - return pcb_cont; + return CBS_CONT; } static void diff --git a/library.c b/library.c new file mode 100644 index 0000000..56a47c4 --- /dev/null +++ b/library.c @@ -0,0 +1,207 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2001,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand + * + * 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 + */ + +#include +#include +#include "library.h" +#include "proc.h" // for enum callback_status +#include "debug.h" + +/* If the other symbol owns the name, we need to make the copy, so + * that the life-times of the two symbols are not dependent on each + * other. */ +static int +strdup_if_owned(const char **retp, const char *str, int owned) +{ + if (!owned || str == NULL) { + *retp = str; + return 0; + } else { + *retp = strdup(str); + return *retp != NULL ? 0 : -1; + } +} + +void +library_symbol_init(struct library_symbol *libsym, + GElf_Addr addr, const char *name, int own_name, + enum toplt type_of_plt, int is_weak) +{ + libsym->needs_init = 0; + libsym->is_weak = is_weak; + libsym->plt_type = type_of_plt; + libsym->name = name; + libsym->own_name = own_name; + libsym->enter_addr = (void *)(uintptr_t)addr; + libsym->next = NULL; +} + +void +library_symbol_destroy(struct library_symbol *libsym) +{ + if (libsym != NULL && libsym->own_name) + free((char *)libsym->name); +} + +int +library_symbol_clone(struct library_symbol *retp, struct library_symbol *libsym) +{ + const char *name; + if (strdup_if_owned(&name, libsym->name, libsym->own_name) < 0) + return -1; + + library_symbol_init(retp, (GElf_Addr)libsym->enter_addr, + name, libsym->own_name, libsym->plt_type, + libsym->is_weak); + retp->needs_init = libsym->needs_init; + return 0; +} + +int +library_symbol_cmp(struct library_symbol *a, struct library_symbol *b) +{ + if (a->enter_addr < b->enter_addr) + return -1; + if (a->enter_addr > b->enter_addr) + return 1; + if (a->name != NULL && b->name != NULL) + return strcmp(a->name, b->name); + if (a->name == NULL) { + if (b->name == NULL) + return 0; + return -1; + } + return 1; +} + +enum callback_status +library_symbol_equal_cb(struct library_symbol *libsym, void *u) +{ + struct library_symbol *standard = u; + return library_symbol_cmp(libsym, standard) == 0 ? CBS_STOP : CBS_CONT; +} + +void +library_init(struct library *lib, const char *name, int own_name) +{ + lib->next = NULL; + lib->name = name; + lib->own_name = own_name; + lib->symbols = NULL; +} + +int +library_clone(struct library *retp, struct library *lib) +{ + const char *name; + if (strdup_if_owned(&name, lib->name, lib->own_name) < 0) + return -1; + + library_init(retp, lib->name, lib->own_name); + + struct library_symbol *it; + struct library_symbol **nsymp = &retp->symbols; + for (it = lib->symbols; it != NULL; it = it->next) { + *nsymp = malloc(sizeof(**nsymp)); + if (*nsymp == NULL + || library_symbol_clone(*nsymp, it) < 0) { + /* Release what we managed to allocate. */ + library_destroy(retp); + return -1; + } + + nsymp = &(*nsymp)->next; + } + return 0; +} + +void +library_destroy(struct library *lib) +{ + if (lib == NULL) + return; + library_set_name(lib, NULL, 0); + + struct library_symbol *sym; + for (sym = lib->symbols; sym != NULL; ) { + struct library_symbol *next = sym->next; + library_symbol_destroy(sym); + free(sym); + sym = next; + } +} + +void +library_set_name(struct library *lib, const char *new_name, int own_name) +{ + if (lib->own_name) + free((char *)lib->name); + lib->name = new_name; + lib->own_name = own_name; +} + +struct library_symbol * +library_each_symbol(struct library *lib, struct library_symbol *it, + enum callback_status (*cb)(struct library_symbol *, void *), + void *data) +{ + if (it == NULL) + it = lib->symbols; + + while (it != NULL) { + struct library_symbol *next = it->next; + + switch ((*cb)(it, data)) { + case CBS_STOP: + return it; + case CBS_CONT: + break; + } + + it = next; + } + + return NULL; +} + +void +library_add_symbol(struct library *lib, struct library_symbol *sym) +{ + sym->next = lib->symbols; + lib->symbols = sym; +} + +enum callback_status +library_named_cb(struct Process *proc, struct library *lib, void *name) +{ + if (name == lib->name + || strcmp(lib->name, (char *)name) == 0) + return CBS_STOP; + else + return CBS_CONT; +} + +enum callback_status +library_with_base_cb(struct Process *proc, struct library *lib, void *basep) +{ + return lib->base == *(target_address_t *)basep ? CBS_STOP : CBS_CONT; +} diff --git a/library.h b/library.h new file mode 100644 index 0000000..88da6c5 --- /dev/null +++ b/library.h @@ -0,0 +1,149 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2006 Paul Gilliam + * + * 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 _LIBRARY_H_ +#define _LIBRARY_H_ + +#include +#include // XXX + +struct Process; +struct library; + +enum toplt { + LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */ + LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ + LS_TOPLT_POINT /* PLT for this symbol is a non-executable. */ +}; + +/* We should in general be able to trace 64-bit processes with 32-bit + * ltrace. (At least PPC has several PTRACE requests related to + * tracing 64-on-32, so presumably it should be possible.) But ltrace + * is currently hopelessly infested with using void* for host address. + * So keep with it, for now. */ +typedef void *target_address_t; + +struct library_symbol { + struct library_symbol *next; + const char *name; + target_address_t enter_addr; + enum toplt plt_type; + char needs_init; + char is_weak; + char own_name; +}; + +/* Init LIBSYM. NAME will be freed when LIBSYM is destroyed if + * OWN_NAME. */ +/* XXX note that we shouldn't use GElf_Addr for ADDR either. The fact + * that Elf is used is a back-end detail. At least ltrace pretends + * that it would like to be cross-platform like that one day. */ +void library_symbol_init(struct library_symbol *libsym, + GElf_Addr addr, const char *name, int own_name, + enum toplt type_of_plt, int is_weak); + +/* Copy library symbol SYM into the area pointed-to by RETP. Return 0 + * on success or a negative value on failure. */ +int library_symbol_clone(struct library_symbol *retp, + struct library_symbol *sym); + +/* Destroy library symbol. This essentially just frees name if it's + * owned. It doesn't free the memory associated with SYM pointer + * itself. Returns 0 on success or a negative value in case of an + * error (which would be an out of memory condition). */ +void library_symbol_destroy(struct library_symbol *sym); + +/* Compare two library symbols. Returns a negative value, 0, or a + * positive value, much like strcmp. The function compares symbol + * addresses, and if those are equal, it compares symbol names. If + * those are equal, too, the symbols are considered equal. */ +int library_symbol_cmp(struct library_symbol *a, struct library_symbol *b); + +/* A function that can be used as library_each_symbol callback. Looks + * for a symbol SYM for which library_symbol_cmp(SYM, STANDARD) + * returns 0. */ +enum callback_status library_symbol_equal_cb(struct library_symbol *libsym, + void *standard); + +/* XXX we might consider sharing libraries across processes. Things + * like libc will be opened by every single process, no point cloning + * these everywhere. But for now, keep the ownership structure + * simple. */ +struct library { + struct library *next; + + /* Address where the library is mapped. Two library objects + * are considered equal, if they have the same base. */ + target_address_t base; + + /* Absolute address of the entry point. Useful for main + * binary, though I the value might be useful for the dynamic + * linker, too (in case we ever want to do early process + * tracing). */ + target_address_t entry; + + /* Symbols associated with the library. */ + struct library_symbol *symbols; + const char *name; + char own_name; +}; + +/* Init LIB. NAME will be freed when LIB is destroyed if + * OWN_NAME. */ +void library_init(struct library *lib, const char *name, int own_name); + +/* Initialize RETP to a library identical to LIB. Symbols are not + * shared, but copied over. Returns 0 on success and a negative value + * in case of failure. */ +int library_clone(struct library *retp, struct library *lib); + +/* Destroy library. Doesn't free LIB itself. */ +void library_destroy(struct library *lib); + +/* Set library name. Frees the old name if necessary. */ +void library_set_name(struct library *lib, const char *new_name, int own_name); + +/* Iterate through list of symbols of library LIB. Restarts are + * supported via START (see each_process for details of iteration + * interface). */ +struct library_symbol *library_each_symbol + (struct library *lib, struct library_symbol *start, + enum callback_status (*cb)(struct library_symbol *, void *), + void *data); + +/* Add a new symbol SYM to LIB. SYM is assumed owned, we need to + * overwrite SYM->next. */ +void library_add_symbol(struct library *lib, struct library_symbol *sym); + +/* A function that can be used as proc_each_library callback. Looks + * for a library with the name passed in DATA. PROC is ignored. */ +enum callback_status library_named_cb(struct Process *proc, + struct library *lib, void *name); + +/* A function that can be used as proc_each_library callback. Looks + * for a library with given base. + * + * NOTE: The base is passed as a POINTER to target_address_t (that + * because in general, target_address_t doesn't fit in void*). */ +enum callback_status library_with_base_cb(struct Process *proc, + struct library *lib, void *basep); + +#endif /* _LIBRARY_H_ */ diff --git a/ltrace-elf.c b/ltrace-elf.c index d3d67d5..7d2ba5b 100644 --- a/ltrace-elf.c +++ b/ltrace-elf.c @@ -14,16 +14,7 @@ #include "common.h" #include "proc.h" - -void do_close_elf(struct ltelf *lte); -void add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, - enum toplt type_of_plt, int is_weak); -int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym); -static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr); - -struct library_symbol *library_symbols = NULL; -struct ltelf main_lte; +#include "library.h" #ifdef PLT_REINITALISATION_BP extern char *PLTs_initialized_by_here; @@ -54,7 +45,7 @@ static int maybe_pick_section(GElf_Addr addr, Elf_Scn *in_sec, GElf_Shdr *in_shdr, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) { - if (inside (addr, in_shdr)) { + if (inside(addr, in_shdr)) { *tgt_sec = in_sec; *tgt_shdr = *in_shdr; return 1; @@ -180,10 +171,13 @@ open_elf(struct ltelf *lte, const char *filename) return 0; } +/* XXX temporarily non-static */ int -do_init_elf(struct ltelf *lte, const char *filename) { +do_init_elf(struct ltelf *lte, const char *filename) +{ int i; GElf_Addr relplt_addr = 0; + GElf_Addr soname_offset = 0; size_t relplt_size = 0; debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); @@ -195,6 +189,21 @@ do_init_elf(struct ltelf *lte, const char *filename) { Elf_Data *plt_data = NULL; GElf_Addr ppcgot = 0; + /* For non-DSO objects, find out the base address. */ + if (lte->ehdr.e_type == ET_EXEC) { + GElf_Phdr phdr; + for (i = 0; gelf_getphdr (lte->elf, i, &phdr) != NULL; ++i) { + if (phdr.p_type == PT_LOAD) { + fprintf(stderr, " + loadable segment at %#lx\n", + phdr.p_vaddr); + lte->base_addr = phdr.p_vaddr; + break; + } + } + } + + lte->entry_addr = lte->ehdr.e_entry; + for (i = 1; i < lte->ehdr.e_shnum; ++i) { Elf_Scn *scn; GElf_Shdr shdr; @@ -268,6 +277,9 @@ do_init_elf(struct ltelf *lte, const char *filename) { size_t j; lte->dyn_addr = shdr.sh_addr; + fprintf(stderr, "dyn_addr = %#lx\n", lte->dyn_addr); + extern void *dyn_addr; + dyn_addr = (void *)lte->dyn_addr; lte->dyn_sz = shdr.sh_size; data = elf_getdata(scn, NULL); @@ -317,75 +329,10 @@ do_init_elf(struct ltelf *lte, const char *filename) { else if (dyn.d_tag == DT_PPC_GOT) { ppcgot = dyn.d_un.d_val; debug(1, "ppcgot %#" PRIx64, ppcgot); + } else if (dyn.d_tag == DT_SONAME) { + soname_offset = dyn.d_un.d_val; } } - } else if (shdr.sh_type == SHT_HASH) { - Elf_Data *data; - size_t j; - - lte->hash_type = SHT_HASH; - - data = elf_getdata(scn, NULL); - if (data == NULL || elf_getdata(scn, data) != NULL - || data->d_off || data->d_size != shdr.sh_size) - error(EXIT_FAILURE, 0, - "Couldn't get .hash data from \"%s\"", - filename); - - if (shdr.sh_entsize == 4) { - /* Standard conforming ELF. */ - if (data->d_type != ELF_T_WORD) - error(EXIT_FAILURE, 0, - "Couldn't get .hash data from \"%s\"", - filename); - lte->hash = (Elf32_Word *) data->d_buf; - } else if (shdr.sh_entsize == 8) { - /* Alpha or s390x. */ - Elf32_Word *dst, *src; - size_t hash_count = data->d_size / 8; - - lte->hash = (Elf32_Word *) - malloc(hash_count * sizeof(Elf32_Word)); - if (lte->hash == NULL) - error(EXIT_FAILURE, 0, - "Couldn't convert .hash section from \"%s\"", - filename); - lte->lte_flags |= LTE_HASH_MALLOCED; - dst = lte->hash; - src = (Elf32_Word *) data->d_buf; - if ((data->d_type == ELF_T_WORD - && __BYTE_ORDER == __BIG_ENDIAN) - || (data->d_type == ELF_T_XWORD - && lte->ehdr.e_ident[EI_DATA] == - ELFDATA2MSB)) - ++src; - for (j = 0; j < hash_count; ++j, src += 2) - *dst++ = *src; - } else - error(EXIT_FAILURE, 0, - "Unknown .hash sh_entsize in \"%s\"", - filename); - } else if (shdr.sh_type == SHT_GNU_HASH - && lte->hash == NULL) { - Elf_Data *data; - - lte->hash_type = SHT_GNU_HASH; - - if (shdr.sh_entsize != 0 - && shdr.sh_entsize != 4) { - error(EXIT_FAILURE, 0, - ".gnu.hash sh_entsize in \"%s\" " - "should be 4, but is %#" PRIx64, - filename, shdr.sh_entsize); - } - - data = loaddata(scn, &shdr); - if (data == NULL) - error(EXIT_FAILURE, 0, - "Couldn't get .gnu.hash data from \"%s\"", - filename); - - lte->hash = (Elf32_Word *) data->d_buf; } else if (shdr.sh_type == SHT_PROGBITS || shdr.sh_type == SHT_NOBITS) { if (strcmp(name, ".plt") == 0) { @@ -465,193 +412,24 @@ do_init_elf(struct ltelf *lte, const char *filename) { debug(1, "%s %zd PLT relocations", filename, lte->relplt_count); } + + if (soname_offset != 0) + lte->soname = lte->dynstr + soname_offset; + return 0; } +/* XXX temporarily non-static */ void do_close_elf(struct ltelf *lte) { debug(DEBUG_FUNCTION, "do_close_elf()"); - if (lte->lte_flags & LTE_HASH_MALLOCED) - free((char *)lte->hash); elf_end(lte->elf); close(lte->fd); } -void -library_symbol_init(struct library_symbol *libsym, - GElf_Addr addr, const char *name, - enum toplt type_of_plt, int is_weak) -{ - libsym->needs_init = 0; - libsym->is_weak = is_weak; - libsym->plt_type = type_of_plt; - libsym->name = name; - libsym->enter_addr = (void *)(uintptr_t)addr; - libsym->next = NULL; -} - -static struct library_symbol * -create_library_symbol(const char *name, GElf_Addr addr, - enum toplt type_of_plt, int is_weak) -{ - size_t namel = strlen(name) + 1; - struct library_symbol * libsym = calloc(sizeof(*libsym) + namel, 1); - if (libsym == NULL) { - perror("create_library_symbol"); - return NULL; - } - memcpy(libsym + 1, name, namel); - library_symbol_init(libsym, addr, - (char *)(libsym + 1), type_of_plt, is_weak); - - return libsym; -} - -void -add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, - enum toplt type_of_plt, int is_weak) -{ - debug(DEBUG_FUNCTION, "add_library_symbol()"); - - struct library_symbol *s = create_library_symbol(name, addr, - type_of_plt, is_weak); - if (s == NULL) - error(EXIT_FAILURE, errno, "add_library_symbol failed"); - - s->needs_init = 1; - s->next = *library_symbolspp; - *library_symbolspp = s; - - debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name); -} - -struct library_symbol * -clone_library_symbol(struct library_symbol *libsym) -{ - GElf_Addr addr = (uintptr_t)libsym->enter_addr; - struct library_symbol *copy - = create_library_symbol(libsym->name, addr, - libsym->plt_type, libsym->is_weak); - if (copy != NULL) - copy->needs_init = libsym->needs_init; - - return copy; -} - -void -destroy_library_symbol(struct library_symbol * sym) -{ - free(sym); -} - -void -destroy_library_symbol_chain(struct library_symbol * sym) -{ - while (sym != NULL) { - struct library_symbol * next = sym->next; - destroy_library_symbol(sym); - sym = next; - } -} - -/* stolen from elfutils-0.123 */ -static unsigned long -private_elf_gnu_hash(const char *name) { - unsigned long h = 5381; - const unsigned char *string = (const unsigned char *)name; - unsigned char c; - for (c = *string; c; c = *++string) - h = h * 33 + c; - return h & 0xffffffff; -} - -static int -symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym, - size_t symidx, const char *name) -{ - GElf_Sym tmp_sym; - GElf_Sym *tmp; - - tmp = (sym) ? (sym) : (&tmp_sym); - - if (gelf_getsym(lte[lte_i].dynsym, symidx, tmp) == NULL) - error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym"); - else { - tmp->st_value += lte[lte_i].base_addr; - debug(2, "symbol found: %s, %zd, %#" PRIx64, - name, lte_i, tmp->st_value); - } - return tmp->st_value != 0 - && tmp->st_shndx != SHN_UNDEF - && strcmp(name, lte[lte_i].dynstr + tmp->st_name) == 0; -} - -int -in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym) { - size_t i; - unsigned long hash; - unsigned long gnu_hash; - - if (!count) - return 1; - -#ifdef ELF_HASH_TAKES_CHARP - hash = elf_hash(name); -#else - hash = elf_hash((const unsigned char *)name); -#endif - gnu_hash = private_elf_gnu_hash(name); - - for (i = 0; i < count; ++i) { - if (lte[i].hash == NULL) - continue; - - if (lte[i].hash_type == SHT_GNU_HASH) { - Elf32_Word * hashbase = lte[i].hash; - Elf32_Word nbuckets = *hashbase++; - Elf32_Word symbias = *hashbase++; - Elf32_Word bitmask_nwords = *hashbase++; - Elf32_Word * buckets; - Elf32_Word * chain_zero; - Elf32_Word bucket; - - // +1 for skipped `shift' - hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1; - buckets = hashbase; - hashbase += nbuckets; - chain_zero = hashbase - symbias; - bucket = buckets[gnu_hash % nbuckets]; - - if (bucket != 0) { - const Elf32_Word *hasharr = &chain_zero[bucket]; - do - if ((*hasharr & ~1u) == (gnu_hash & ~1u)) { - int symidx = hasharr - chain_zero; - if (symbol_matches(lte, i, - sym, symidx, - name)) - return 1; - } - while ((*hasharr++ & 1u) == 0); - } - } else { - Elf32_Word nbuckets, symndx; - Elf32_Word *buckets, *chain; - nbuckets = lte[i].hash[0]; - buckets = <e[i].hash[2]; - chain = <e[i].hash[2 + nbuckets]; - - for (symndx = buckets[hash % nbuckets]; - symndx != STN_UNDEF; symndx = chain[symndx]) - if (symbol_matches(lte, i, sym, symndx, name)) - return 1; - } - } - return 0; -} - -static GElf_Addr +/* XXX non-static for now, as it's not called anywhere. But we want + * this code around. */ +GElf_Addr opd2addr(struct ltelf *lte, GElf_Addr addr) { #ifdef ARCH_SUPPORTS_OPD unsigned long base, offset; @@ -670,198 +448,101 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) { #endif } -struct library_symbol * -read_elf(Process *proc, GElf_Addr *entryp) +struct library * +ltelf_read_library(const char *filename, GElf_Addr base) { - struct ltelf lte[MAX_LIBRARIES + 1]; - size_t i; - struct opt_x_t *xptr; - struct opt_x_t *opt_x_loc = opt_x; - struct library_symbol **lib_tail = NULL; - int exit_out = 0; - int count = 0; - - debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename); - - memset(lte, 0, sizeof(lte)); - library_symbols = NULL; - library_num = 0; - proc->libdl_hooked = 0; - - if (do_init_elf(lte, proc->filename)) + // XXX we leak LTE contents + struct ltelf lte = { + .base_addr = base, + }; + if (do_init_elf(<e, filename) < 0) return NULL; - memcpy(&main_lte, lte, sizeof(struct ltelf)); - - if (opt_p && opt_p->pid > 0) { - linkmap_init(proc, lte); - proc->libdl_hooked = 1; + struct library *lib = malloc(sizeof(*lib)); + char *soname = NULL; + if (lib == NULL) { + fail: + free(soname); + library_destroy(lib); + free(lib); + lib = NULL; + goto done; } - proc->e_machine = lte->ehdr.e_machine; - - for (i = 0; i < library_num; ++i) { - if (do_init_elf(<e[i + 1], library[i])) - error(EXIT_FAILURE, errno, "Can't open \"%s\"", - library[i]); + if (lte.soname != NULL) { + soname = strdup(lte.soname); + if (soname == NULL) + goto fail; } - if (!options.no_plt) { -#ifdef __mips__ - // MIPS doesn't use the PLT and the GOT entries get changed - // on startup. - for(i=lte->mips_gotsym; idynsym_count;i++){ - GElf_Sym sym; - const char *name; - GElf_Addr addr = arch_plt_sym_val(lte, i, 0); - if (gelf_getsym(lte->dynsym, i, &sym) == NULL){ - error(EXIT_FAILURE, 0, - "Couldn't get relocation from \"%s\"", - proc->filename); - } - name=lte->dynstr+sym.st_name; - if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){ - debug(2,"sym %s not a function",name); - continue; - } - add_library_symbol(addr, name, &library_symbols, 0, - ELF64_ST_BIND(sym.st_info) != 0); - if (!lib_tail) - lib_tail = &(library_symbols->next); - } -#else - for (i = 0; i < lte->relplt_count; ++i) { - GElf_Rel rel; - GElf_Rela rela; - GElf_Sym sym; - GElf_Addr addr; - void *ret; - const char *name; - - if (lte->relplt->d_type == ELF_T_REL) { - ret = gelf_getrel(lte->relplt, i, &rel); - rela.r_offset = rel.r_offset; - rela.r_info = rel.r_info; - rela.r_addend = 0; - } else - ret = gelf_getrela(lte->relplt, i, &rela); - - if (ret == NULL - || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count - || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info), - &sym) == NULL) - error(EXIT_FAILURE, 0, - "Couldn't get relocation from \"%s\"", - proc->filename); - - name = lte->dynstr + sym.st_name; - count = library_num ? library_num+1 : 0; + library_init(lib, soname, soname != NULL); + lib->entry = (target_address_t)lte.entry_addr; + lib->base = (target_address_t)lte.base_addr; - if (in_load_libraries(name, lte, count, NULL)) { - enum toplt pltt; - if (sym.st_value == 0 && lte->plt_stub_vma != 0) { - pltt = LS_TOPLT_EXEC; - addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i; - } - else { - pltt = PLTS_ARE_EXECUTABLE(lte) - ? LS_TOPLT_EXEC : LS_TOPLT_POINT; - addr = arch_plt_sym_val(lte, i, &rela); - } - - add_library_symbol(addr, name, &library_symbols, pltt, - ELF64_ST_BIND(sym.st_info) == STB_WEAK); - if (!lib_tail) - lib_tail = &(library_symbols->next); - } - } -#endif // !__mips__ - } else { - lib_tail = &library_symbols; - } - - for (i = 0; i < lte->symtab_count; ++i) { + size_t i; + for (i = 0; i < lte.relplt_count; ++i) { + GElf_Rel rel; + GElf_Rela rela; GElf_Sym sym; GElf_Addr addr; - const char *name; + void *ret; - if (gelf_getsym(lte->symtab, i, &sym) == NULL) - error(EXIT_FAILURE, 0, - "Couldn't get symbol from \"%s\"", - proc->filename); - - name = lte->strtab + sym.st_name; - addr = sym.st_value; - if (!addr) - continue; - - for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if (xptr->name && strcmp(xptr->name, name) == 0) { - /* FIXME: Should be able to use &library_symbols as above. But - when you do, none of the real library symbols cause breaks. */ - add_library_symbol(opd2addr(lte, addr), - name, lib_tail, LS_TOPLT_NONE, 0); - xptr->found = 1; - break; - } - } - - unsigned found_count = 0; + if (lte.relplt->d_type == ELF_T_REL) { + ret = gelf_getrel(lte.relplt, i, &rel); + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } else { + ret = gelf_getrela(lte.relplt, i, &rela); + } - for (xptr = opt_x_loc; xptr; xptr = xptr->next) { - if (xptr->found) - continue; + if (ret == NULL + || ELF64_R_SYM(rela.r_info) >= lte.dynsym_count + || gelf_getsym(lte.dynsym, ELF64_R_SYM(rela.r_info), + &sym) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get relocation from \"%s\"", + filename); - GElf_Sym sym; - GElf_Addr addr; - if (in_load_libraries(xptr->name, lte, library_num+1, &sym)) { - debug(2, "found symbol %s @ %#" PRIx64 ", adding it.", - xptr->name, sym.st_value); - addr = sym.st_value; - if (ELF32_ST_TYPE (sym.st_info) == STT_FUNC) { - add_library_symbol(addr, xptr->name, lib_tail, LS_TOPLT_NONE, 0); - xptr->found = 1; - found_count++; - } - } - if (found_count == opt_x_cnt){ - debug(2, "done, found everything: %d\n", found_count); - break; + /* We will destroy the ELF object at the end of the + * scope. We need to copy the name for our purposes. + * XXX consider just keeping the ELF around. */ + char *name = strdup(lte.dynstr + sym.st_name); + if (name == NULL) { + fail2: + free(name); + goto fail; } - } - - 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) { - fprintf (stderr, "WARNING: Using e_ent" - "ry from elf header (%p) for " - "address of \"%s\"\n", (void*) - (long) lte->ehdr.e_entry, - PLTs_initialized_by_here); - continue; - } - badthing = "ERROR"; - exit_out = 1; - } -#endif - fprintf (stderr, - "%s: Couldn't find symbol \"%s\" in file \"%s\" assuming it will be loaded by libdl!" - "\n", badthing, xptr->name, proc->filename); + enum toplt pltt; + if (sym.st_value == 0 && lte.plt_stub_vma != 0) { + pltt = LS_TOPLT_EXEC; + addr = lte.plt_stub_vma + PPC_PLT_STUB_SIZE * i; + } else { + pltt = PLTS_ARE_EXECUTABLE(<e) + ? LS_TOPLT_EXEC : LS_TOPLT_POINT; + addr = arch_plt_sym_val(<e, i, &rela); } - if (exit_out) { - exit (1); + + struct library_symbol *libsym = malloc(sizeof(*libsym)); + if (libsym == NULL) + goto fail2; + library_symbol_init(libsym, addr, name, 1, pltt, + ELF64_ST_BIND(sym.st_info) == STB_WEAK); + library_add_symbol(lib, libsym); } - for (i = 0; i < library_num + 1; ++i) - do_close_elf(<e[i]); +done: + do_close_elf(<e); + return lib; +} - return library_symbols; +struct library * +ltelf_read_main_binary(struct Process *proc, const char *path) +{ + fprintf(stderr, "ltelf_read_main_binary %d %s\n", proc->pid, path); + char *fname = pid2name(proc->pid); + struct library *lib = ltelf_read_library(fname, 0); + library_set_name(lib, path, 0); + return lib; } diff --git a/ltrace-elf.h b/ltrace-elf.h index 507a466..7616814 100644 --- a/ltrace-elf.h +++ b/ltrace-elf.h @@ -3,8 +3,16 @@ #include #include -#include "proc.h" +struct Process; +struct library; + +/* XXX Ok, the original idea was to separate the low-level ELF data + * from the abstract "struct library" object, but we use some of the + * following extensively in the back end. Not all though. So what we + * use should be move to struct library, and the rest of this + * structure maybe could be safely hidden in .c. How to integrate the + * arch-specific bits into struct library is unclear as of now. */ struct ltelf { int fd; Elf *elf; @@ -18,16 +26,16 @@ struct ltelf { size_t relplt_count; Elf_Data *symtab; const char *strtab; + const char *soname; size_t symtab_count; Elf_Data *opd; GElf_Addr *opd_addr; size_t opd_size; - Elf32_Word *hash; - int hash_type; int lte_flags; GElf_Addr dyn_addr; size_t dyn_sz; GElf_Addr base_addr; + GElf_Addr entry_addr; #ifdef __mips__ size_t pltgot_addr; size_t mips_local_gotno; @@ -37,22 +45,22 @@ struct ltelf { }; #define ELF_MAX_SEGMENTS 50 -#define LTE_HASH_MALLOCED 1 #define LTE_PLT_EXECUTABLE 2 -#define PLTS_ARE_EXECUTABLE(lte) ((lte->lte_flags & LTE_PLT_EXECUTABLE) != 0) +#define PLTS_ARE_EXECUTABLE(lte) (((lte)->lte_flags & LTE_PLT_EXECUTABLE) != 0) -extern size_t library_num; -extern char *library[MAX_LIBRARIES]; +int open_elf(struct ltelf *lte, const char *filename); -extern int open_elf(struct ltelf *lte, const char *filename); -extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp); +/* XXX is it possible to put breakpoints in VDSO and VSYSCALL + * pseudo-libraries? For now we assume that all libraries can be + * opened via a filesystem. BASE is ignored for ET_EXEC files. */ +struct library *ltelf_read_library(const char *filename, GElf_Addr base); -extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); +/* Create a library object representing the main binary. The entry + * point address is stored to *ENTRYP. */ +struct library *ltelf_read_main_binary(struct Process *proc, const char *path); -#ifndef SHT_GNU_HASH -#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ -#endif +GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); #if __WORDSIZE == 32 #define PRI_ELF_ADDR PRIx32 diff --git a/options.c b/options.c index 74c28bd..eae797e 100644 --- a/options.c +++ b/options.c @@ -9,6 +9,7 @@ #include #include +#include #include "common.h" @@ -36,8 +37,6 @@ struct options_t options = { .follow = 0, /* trace child processes */ }; -char *library[MAX_LIBRARIES]; -size_t library_num = 0; static char *progname; /* Program name (`ltrace') */ int opt_i = 0; /* instruction pointer */ int opt_r = 0; /* print relative timestamp */ @@ -316,13 +315,8 @@ process_options(int argc, char **argv) { opt_i++; break; case 'l': - if (library_num == MAX_LIBRARIES) { - fprintf(stderr, - "Too many libraries. Maximum is %i.\n", - MAX_LIBRARIES); - exit(1); - } - library[library_num++] = optarg; + assert(!"-l support not yet implemented"); + abort(); break; case 'L': options.libcalls = 0; diff --git a/output.h b/output.h index 10a144f..8e4fa46 100644 --- a/output.h +++ b/output.h @@ -1,3 +1,6 @@ -void output_line(Process *proc, char *fmt, ...); -void output_left(enum tof type, Process *proc, const char *function_name); -void output_right(enum tof type, Process *proc, const char *function_name); +struct Process; +void output_line(struct Process *proc, char *fmt, ...); +void output_left(enum tof type, struct Process *proc, + const char *function_name); +void output_right(enum tof type, struct Process *proc, + const char *function_name); diff --git a/proc.c b/proc.c index 5360b0b..1984d12 100644 --- a/proc.c +++ b/proc.c @@ -17,40 +17,189 @@ #include "breakpoint.h" #include "proc.h" -Process * -open_program(char *filename, pid_t pid, int enable) { - Process *proc; - assert(pid != 0); - proc = calloc(sizeof(Process), 1); - if (!proc) { - perror("malloc"); - exit(1); - } +static int +process_bare_init(struct Process *proc, const char *filename, pid_t pid) +{ + fprintf(stderr, "process_bare_init %s %d\n", filename, pid); + memset(proc, 0, sizeof(*proc)); proc->filename = strdup(filename); + if (proc->filename == NULL) { + fail: + free(proc->filename); + if (proc->breakpoints != NULL) + dict_clear(proc->breakpoints); + return -1; + } + + /* Add process so that we know who the leader is. */ proc->pid = pid; + add_process(proc); + if (proc->leader == NULL) + goto fail; + + if (proc->leader == proc) { + proc->breakpoints = dict_init(dict_key2hash_int, + dict_key_cmp_int); + if (proc->breakpoints == NULL) + goto fail; + } else { + proc->breakpoints = NULL; + } + #if defined(HAVE_LIBUNWIND) proc->unwind_priv = _UPT_create(pid); proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0); #endif /* defined(HAVE_LIBUNWIND) */ - add_process(proc); - if (proc->leader == NULL) { + return 0; +} + +static void +process_bare_destroy(struct Process *proc) +{ + free(proc->filename); + dict_clear(proc->breakpoints); + remove_process(proc); +} + +int +process_init(struct Process *proc, const char *filename, pid_t pid, int enable) +{ + fprintf(stderr, "process_init %s %d enable=%d\n", filename, pid, enable); + if (process_bare_init(proc, filename, pid) < 0) { + error(0, errno, "init process %d", pid); + return -1; + } + + if (proc->leader == proc && breakpoints_init(proc, enable) < 0) { + fprintf(stderr, "failed to init breakpoints %d\n", + proc->pid); + process_bare_destroy(proc); + return -1; + } + + return 0; +} + +struct Process * +open_program(const char *filename, pid_t pid, int enable) +{ + fprintf(stderr, "open_program %s %d enable=%d\n", + filename, pid, enable); + assert(pid != 0); + struct Process *proc = malloc(sizeof(*proc)); + if (proc == NULL + || process_init(proc, filename, pid, enable) < 0) { free(proc); return NULL; } + return 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; +struct clone_single_bp_data { + struct Process *old_proc; + struct Process *new_proc; + int error; +}; + +struct find_symbol_data { + struct library_symbol *old_libsym; + struct library_symbol *found_libsym; +}; + +static enum callback_status +find_sym_in_lib(struct Process *proc, struct library *lib, void *u) +{ + struct find_symbol_data *fs = u; + fs->found_libsym + = library_each_symbol(lib, NULL, library_symbol_equal_cb, + fs->old_libsym); + return fs->found_libsym != NULL ? CBS_STOP : CBS_CONT; +} + +static void +clone_single_bp(void *key, void *value, void *u) +{ + target_address_t addr = (target_address_t)key; + struct breakpoint *bp = value; + struct clone_single_bp_data *data = u; + + /* Find library and symbol that this symbol was linked to. */ + struct library_symbol *libsym = bp->libsym; + struct library *lib = NULL; + if (libsym != NULL) { + struct find_symbol_data f_data = { + .old_libsym = libsym, + }; + lib = proc_each_library(data->old_proc, NULL, + find_sym_in_lib, &f_data); + assert(lib != NULL); + libsym = f_data.found_libsym; + } + + /* LIB and LIBSYM now hold the new library and symbol that + * correspond to the original breakpoint. Now we can do the + * clone itself. */ + struct breakpoint *clone = malloc(sizeof(*clone)); + if (clone == NULL + || breakpoint_init(clone, data->new_proc, addr, + libsym, bp->cbs) < 0) { + data->error = -1; + return; + } +} + +int +process_clone(struct Process *retp, struct Process *proc, pid_t pid) +{ + if (process_bare_init(retp, proc->filename, pid) < 0) { + fail: + error(0, errno, "clone process %d->%d", proc->pid, pid); + return -1; + } + + /* For non-leader processes, that's all we need to do. */ + if (proc->leader != proc) + return 0; + + /* Clone symbols first so that we can clone and relink + * breakpoints. */ + struct library *lib; + struct library **nlibp = &retp->libraries; + for (lib = proc->libraries; lib != NULL; lib = lib->next) { + *nlibp = malloc(sizeof(**nlibp)); + if (*nlibp == NULL + || library_clone(*nlibp, lib) < 0) { + fail2: + process_bare_destroy(retp); + + /* Error when cloning. Unroll what was done. */ + for (lib = retp->libraries; lib != NULL; ) { + struct library *next = lib->next; + library_destroy(lib); + free(lib); + lib = next; + } + goto fail; } + + nlibp = &(*nlibp)->next; } - return proc; + /* Now clone breakpoints. Symbol relinking is done in + * clone_single_bp. */ + struct clone_single_bp_data data = { + .old_proc = proc, + .new_proc = retp, + .error = 0, + }; + dict_apply_to_all(proc->breakpoints, &clone_single_bp, &data); + + if (data.error < 0) + goto fail2; + + return 0; } static int @@ -76,11 +225,11 @@ open_one_pid(pid_t pid) return 0; } -static enum pcb_status +static enum callback_status start_one_pid(Process * proc, void * data) { continue_process(proc->pid); - return pcb_cont; + return CBS_CONT; } void @@ -144,11 +293,11 @@ open_pid(pid_t pid) each_task(pid2proc(pid)->leader, start_one_pid, NULL); } -static enum pcb_status +static enum callback_status find_proc(Process * proc, void * data) { pid_t pid = (pid_t)(uintptr_t)data; - return proc->pid == pid ? pcb_stop : pcb_cont; + return proc->pid == pid ? CBS_STOP : CBS_CONT; } Process * @@ -180,33 +329,43 @@ unlist_process(Process * proc) } } -Process * -each_process(Process * proc, - enum pcb_status (* cb)(Process * proc, void * data), - void * data) +struct Process * +each_process(struct Process *it, + enum callback_status(*cb)(struct Process *proc, void *data), + void *data) { - Process * it = proc ?: list_of_processes; + if (it == NULL) + it = list_of_processes; for (; it != NULL; ) { /* Callback might call remove_process. */ Process * next = it->next; - if ((*cb) (it, data) == pcb_stop) + switch ((*cb)(it, data)) { + case CBS_STOP: return it; + case CBS_CONT: + break; + } it = next; } return NULL; } Process * -each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data), - void * data) +each_task(struct Process *it, + enum callback_status(*cb)(struct Process *proc, void *data), + void *data) { if (it != NULL) { Process * leader = it->leader; for (; it != NULL && it->leader == leader; ) { /* Callback might call remove_process. */ Process * next = it->next; - if ((*cb) (it, data) == pcb_stop) + switch ((*cb)(it, data)) { + case CBS_STOP: return it; + case CBS_CONT: + break; + } it = next; } } @@ -216,9 +375,11 @@ each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data), void add_process(Process * proc) { + fprintf(stderr, "add_process %d\n", proc->pid); Process ** leaderp = &list_of_processes; if (proc->pid) { pid_t tgid = process_leader(proc->pid); + fprintf(stderr, " + leader is %d\n", tgid); if (tgid == 0) /* Must have been terminated before we managed * to fully attach. */ @@ -253,13 +414,13 @@ change_process_leader(Process * proc, Process * leader) *leaderp = proc; } -static enum pcb_status -clear_leader(Process * proc, void * data) +static enum callback_status +clear_leader(struct Process *proc, void *data) { debug(DEBUG_FUNCTION, "detach_task %d from leader %d", proc->pid, proc->leader->pid); proc->leader = NULL; - return pcb_cont; + return CBS_CONT; } static enum ecb_status @@ -311,3 +472,69 @@ destroy_event_handler(Process * proc) free(handler); proc->event_handler = NULL; } + +static enum callback_status +breakpoint_for_symbol(struct library_symbol *libsym, void *data) +{ + struct Process *proc = data; + fprintf(stderr, " %s@%p\n", libsym->name, libsym->enter_addr); + + if (insert_breakpoint(proc, libsym->enter_addr, libsym, 1) == NULL) + return CBS_STOP; + + return CBS_CONT; +} + +void +proc_add_library(struct Process *proc, struct library *lib) +{ + assert(lib->next == NULL); + lib->next = proc->libraries; + proc->libraries = lib; + fprintf(stderr, "=== Added library %s@%p to %d:\n", + lib->name, lib->base, proc->pid); + + struct library_symbol *libsym = NULL; + while ((libsym = library_each_symbol(lib, libsym, breakpoint_for_symbol, + proc)) != NULL) { + error(0, errno, "insert breakpoint for %s", libsym->name); + libsym = libsym->next; + } +} + +int +proc_remove_library(struct Process *proc, struct library *lib) +{ + struct library **libp; + for (libp = &proc->libraries; *libp != NULL; libp = &(*libp)->next) + if (*libp == lib) { + *libp = lib->next; + return 0; + } + return -1; +} + +struct library * +proc_each_library(struct Process *proc, struct library *it, + enum callback_status (*cb)(struct Process *proc, + struct library *lib, void *data), + void *data) +{ + if (it == NULL) + it = proc->libraries; + + while (it != NULL) { + struct library *next = it->next; + + switch (cb(proc, it, data)) { + case CBS_STOP: + return it; + case CBS_CONT: + break; + } + + it = next; + } + + return NULL; +} diff --git a/proc.h b/proc.h index 9b80556..0f670e5 100644 --- a/proc.h +++ b/proc.h @@ -1,3 +1,25 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2010 Joe Damato + * Copyright (C) 1998,2001,2008,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 _PROC_H_ #define _PROC_H_ @@ -8,6 +30,16 @@ #include "ltrace.h" #include "dict.h" +struct library; + +/* XXX Move this somewhere where it makes sense. When the mess in + * common.h is disentangled, that would actually be a good place for + * this. */ +enum callback_status { + CBS_STOP, /* The iteration should stop. */ + CBS_CONT, /* The iteration should continue. */ +}; + struct event_handler { /* Event handler that overrides the default one. Should * return NULL if the event was handled, otherwise the @@ -24,11 +56,6 @@ enum process_state { STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ }; -enum pcb_status { - pcb_stop, /* The iteration should stop. */ - pcb_cont, /* The iteration should continue. */ -}; - struct callstack_element { union { int syscall; @@ -65,9 +92,8 @@ struct Process { int callstack_depth; struct callstack_element callstack[MAX_CALLDEPTH]; - struct library_symbol * list_of_symbols; + struct library *libraries; - int libdl_hooked; /* Arch-dependent: */ void * debug; /* arch-dep process debug struct */ long debug_state; /* arch-dep debug state */ @@ -76,7 +102,6 @@ struct Process { void * return_addr; void * arch_ptr; short e_machine; - short need_to_reinitialize_breakpoints; #ifdef __arm__ int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ #endif @@ -104,19 +129,65 @@ struct Process { Process * leader; }; -Process * open_program(char *filename, pid_t pid, int init_breakpoints); +int process_init(struct Process *proc, + const char *filename, pid_t pid, int enable_breakpoints); + +Process * open_program(const char *filename, pid_t pid, int enable_breakpoints); void open_pid(pid_t pid); Process * pid2proc(pid_t pid); + +/* Clone the contents of PROC into the memory referenced by RETP. + * Returns 0 on success or a negative value on failure. */ +int process_clone(struct Process *retp, struct Process *proc, pid_t pid); + +/* Iterate through the processes that ltrace currently traces. CB is + * called for each process. Tasks are considered to be processes for + * the purpose of this iterator. + * + * Notes on this iteration interface: DATA is passed verbatim to CB. + * If CB returns CBS_STOP, the iteration stops and the current + * iterator is returned. That iterator can then be used to restart + * the iteration. If you don't want CB to see the same process more + * than once, restart with IT->next instead of just IT. NULL is + * returned when iteration ends. + * + * There's no provision for returning error states. Errors need to be + * signaled to the caller via DATA, together with any other data that + * the callback needs. */ Process *each_process(Process *start, - enum pcb_status (* cb)(Process *proc, void *data), + enum callback_status (*cb)(struct Process *proc, + void *data), void *data); + +/* Iterate through list of tasks of given process START. Normally you + * start the iteration by calling this on PROC->leader, the iterator + * doesn't do this for you (so as to support restarts). See above for + * details on the iteration interface. */ Process *each_task(Process *start, - enum pcb_status (* cb)(Process *proc, void *data), + enum callback_status (*cb)(struct Process *proc, + void *data), void *data); + void add_process(Process *proc); void change_process_leader(Process *proc, Process *leader); void remove_process(Process *proc); void install_event_handler(Process *proc, struct event_handler *handler); void destroy_event_handler(Process *proc); +/* Add a library LIB to the list of PROC's libraries. */ +void proc_add_library(struct Process *proc, struct library *lib); + +/* Remove LIB from list of PROC's libraries. Returns 0 if the library + * was found and unlinked, otherwise returns a negative value. */ +int proc_remove_library(struct Process *proc, struct library *lib); + +/* Iterate through the libraries of PROC. See each_process for + * detailed description of the iteration interface. */ +struct library *proc_each_library(struct Process *proc, struct library *start, + enum callback_status (*cb)(struct Process *p, + struct library *l, + void *data), + void *data); + + #endif /* _PROC_H_ */ diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h index 8f2dfb3..d50e439 100644 --- a/sysdeps/linux-gnu/arm/arch.h +++ b/sysdeps/linux-gnu/arm/arch.h @@ -9,3 +9,8 @@ #define LT_ELFCLASS ELFCLASS32 #define LT_ELF_MACHINE EM_ARM + +#define ARCH_HAVE_BREAKPOINT_DATA +struct arch_breakpoint_data { + int thumb_mode; +}; diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c index 493f973..b94e471 100644 --- a/sysdeps/linux-gnu/arm/breakpoint.c +++ b/sysdeps/linux-gnu/arm/breakpoint.c @@ -82,3 +82,14 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), current.l); } } + +int +arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) +{ + int thumb_mode = (int)addr & 1; + if (thumb_mode) + addr = (void *)((int)addr & ~1); + sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode; + proc->thumb_mode = 0; + return 0; +} diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c index ea4d3a6..22bc4bf 100644 --- a/sysdeps/linux-gnu/arm/regs.c +++ b/sysdeps/linux-gnu/arm/regs.c @@ -40,9 +40,15 @@ void * get_return_addr(Process *proc, void *stack_pointer) { long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); + /* Remember & unset the thumb mode bit. XXX This is really a + * bit of a hack, as we assume that the following + * insert_breakpoint call will be related to this address. + * This interface should really be get_return_breakpoint, or + * maybe install_return_breakpoint. */ proc->thumb_mode = addr & 1; if (proc->thumb_mode) addr &= ~1; + return (void *)addr; } diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c index 58eac8d..1012447 100644 --- a/sysdeps/linux-gnu/breakpoint.c +++ b/sysdeps/linux-gnu/breakpoint.c @@ -7,6 +7,7 @@ #include "arch.h" #include "breakpoint.h" #include "proc.h" +#include "library.h" #ifdef ARCH_HAVE_ENABLE_BREAKPOINT extern void arch_enable_breakpoint(pid_t, struct breakpoint *); diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c index 9c376f3..91d873e 100644 --- a/sysdeps/linux-gnu/events.c +++ b/sysdeps/linux-gnu/events.c @@ -22,10 +22,10 @@ static Event event; static Event * delayed_events = NULL; static Event * end_delayed_events = NULL; -static enum pcb_status +static enum callback_status first (Process * proc, void * data) { - return pcb_stop; + return CBS_STOP; } void @@ -175,14 +175,6 @@ next_event(void) get_arch_dep(event.proc); debug(3, "event from pid %u", pid); Process *leader = event.proc->leader; - if (leader == event.proc) { - 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; - } - } - } /* The process should be stopped after the waitpid call. But * when the whole thread group is terminated, we see diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c index 70bc19b..707d9d9 100644 --- a/sysdeps/linux-gnu/ppc/plt.c +++ b/sysdeps/linux-gnu/ppc/plt.c @@ -8,6 +8,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { return rela->r_offset; } +/* XXX Apparently PPC64 doesn't support PLT breakpoints. */ void * sym2addr(Process *proc, struct library_symbol *sym) { void *addr = sym->enter_addr; @@ -53,6 +54,8 @@ sym2addr(Process *proc, struct library_symbol *sym) { addr = (void *)pt_ret; } #else + /* XXX Um, so where exactly are we dealing with the non-secure + PLT thing? */ addr = (void *)pt_ret; #endif diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c index eba030f..4592924 100644 --- a/sysdeps/linux-gnu/proc.c +++ b/sysdeps/linux-gnu/proc.c @@ -19,6 +19,7 @@ #include "common.h" #include "breakpoint.h" #include "proc.h" +#include "library.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 @@ -78,27 +79,28 @@ find_line_starting(FILE * file, const char * prefix, size_t len) } static void -each_line_starting(FILE * file, const char *prefix, - enum pcb_status (*cb)(const char * line, const char * prefix, - void * data), - void * data) +each_line_starting(FILE *file, const char *prefix, + enum callback_status (*cb)(const char *line, + const char *prefix, + void *data), + void *data) { size_t len = strlen(prefix); char * line; while ((line = find_line_starting(file, prefix, len)) != NULL) { - enum pcb_status st = (*cb)(line, prefix, data); + enum callback_status st = (*cb)(line, prefix, data); free (line); - if (st == pcb_stop) + if (st == CBS_STOP) return; } } -static enum pcb_status -process_leader_cb(const char * line, const char * prefix, void * data) +static enum callback_status +process_leader_cb(const char *line, const char *prefix, void *data) { pid_t * pidp = data; *pidp = atoi(line + strlen(prefix)); - return pcb_stop; + return CBS_STOP; } pid_t @@ -114,13 +116,13 @@ process_leader(pid_t pid) return tgid; } -static enum pcb_status -process_stopped_cb(const char * line, const char * prefix, void * data) +static enum callback_status +process_stopped_cb(const char *line, const char *prefix, void *data) { char c = line[strlen(prefix)]; // t:tracing stop, T:job control stop *(int *)data = (c == 't' || c == 'T'); - return pcb_stop; + return CBS_STOP; } int @@ -136,15 +138,15 @@ process_stopped(pid_t pid) return is_stopped; } -static enum pcb_status -process_status_cb(const char * line, const char * prefix, void * data) +static enum callback_status +process_status_cb(const char *line, const char *prefix, void *data) { const char * status = line + strlen(prefix); const char c = *status; #define RETURN(C) do { \ *(enum process_status *)data = C; \ - return pcb_stop; \ + return CBS_STOP; \ } while (0) switch (c) { @@ -245,6 +247,8 @@ process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) static int find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { + fprintf(stderr, "find_dynamic_entry_addr %d %p %d\n", + proc->pid, pvAddr, d_tag); int i = 0, done = 0; ElfW(Dyn) entry; @@ -257,7 +261,9 @@ find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { while ((!done) && (i < ELF_MAX_SEGMENTS) && (sizeof(entry) == umovebytes(proc, pvAddr, &entry, sizeof(entry))) && (entry.d_tag != DT_NULL)) { + fprintf(stderr, " entry %ld %#lx\n", entry.d_tag, entry.d_un.d_val); if (entry.d_tag == d_tag) { + fprintf(stderr, " hit\n"); done = 1; *addr = (void *)entry.d_un.d_val; } @@ -275,15 +281,16 @@ find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { } } -struct cb_data { - const char *lib_name; - struct ltelf *lte; - ElfW(Addr) addr; - Process *proc; -}; +enum callback_status +find_library_addr(struct Process *proc, struct library *lib, void *data) +{ + target_address_t addr = (target_address_t)*(GElf_Addr *)data; + return lib->base == addr ? CBS_STOP : CBS_CONT; +} static void -crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), struct cb_data *data) { +crawl_linkmap(Process *proc, struct r_debug *dbg) +{ struct link_map rlm; char lib_name[BUFSIZ]; struct link_map *lm = NULL; @@ -311,19 +318,33 @@ crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), stru umovebytes(proc, rlm.l_name, lib_name, sizeof(lib_name)); - if (lib_name[0] == '\0') { - debug(2, "Library name is an empty string"); + debug(2, "Dispatching callback for: %s, " + "Loaded at 0x%" PRI_ELF_ADDR "\n", + lib_name, rlm.l_addr); + fprintf(stderr, "DSO addr=%#lx, name='%s'\n", rlm.l_addr, lib_name); + + /* Do we have that library already? */ + struct library *lib + = proc_each_library(proc, NULL, find_library_addr, + &rlm.l_addr); + if (lib != NULL) + continue; + + if (*lib_name == '\0') { + /* VDSO. No associated file, XXX but we might + * load it from the address space of the + * process. */ continue; } - if (callback) { - debug(2, "Dispatching callback for: %s, " - "Loaded at 0x%" PRI_ELF_ADDR "\n", - lib_name, rlm.l_addr); - data->addr = rlm.l_addr; - data->lib_name = lib_name; - callback(data); + lib = ltelf_read_library(lib_name, rlm.l_addr); + if (lib == NULL) { + error(0, errno, "Couldn't load ELF object %s\n", + lib_name); + continue; } + + proc_add_library(proc, lib); } return; } @@ -349,63 +370,11 @@ load_debug_struct(Process *proc) { } 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); - } -} - -void -arch_check_dbg(Process *proc) { +rdebug_callback_hit(struct breakpoint *bp, struct Process *proc) +{ + fprintf(stderr, "======= HIT\n"); struct r_debug *dbg = NULL; - struct cb_data data; + //struct cb_data data; debug(DEBUG_FUNCTION, "arch_check_dbg"); @@ -418,8 +387,9 @@ arch_check_dbg(Process *proc) { debug(2, "Linkmap is now consistent"); if (proc->debug_state == RT_ADD) { debug(2, "Adding DSO to linkmap"); - data.proc = proc; - crawl_linkmap(proc, dbg, linkmap_add_cb, &data); + //data.proc = proc; + crawl_linkmap(proc, dbg); + //&data); } else if (proc->debug_state == RT_DELETE) { debug(2, "Removing DSO from linkmap"); } else { @@ -428,45 +398,19 @@ arch_check_dbg(Process *proc) { } proc->debug_state = dbg->r_state; - return; } -static void -hook_libdl_cb(void *data) { - struct cb_data *hook_data = data; - const char *lib_name = NULL; - ElfW(Addr) addr; - struct ltelf *lte = NULL; - - debug(DEBUG_FUNCTION, "add_library_cb"); - - if (!data) { - debug(2, "No callback data"); - return; - } - - lib_name = hook_data->lib_name; - addr = hook_data->addr; - lte = hook_data->lte; - - if (library_num < MAX_LIBRARIES) { - lte[library_num].base_addr = addr; - library[library_num++] = strdup(lib_name); - } - else { - fprintf (stderr, "MAX LIBS REACHED\n"); - exit(EXIT_FAILURE); - } -} - +void *dyn_addr; int -linkmap_init(Process *proc, struct ltelf *lte) { - void *dbg_addr = NULL, *dyn_addr = GELF_ADDR_CAST(lte->dyn_addr); +linkmap_init(struct Process *proc) +{ + void *dbg_addr = NULL; struct r_debug *rdbg = NULL; - struct cb_data data; + //struct cb_data data; debug(DEBUG_FUNCTION, "linkmap_init()"); + fprintf(stderr, "linkmap_init dyn_addr=%p\n", dyn_addr); if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, &dbg_addr) == -1) { debug(2, "Couldn't find debug structure!"); @@ -480,13 +424,23 @@ linkmap_init(Process *proc, struct ltelf *lte) { return -1; } - data.lte = lte; + //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); + void *addr; + { + struct library_symbol libsym; + library_symbol_init(&libsym, rdbg->r_brk, NULL, 0, + LS_TOPLT_NONE, 0); + addr = sym2addr(proc, &libsym); + library_symbol_destroy(&libsym); + } + struct breakpoint *rdebug_bp = insert_breakpoint(proc, addr, NULL, 1); + static struct bp_callbacks rdebug_callbacks = { + .on_hit = rdebug_callback_hit, + }; + rdebug_bp->cbs = &rdebug_callbacks; - crawl_linkmap(proc, rdbg, hook_libdl_cb, &data); + crawl_linkmap(proc, rdbg); free(rdbg); return 0; diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 6c6e814..9613271 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -304,8 +304,8 @@ add_task_info(struct pid_set * pids, pid_t pid) return task_info; } -static enum pcb_status -task_stopped(Process * task, void * data) +static enum callback_status +task_stopped(struct Process *task, void *data) { enum process_status st = process_status(task->pid); if (data != NULL) @@ -319,38 +319,38 @@ task_stopped(Process * task, void * data) case ps_invalid: case ps_tracing_stop: case ps_zombie: - return pcb_cont; + return CBS_CONT; case ps_sleeping: case ps_stop: case ps_other: - return pcb_stop; + return CBS_STOP; } abort (); } /* Task is blocked if it's stopped, or if it's a vfork parent. */ -static enum pcb_status -task_blocked(Process * task, void * data) +static enum callback_status +task_blocked(struct Process *task, void *data) { struct pid_set * pids = data; struct pid_task * task_info = get_task_info(pids, task->pid); if (task_info != NULL && task_info->vforked) - return pcb_cont; + return CBS_CONT; return task_stopped(task, NULL); } static Event *process_vfork_on_event(struct event_handler *super, Event *event); -static enum pcb_status -task_vforked(Process * task, void * data) +static enum callback_status +task_vforked(struct Process *task, void *data) { if (task->event_handler != NULL && task->event_handler->on_event == &process_vfork_on_event) - return pcb_stop; - return pcb_cont; + return CBS_STOP; + return CBS_CONT; } static int @@ -359,8 +359,8 @@ is_vfork_parent(Process * task) return each_task(task->leader, &task_vforked, NULL) != NULL; } -static enum pcb_status -send_sigstop(Process * task, void * data) +static enum callback_status +send_sigstop(struct Process *task, void *data) { Process * leader = task->leader; struct pid_set * pids = data; @@ -373,24 +373,24 @@ send_sigstop(Process * task, void * data) perror("send_sigstop: add_task_info"); destroy_event_handler(leader); /* Signal failure upwards. */ - return pcb_stop; + return CBS_STOP; } /* This task still has not been attached to. It should be stopped by the kernel. */ if (task->state == STATE_BEING_CREATED) - return pcb_cont; + return CBS_CONT; /* Don't bother sending SIGSTOP if we are already stopped, or * if we sent the SIGSTOP already, which happens when we are * handling "onexit" and inherited the handler from breakpoint * re-enablement. */ enum process_status st; - if (task_stopped(task, &st) == pcb_cont) - return pcb_cont; + if (task_stopped(task, &st) == CBS_CONT) + return CBS_CONT; if (task_info->sigstopped) { if (!task_info->delivered) - return pcb_cont; + return CBS_CONT; task_info->delivered = 0; } @@ -401,7 +401,7 @@ send_sigstop(Process * task, void * data) if (st == ps_sleeping && is_vfork_parent (task)) { task_info->vforked = 1; - return pcb_cont; + return CBS_CONT; } if (task_kill(task->pid, SIGSTOP) >= 0) { @@ -411,7 +411,7 @@ send_sigstop(Process * task, void * data) fprintf(stderr, "Warning: couldn't send SIGSTOP to %d\n", task->pid); - return pcb_cont; + return CBS_CONT; } /* On certain kernels, detaching right after a singlestep causes the @@ -465,21 +465,21 @@ undo_breakpoint(Event * event, void * data) return ecb_cont; } -static enum pcb_status -untrace_task(Process * task, void * data) +static enum callback_status +untrace_task(struct Process *task, void *data) { if (task != data) untrace_pid(task->pid); - return pcb_cont; + return CBS_CONT; } -static enum pcb_status -remove_task(Process * task, void * data) +static enum callback_status +remove_task(struct Process *task, void *data) { /* Don't untrace leader just yet. */ if (task != data) remove_process(task); - return pcb_cont; + return CBS_CONT; } static void diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c index 8b0fc46..bb1b2b1 100644 --- a/sysdeps/linux-gnu/x86_64/plt.c +++ b/sysdeps/linux-gnu/x86_64/plt.c @@ -1,6 +1,7 @@ #include #include "proc.h" #include "common.h" +#include "library.h" GElf_Addr arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -- cgit v1.2.3