diff --git a/ltrace-0.5-demangle.patch b/ltrace-0.5-demangle.patch deleted file mode 100644 index 61085f4..0000000 --- a/ltrace-0.5-demangle.patch +++ /dev/null @@ -1,58 +0,0 @@ -diff -urp ltrace-0.5/configure.ac ltrace-0.5-pm/configure.ac ---- ltrace-0.5/configure.ac 2006-06-14 06:55:21.000000000 +0200 -+++ ltrace-0.5-pm/configure.ac 2010-09-08 14:56:29.000000000 +0200 -@@ -16,6 +16,7 @@ AC_PROG_INSTALL - dnl Checks for libraries. - AC_CHECK_LIB(iberty, cplus_demangle,,,) - AC_CHECK_LIB(supc++, __cxa_demangle,,,) -+AC_CHECK_LIB(stdc++, __cxa_demangle,,,) - - dnl - dnl The following stuff may be useful, but I don't use it now. -diff -urp ltrace-0.5/demangle.c ltrace-0.5-pm/demangle.c ---- ltrace-0.5/demangle.c 2006-02-20 22:48:07.000000000 +0100 -+++ ltrace-0.5-pm/demangle.c 2010-09-08 15:06:23.000000000 +0200 -@@ -9,6 +9,7 @@ - #include "options.h" - #include "output.h" - #include "demangle.h" -+#include "ltrace.h" - - #include "dict.h" - -diff -up ltrace-0.5/demangle.c\~ ltrace-0.5/demangle.c ---- ltrace-0.5/demangle.c~ 2006-02-20 22:48:07.000000000 +0100 -+++ ltrace-0.5/demangle.c 2010-09-08 15:21:44.000000000 +0200 -@@ -28,7 +28,7 @@ static void my_demangle_dict_clear(void) - const char *my_demangle(const char *function_name) - { - const char *tmp, *fn_copy; --#if !defined HAVE_LIBIBERTY && defined HAVE_LIBSUPC__ -+#ifdef USE_CXA_DEMANGLE - extern char *__cxa_demangle(const char *, char *, size_t *, int *); - int status = 0; - #endif -@@ -42,7 +42,7 @@ const char *my_demangle(const char *func - fn_copy = strdup(function_name); - #ifdef HAVE_LIBIBERTY - tmp = cplus_demangle(function_name, DMGL_ANSI | DMGL_PARAMS); --#elif defined HAVE_LIBSUPC__ -+#elif defined USE_CXA_DEMANGLE - tmp = __cxa_demangle(function_name, NULL, NULL, &status); - #endif - if (!tmp) -diff -up ltrace-0.5/ltrace.h\~ ltrace-0.5/ltrace.h ---- ltrace-0.5/ltrace.h~ 2010-09-08 15:30:25.000000000 +0200 -+++ ltrace-0.5/ltrace.h 2010-09-08 15:38:27.000000000 +0200 -@@ -14,7 +14,10 @@ - #define MAX_LIBRARY 30 - #define TRACE_FORK 1 - --#if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__ -+#if defined HAVE_LIBSUPC__ || defined HAVE_LIBSTDC__ -+# define USE_CXA_DEMANGLE -+#endif -+#if defined HAVE_LIBIBERTY || defined USE_CXA_DEMANGLE - # define USE_DEMANGLE - #endif - diff --git a/ltrace-0.6.0-libs.patch b/ltrace-0.6.0-libs.patch new file mode 100644 index 0000000..5d2557f --- /dev/null +++ b/ltrace-0.6.0-libs.patch @@ -0,0 +1,10190 @@ +diff --git a/Makefile.am b/Makefile.am +index 6c299d8..a00c8bf 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -24,7 +24,10 @@ libltrace_la_SOURCES = \ + output.c \ + proc.c \ + read_config_file.c \ +- summary.c ++ summary.c \ ++ library.c \ ++ filter.c \ ++ glob.c + + libltrace_la_LIBADD = \ + $(libelf_LIBS) \ +@@ -56,7 +59,10 @@ noinst_HEADERS = \ + ltrace.h \ + options.h \ + output.h \ +- read_config_file.h ++ read_config_file.h \ ++ library.h \ ++ filter.h \ ++ glob.h + + dist_man1_MANS = \ + ltrace.1 +diff --git a/breakpoint.h b/breakpoint.h +index ce6f501..0398072 100644 +--- a/breakpoint.h ++++ b/breakpoint.h +@@ -33,74 +33,106 @@ + * 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. ++ * 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" ++#include "sysdep.h" ++#include "library.h" + + struct Process; + struct breakpoint; + + struct bp_callbacks { +- void (*on_hit) (struct breakpoint *bp, struct Process *proc); +- void (*on_destroy) (struct breakpoint *bp); ++ void (*on_hit)(struct breakpoint *bp, struct Process *proc); ++ void (*on_continue)(struct breakpoint *bp, struct Process *proc); ++ void (*on_retract)(struct breakpoint *bp, struct Process *proc); + }; + + struct breakpoint { + struct bp_callbacks *cbs; ++ struct library_symbol *libsym; + 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. */ + 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); ++/* Call on-continue handler of BP. If none is set, call ++ * continue_after_breakpoint. */ ++void breakpoint_on_continue(struct breakpoint *bp, struct Process *proc); ++ ++/* Call on-retract handler of BP, if any is set. This should be ++ * called before the breakpoints are destroyed. The reason for a ++ * separate interface is that breakpoint_destroy has to be callable ++ * without PROC. ON_DISABLE might be useful as well, but that would ++ * be called every time we disable the breakpoint, which is too often ++ * (a breakpoint has to be disabled every time that we need to execute ++ * the instruction underneath it). */ ++void breakpoint_on_retract(struct breakpoint *bp, struct Process *proc); ++ ++/* 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); ++ ++/* Make a clone of breakpoint BP into the area of memory pointed to by ++ * RETP. The original breakpoint was assigned to process OLD_PROC, ++ * the cloned breakpoint will be attached to process NEW_PROC. ++ * Returns 0 on success or a negative value on failure. */ ++int breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, ++ struct breakpoint *bp, struct Process *old_proc); ++ ++/* Set callbacks. If CBS is non-NULL, then BP->cbs shall be NULL. */ ++void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs); + +-/* This is actually three functions rolled in one: +- * - breakpoint_init +- * - proc_insert_breakpoint +- * - breakpoint_enable +- * XXX I think it should be broken up somehow. */ ++/* Destroy a breakpoint structure. */ ++void breakpoint_destroy(struct breakpoint *bp); ++ ++/* Call enable_breakpoint the first time it's called. Returns 0 on ++ * success and a negative value on failure. */ ++int breakpoint_turn_on(struct breakpoint *bp, struct Process *proc); ++ ++/* Call disable_breakpoint when turned off the same number of times ++ * that it was turned on. Returns 0 on success and a negative value ++ * on failure. */ ++int breakpoint_turn_off(struct breakpoint *bp, struct Process *proc); ++ ++/* Utility function that does what typically needs to be done when a ++ * breakpoint is to be inserted. It checks whether there is another ++ * breakpoint in PROC->LEADER for given ADDR. If not, it allocates ++ * memory for a new breakpoint on the heap, initializes it, and calls ++ * PROC_ADD_BREAKPOINT to add the newly-created breakpoint. For newly ++ * added as well as preexisting breakpoints, it then calls ++ * BREAKPOINT_TURN_ON. If anything fails, it cleans up and returns ++ * NULL. Otherwise it returns the breakpoint for ADDR. */ + struct breakpoint *insert_breakpoint(struct Process *proc, void *addr, +- struct library_symbol *libsym, int enable); ++ struct library_symbol *libsym); ++ ++/* Name of a symbol associated with BP. May be NULL. */ ++const char *breakpoint_name(const struct breakpoint *bp); + +-/* */ ++/* A library that this breakpoint comes from. May be NULL. */ ++struct library *breakpoint_library(const struct breakpoint *bp); ++ ++/* Again, this seems to be several interfaces rolled into one: ++ * - breakpoint_disable ++ * - proc_remove_breakpoint ++ * - breakpoint_destroy ++ * XXX */ + 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); +- ++int breakpoints_init(struct Process *proc); + + #endif /* BREAKPOINT_H */ +diff --git a/breakpoints.c b/breakpoints.c +index 5713fe4..9536266 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #ifdef __powerpc__ + #include +@@ -10,21 +11,52 @@ + + #include "breakpoint.h" + #include "common.h" ++#include "proc.h" ++#include "library.h" ++ ++#ifndef ARCH_HAVE_TRANSLATE_ADDRESS ++int ++arch_translate_address_dyn(struct Process *proc, ++ target_address_t addr, target_address_t *ret) ++{ ++ *ret = addr; ++ return 0; ++} ++ ++struct ltelf; ++int ++arch_translate_address(struct ltelf *lte, ++ target_address_t addr, target_address_t *ret) ++{ ++ *ret = addr; ++ return 0; ++} ++#endif + + 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); ++ (bp->cbs->on_hit)(bp, proc); ++} ++ ++void ++breakpoint_on_continue(struct breakpoint *bp, struct Process *proc) ++{ ++ assert(bp != NULL); ++ if (bp->cbs != NULL && bp->cbs->on_continue != NULL) ++ (bp->cbs->on_continue)(bp, proc); ++ else ++ continue_after_breakpoint(proc, bp); + } + + void +-breakpoint_on_destroy(struct breakpoint *bp) ++breakpoint_on_retract(struct breakpoint *bp, struct Process *proc) + { + assert(bp != NULL); +- if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) +- (bp->cbs->on_destroy) (bp); ++ if (bp->cbs != NULL && bp->cbs->on_retract != NULL) ++ (bp->cbs->on_retract)(bp, proc); + } + + /*****************************************************************************/ +@@ -39,52 +71,171 @@ address2bpstruct(Process *proc, void *addr) + return dict_find_entry(proc->breakpoints, addr); + } + +-struct breakpoint * +-insert_breakpoint(Process *proc, void *addr, +- struct library_symbol *libsym, int enable) ++#ifndef ARCH_HAVE_BREAKPOINT_DATA ++int ++arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) + { +- struct breakpoint *sbp; ++ return 0; ++} + +- Process * leader = proc->leader; ++void ++arch_breakpoint_destroy(struct breakpoint *sbp) ++{ ++} ++ ++int ++arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) ++{ ++ return 0; ++} ++#endif ++ ++static void ++breakpoint_init_base(struct breakpoint *bp, struct Process *proc, ++ target_address_t addr, struct library_symbol *libsym) ++{ ++ bp->cbs = NULL; ++ bp->addr = addr; ++ memset(bp->orig_value, 0, sizeof(bp->orig_value)); ++ bp->enabled = 0; ++ bp->libsym = libsym; ++} ++ ++/* On second thought, I don't think we need PROC. All the translation ++ * (arch_translate_address in particular) should be doable using ++ * static lookups of various sections in the ELF file. We shouldn't ++ * need process for anything. */ ++int ++breakpoint_init(struct breakpoint *bp, struct Process *proc, ++ target_address_t addr, struct library_symbol *libsym) ++{ ++ breakpoint_init_base(bp, proc, addr, libsym); ++ return arch_breakpoint_init(proc, bp); ++} ++ ++void ++breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs) ++{ ++ if (bp->cbs != NULL) ++ assert(bp->cbs == NULL); ++ bp->cbs = cbs; ++} ++ ++void ++breakpoint_destroy(struct breakpoint *bp) ++{ ++ if (bp == NULL) ++ return; ++ arch_breakpoint_destroy(bp); ++} ++ ++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; ++} ++ ++int ++breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, ++ struct breakpoint *bp, struct Process *old_proc) ++{ ++ /* Find library and symbol that this breakpoint 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(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. */ ++ breakpoint_init_base(retp, new_proc, bp->addr, libsym); ++ memcpy(retp->orig_value, bp->orig_value, sizeof(bp->orig_value)); ++ retp->enabled = bp->enabled; ++ if (arch_breakpoint_clone(retp, bp) < 0) ++ return -1; ++ breakpoint_set_callbacks(retp, bp->cbs); ++ return 0; ++} ++ ++int ++breakpoint_turn_on(struct breakpoint *bp, struct Process *proc) ++{ ++ bp->enabled++; ++ if (bp->enabled == 1) { ++ assert(proc->pid != 0); ++ enable_breakpoint(proc, bp); ++ } ++ return 0; ++} ++ ++int ++breakpoint_turn_off(struct breakpoint *bp, struct Process *proc) ++{ ++ bp->enabled--; ++ if (bp->enabled == 0) ++ disable_breakpoint(proc, bp); ++ assert(bp->enabled >= 0); ++ return 0; ++} ++ ++struct breakpoint * ++insert_breakpoint(struct Process *proc, void *addr, ++ struct library_symbol *libsym) ++{ ++ Process *leader = proc->leader; + + /* Only the group leader should be getting the breakpoints and + * thus have ->breakpoint initialized. */ + assert(leader != NULL); + assert(leader->breakpoints != NULL); + +-#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); +- +- if (!addr) +- return NULL; ++ debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", ++ proc->pid, addr, libsym ? libsym->name : "NULL"); + +- if (libsym) +- libsym->needs_init = 0; ++ assert(addr != 0); + +- sbp = dict_find_entry(leader->breakpoints, addr); ++ /* XXX what we need to do instead is have a list of ++ * breakpoints that are enabled at this address. The ++ * following works if every breakpoint is the same and there's ++ * no extra data, but that doesn't hold anymore. For now it ++ * will suffice, about the only realistic case where we need ++ * to have more than one breakpoint per address is return from ++ * a recursive library call. */ ++ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); + if (sbp == NULL) { +- sbp = 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) < 0) { ++ free(sbp); ++ return NULL; ++ } ++ if (proc_add_breakpoint(leader, sbp) < 0) { ++ fail: ++ breakpoint_destroy(sbp); ++ 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); +- enable_breakpoint(proc, sbp); ++ ++ if (breakpoint_turn_on(sbp, proc) < 0) { ++ proc_remove_breakpoint(leader, sbp); ++ goto fail; + } + + return sbp; +@@ -93,23 +244,41 @@ insert_breakpoint(Process *proc, void *addr, + void + delete_breakpoint(Process *proc, void *addr) + { +- struct breakpoint *sbp; +- + debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); + + Process * leader = proc->leader; + assert(leader != NULL); + +- sbp = dict_find_entry(leader->breakpoints, addr); +- assert(sbp); /* FIXME: remove after debugging has been done. */ ++ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); ++ assert(sbp != NULL); + /* This should only happen on out-of-memory conditions. */ + if (sbp == NULL) + return; + +- sbp->enabled--; +- if (sbp->enabled == 0) +- disable_breakpoint(proc, sbp); +- assert(sbp->enabled >= 0); ++ if (breakpoint_turn_off(sbp, proc) < 0) { ++ fprintf(stderr, "Couldn't turn off the breakpoint %s@%p\n", ++ breakpoint_name(sbp), sbp->addr); ++ return; ++ } ++ if (sbp->enabled == 0) { ++ proc_remove_breakpoint(leader, sbp); ++ breakpoint_destroy(sbp); ++ free(sbp); ++ } ++} ++ ++const char * ++breakpoint_name(const struct breakpoint *bp) ++{ ++ assert(bp != NULL); ++ return bp->libsym != NULL ? bp->libsym->name : NULL; ++} ++ ++struct library * ++breakpoint_library(const struct breakpoint *bp) ++{ ++ assert(bp != NULL); ++ return bp->libsym != NULL ? bp->libsym->lib : NULL; + } + + static void +@@ -176,108 +345,97 @@ 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); +-} ++/* XXX This is not currently properly supported. On clone, this is ++ * just sliced. Hopefully at the point that clone is done, this ++ * breakpoint is not necessary anymore. If this use case ends up ++ * being important, we need to add a clone and destroy callbacks to ++ * breakpoints, and we should also probably drop arch_breakpoint_data ++ * so that we don't end up with two different customization mechanisms ++ * for one structure. */ ++struct entry_breakpoint { ++ struct breakpoint super; ++ target_address_t dyn_addr; ++}; + + static void +-entry_callback_hit(struct breakpoint *bp, struct Process *proc) ++entry_breakpoint_on_hit(struct breakpoint *a, struct Process *proc) + { ++ struct entry_breakpoint *bp = (void *)a; + if (proc == NULL || proc->leader == NULL) + return; +- delete_breakpoint(proc, bp->addr); // xxx +- reinitialize_breakpoints(proc->leader); ++ target_address_t dyn_addr = bp->dyn_addr; ++ delete_breakpoint(proc, bp->super.addr); ++ linkmap_init(proc, dyn_addr); ++ arch_dynlink_done(proc); + } + + int +-breakpoints_init(Process *proc, int enable) ++entry_breakpoint_init(struct Process *proc, ++ struct entry_breakpoint *bp, target_address_t addr, ++ struct library *lib) + { +- 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; +- } ++ int err; ++ if ((err = breakpoint_init(&bp->super, proc, addr, NULL)) < 0) ++ return err; + +- /* 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); ++ static struct bp_callbacks entry_callbacks = { ++ .on_hit = entry_breakpoint_on_hit, ++ }; ++ bp->super.cbs = &entry_callbacks; ++ bp->dyn_addr = lib->dyn_addr; ++ return 0; ++} + +- proc->breakpoints = dict_init(dict_key2hash_int, +- dict_key_cmp_int); ++int ++breakpoints_init(Process *proc) ++{ ++ debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); + +- destroy_library_symbol_chain(proc->list_of_symbols); +- proc->list_of_symbols = NULL; ++ /* XXX breakpoint dictionary should be initialized ++ * outside. Here we just put in breakpoints. */ ++ assert(proc->breakpoints != NULL); + +- GElf_Addr entry; +- if (options.libcalls && proc->filename) { +- proc->list_of_symbols = read_elf(proc, &entry); +- if (proc->list_of_symbols == NULL) { +- fail: +- /* XXX leak breakpoints */ +- return -1; +- } ++ /* Only the thread group leader should hold the breakpoints. */ ++ assert(proc->leader == proc); + +- if (opt_e) { +- struct library_symbol **tmp1 = &proc->list_of_symbols; +- while (*tmp1) { +- struct opt_e_t *tmp2 = opt_e; +- int keep = !opt_e_enable; +- +- while (tmp2) { +- if (!strcmp((*tmp1)->name, +- tmp2->name)) { +- keep = opt_e_enable; +- } +- tmp2 = tmp2->next; +- } +- if (!keep) { +- *tmp1 = (*tmp1)->next; +- } else { +- tmp1 = &((*tmp1)->next); +- } +- } ++ /* N.B. the following used to be conditional on this, and ++ * maybe it still needs to be. */ ++ assert(proc->filename != NULL); ++ ++ struct library *lib = ltelf_read_main_binary(proc, proc->filename); ++ struct entry_breakpoint *entry_bp = NULL; ++ int bp_state = 0; ++ int result = -1; ++ switch (lib != NULL) { ++ fail: ++ switch (bp_state) { ++ case 2: ++ proc_remove_library(proc, lib); ++ proc_remove_breakpoint(proc, &entry_bp->super); ++ case 1: ++ breakpoint_destroy(&entry_bp->super); + } ++ library_destroy(lib); ++ free(entry_bp); ++ case 0: ++ return result; + } + +- struct breakpoint *entry_bp +- = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); +- if (entry_bp == NULL) { +- fprintf(stderr, "fail!\n"); ++ entry_bp = malloc(sizeof(*entry_bp)); ++ if (entry_bp == NULL ++ || (result = entry_breakpoint_init(proc, entry_bp, ++ lib->entry, lib)) < 0) + goto fail; +- } ++ ++bp_state; + +- static struct bp_callbacks entry_callbacks = { +- .on_hit = entry_callback_hit, +- }; +- entry_bp->cbs = &entry_callbacks; ++ if ((result = proc_add_breakpoint(proc, &entry_bp->super)) < 0) ++ goto fail; ++ ++bp_state; ++ ++ if ((result = breakpoint_turn_on(&entry_bp->super, proc)) < 0) ++ goto fail; ++ proc_add_library(proc, lib); + + proc->callstack_depth = 0; + return 0; + } +- +-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 ed618b5..04e095c 100644 +--- a/common.h ++++ b/common.h +@@ -2,7 +2,4 @@ + #include +-#if defined(HAVE_LIBUNWIND) +-#include +-#endif /* defined(HAVE_LIBUNWIND) */ + + #include + #include +@@ -17,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 +@@ -116,116 +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 { +- char * name; +- void * enter_addr; +- char needs_init; +- enum toplt plt_type; +- char is_weak; +- struct library_symbol * next; +-}; +- +-struct callstack_element { +- union { +- int syscall; +- struct library_symbol * libfunc; +- } c_un; +- int is_syscall; +- void * return_addr; +- struct timeval time_spent; +- void * arch_ptr; +-}; +- +-#define MAX_CALLDEPTH 64 +- +-typedef enum Process_State Process_State; +-enum Process_State { +- STATE_ATTACHED = 0, +- STATE_BEING_CREATED, +- STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ +-}; +- +-typedef struct Event_Handler Event_Handler; +-struct Event_Handler { +- /* Event handler that overrides the default one. Should +- * return NULL if the event was handled, otherwise the +- * returned event is passed to the default handler. */ +- Event * (* on_event)(Event_Handler * self, Event * event); +- +- /* Called when the event handler removal is requested. */ +- void (* destroy)(Event_Handler * self); +-}; +- +-/* XXX We would rather have this all organized a little differently, +- * have Process for the whole group and Task for what's there for +- * per-thread stuff. But for now this is the less invasive way of +- * structuring it. */ +-struct Process { +- Process_State state; +- Process * parent; /* needed by STATE_BEING_CREATED */ +- char * filename; +- pid_t pid; +- +- /* Dictionary of breakpoints (which is a mapping +- * address->breakpoint). This is NULL for non-leader +- * processes. */ +- Dict * breakpoints; +- +- int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ +- unsigned int personality; +- int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ +- +- int callstack_depth; +- struct callstack_element callstack[MAX_CALLDEPTH]; +- struct library_symbol * list_of_symbols; +- +- int libdl_hooked; +- /* Arch-dependent: */ +- void * debug; /* arch-dep process debug struct */ +- long debug_state; /* arch-dep debug state */ +- void * instruction_pointer; +- void * stack_pointer; /* To get return addr, args... */ +- 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 +- +-#if defined(HAVE_LIBUNWIND) +- /* libunwind address space */ +- unw_addr_space_t unwind_as; +- void *unwind_priv; +-#endif /* defined(HAVE_LIBUNWIND) */ +- +- /* Set in leader. */ +- Event_Handler * event_handler; +- +- +- /** +- * Process chaining. +- **/ +- Process * next; +- +- /* LEADER points to the leader thread of the POSIX.1 process. +- If X->LEADER == X, then X is the leader thread and the +- Process structures chained by NEXT represent other threads, +- up until, but not including, the next leader thread. +- LEADER may be NULL after the leader has already exited. In +- that case this process is waiting to be collected. */ +- Process * leader; +-}; +- + struct opt_c_struct { + int count; + struct timeval tv; +@@ -248,23 +139,6 @@ enum process_status { + ps_other, /* Necessary other states can be added as needed. */ + }; + +-enum pcb_status { +- pcb_stop, /* The iteration should stop. */ +- pcb_cont, /* The iteration should continue. */ +-}; +- +-/* Process list */ +-extern Process * pid2proc(pid_t pid); +-extern void add_process(Process * proc); +-extern void remove_process(Process * proc); +-extern void change_process_leader(Process * proc, Process * leader); +-extern Process *each_process(Process * start, +- enum pcb_status (* cb)(Process * proc, void * data), +- void * data); +-extern Process *each_task(Process * start, +- enum pcb_status (* cb)(Process * proc, void * data), +- void * data); +- + /* Events */ + enum ecb_status { + ecb_cont, /* The iteration should continue. */ +@@ -279,31 +153,15 @@ extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data), + extern void enque_event(Event * event); + extern void handle_event(Event * event); + +-extern void install_event_handler(Process * proc, Event_Handler * handler); +-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 void disable_all_breakpoints(Process * proc); + +-extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); +-extern void open_pid(pid_t pid); + 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 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); +@@ -311,8 +169,8 @@ extern pid_t process_leader(pid_t pid); + 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_set_options(struct Process *proc); ++extern int 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); +@@ -322,13 +180,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, struct breakpoint *sbp); +-extern void disable_breakpoint(Process * proc, struct breakpoint *sbp); ++extern void enable_breakpoint(struct Process *proc, struct breakpoint *sbp); ++extern void disable_breakpoint(struct 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, struct breakpoint *sbp); ++extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); + extern void continue_after_vfork(Process * proc); + extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); + extern void save_register_args(enum tof type, Process * proc); +@@ -337,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 *proc, void *dyn_addr); + extern void arch_check_dbg(Process *proc); + extern int task_kill (pid_t pid, int sig); + +@@ -357,9 +215,51 @@ void trace_fail_warning(pid_t pid); + * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the + * request, OS_LTRACE_EXITING is called when the next event is + * generated. Therefore it's called in "safe" context, without +- * re-entrancy concerns, but it's only called after an even is ++ * re-entrancy concerns, but it's only called after an event is + * generated. */ + int os_ltrace_exiting_sighandler(void); + void os_ltrace_exiting(void); + +-extern struct ltelf main_lte; ++int arch_elf_init(struct ltelf *lte, struct library *lib); ++void arch_elf_destroy(struct ltelf *lte); ++ ++enum plt_status { ++ plt_fail, ++ plt_ok, ++ plt_default, ++}; ++ ++enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l, ++ const char *n, GElf_Rela *r, size_t i, ++ struct library_symbol **ret); ++ ++int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp); ++void arch_breakpoint_destroy(struct breakpoint *sbp); ++int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp); ++ ++void arch_library_init(struct library *lib); ++void arch_library_destroy(struct library *lib); ++void arch_library_clone(struct library *retp, struct library *lib); ++ ++int arch_library_symbol_init(struct library_symbol *libsym); ++void arch_library_symbol_destroy(struct library_symbol *libsym); ++int arch_library_symbol_clone(struct library_symbol *retp, ++ struct library_symbol *libsym); ++ ++int arch_process_init(struct Process *proc); ++void arch_process_destroy(struct Process *proc); ++int arch_process_clone(struct Process *retp, struct Process *proc); ++int arch_process_exec(struct Process *proc); ++ ++typedef void *target_address_t; ++/* This should extract entry point address and interpreter (dynamic ++ * linker) bias if possible. Returns 0 if there were no errors, -1 ++ * otherwise. Sets *ENTRYP and *INTERP_BIASP to non-zero values if ++ * the corresponding value is known. Unknown values are set to 0. */ ++int process_get_entry(struct Process *proc, ++ target_address_t *entryp, ++ target_address_t *interp_biasp); ++ ++/* This is called after the dynamic linker is done with the ++ * process startup. */ ++void arch_dynlink_done(struct Process *proc); +diff --git a/debug.c b/debug.c +index 1be873b..5fb9feb 100644 +--- a/debug.c ++++ b/debug.c +@@ -16,6 +16,7 @@ debug_(int level, const char *file, int line, const char *fmt, ...) { + va_end(args); + + output_line(NULL, "DEBUG: %s:%d: %s", file, line, buf); ++ fflush(options.output); + } + + /* +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/dict.c b/dict.c +index ba318cd..b32ef8e 100644 +--- a/dict.c ++++ b/dict.c +@@ -24,13 +24,14 @@ struct dict_entry { + + struct dict { + struct dict_entry *buckets[DICTTABLESIZE]; +- unsigned int (*key2hash) (void *); +- int (*key_cmp) (void *, void *); ++ unsigned int (*key2hash) (const void *); ++ int (*key_cmp) (const void *, const void *); + }; + + Dict * +-dict_init(unsigned int (*key2hash) (void *), +- int (*key_cmp) (void *, void *)) { ++dict_init(unsigned int (*key2hash) (const void *), ++ int (*key_cmp) (const void *, const void *)) ++{ + Dict *d; + int i; + +@@ -103,7 +104,31 @@ dict_enter(Dict *d, void *key, void *value) { + } + + void * +-dict_find_entry(Dict *d, void *key) { ++dict_remove(Dict *d, void *key) ++{ ++ assert(d != NULL); ++ debug(DEBUG_FUNCTION, "dict_remove(%p)", key); ++ ++ unsigned int hash = d->key2hash(key); ++ unsigned int bucketpos = hash % DICTTABLESIZE; ++ ++ struct dict_entry **entryp; ++ for (entryp = &d->buckets[bucketpos]; (*entryp) != NULL; ++ entryp = &(*entryp)->next) { ++ struct dict_entry *entry = *entryp; ++ if (hash != entry->hash) ++ continue; ++ if (d->key_cmp(key, entry->key) == 0) { ++ *entryp = entry->next; ++ return entry->value; ++ } ++ } ++ return NULL; ++} ++ ++void * ++dict_find_entry(Dict *d, const void *key) ++{ + unsigned int hash; + unsigned int bucketpos; + struct dict_entry *entry; +@@ -147,7 +172,8 @@ dict_apply_to_all(Dict *d, + /*****************************************************************************/ + + unsigned int +-dict_key2hash_string(void *key) { ++dict_key2hash_string(const void *key) ++{ + const char *s = (const char *)key; + unsigned int total = 0, shift = 0; + +@@ -163,19 +189,22 @@ dict_key2hash_string(void *key) { + } + + int +-dict_key_cmp_string(void *key1, void *key2) { ++dict_key_cmp_string(const void *key1, const void *key2) ++{ + assert(key1); + assert(key2); + return strcmp((const char *)key1, (const char *)key2); + } + + unsigned int +-dict_key2hash_int(void *key) { ++dict_key2hash_int(const void *key) ++{ + return (unsigned long)key; + } + + int +-dict_key_cmp_int(void *key1, void *key2) { ++dict_key_cmp_int(const void *key1, const void *key2) ++{ + return key1 - key2; + } + +diff --git a/dict.h b/dict.h +index 27dc7bf..f41011a 100644 +--- a/dict.h ++++ b/dict.h +@@ -4,19 +4,22 @@ + + typedef struct dict Dict; + +-extern Dict *dict_init(unsigned int (*key2hash) (void *), +- int (*key_cmp) (void *, void *)); ++extern Dict *dict_init(unsigned int (*key2hash) (const void *), ++ int (*key_cmp) (const void *, const void *)); + extern void dict_clear(Dict *d); + extern int dict_enter(Dict *d, void *key, void *value); +-extern void *dict_find_entry(Dict *d, void *key); ++extern void *dict_remove(Dict *d, void *key); ++extern void *dict_find_entry(Dict *d, const void *key); + extern void dict_apply_to_all(Dict *d, + void (*func) (void *key, void *value, void *data), + void *data); + +-extern unsigned int dict_key2hash_string(void *key); +-extern int dict_key_cmp_string(void *key1, void *key2); +-extern unsigned int dict_key2hash_int(void *key); +-extern int dict_key_cmp_int(void *key1, void *key2); ++extern unsigned int dict_key2hash_string(const void *key); ++extern int dict_key_cmp_string(const void *key1, const void *key2); ++ ++extern unsigned int dict_key2hash_int(const void *key); ++extern int dict_key_cmp_int(const void *key1, const void *key2); ++ + extern Dict * dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)); + extern Dict * dict_clone2(Dict * old, + void * (* key_clone)(void * key, void * data), +diff --git a/display_args.c b/display_args.c +index c639c88..5df34ca 100644 +--- a/display_args.c ++++ b/display_args.c +@@ -5,6 +5,7 @@ + #include + + #include "common.h" ++#include "proc.h" + + static int display_char(int what); + static int display_string(enum tof type, Process *proc, +diff --git a/execute_program.c b/execute_program.c +index 859f32c..55df205 100644 +--- a/execute_program.c ++++ b/execute_program.c +@@ -78,6 +78,7 @@ execute_program(const char * command, char **argv) + + pid = fork(); + if (pid < 0) { ++ fail: + perror("ltrace: fork"); + exit(1); + } else if (!pid) { /* child */ +@@ -89,9 +90,9 @@ execute_program(const char * command, char **argv) + _exit(1); + } + +- wait_for_proc(pid); ++ if (wait_for_proc(pid) < 0) ++ goto fail; + + debug(1, "PID=%d", pid); +- + return pid; + } +diff --git a/filter.c b/filter.c +new file mode 100644 +index 0000000..003010d +--- /dev/null ++++ b/filter.c +@@ -0,0 +1,188 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * 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 ++#include ++ ++#include "filter.h" ++#include "library.h" ++ ++void ++filter_init(struct filter *filt) ++{ ++ filt->rules = NULL; ++ filt->next = NULL; ++} ++ ++void ++filter_destroy(struct filter *filt) ++{ ++ struct filter_rule *it; ++ for (it = filt->rules; it != NULL; ) { ++ struct filter_rule *next = it->next; ++ filter_rule_destroy(it); ++ it = next; ++ } ++} ++ ++void ++filter_rule_init(struct filter_rule *rule, enum filter_rule_type type, ++ struct filter_lib_matcher *matcher, ++ regex_t symbol_re) ++{ ++ rule->type = type; ++ rule->lib_matcher = matcher; ++ rule->symbol_re = symbol_re; ++ rule->next = NULL; ++} ++ ++void ++filter_rule_destroy(struct filter_rule *rule) ++{ ++ filter_lib_matcher_destroy(rule->lib_matcher); ++ regfree(&rule->symbol_re); ++} ++ ++void ++filter_add_rule(struct filter *filt, struct filter_rule *rule) ++{ ++ struct filter_rule **rulep; ++ for (rulep = &filt->rules; *rulep != NULL; rulep = &(*rulep)->next) ++ ; ++ *rulep = rule; ++} ++ ++void ++filter_lib_matcher_name_init(struct filter_lib_matcher *matcher, ++ enum filter_lib_matcher_type type, ++ regex_t libname_re) ++{ ++ switch (type) { ++ case FLM_MAIN: ++ assert(type != type); ++ abort(); ++ ++ case FLM_SONAME: ++ case FLM_PATHNAME: ++ matcher->type = type; ++ matcher->libname_re = libname_re; ++ } ++} ++ ++void ++filter_lib_matcher_main_init(struct filter_lib_matcher *matcher) ++{ ++ matcher->type = FLM_MAIN; ++} ++ ++void ++filter_lib_matcher_destroy(struct filter_lib_matcher *matcher) ++{ ++ switch (matcher->type) { ++ case FLM_SONAME: ++ case FLM_PATHNAME: ++ regfree(&matcher->libname_re); ++ break; ++ case FLM_MAIN: ++ break; ++ } ++} ++ ++static int ++re_match_or_error(regex_t *re, const char *name, const char *what) ++{ ++ int status = regexec(re, name, 0, NULL, 0); ++ if (status == 0) ++ return 1; ++ if (status == REG_NOMATCH) ++ return 0; ++ ++ char buf[200]; ++ regerror(status, re, buf, sizeof buf); ++ fprintf(stderr, "Error when matching %s: %s\n", name, buf); ++ ++ return 0; ++} ++ ++static int ++matcher_matches_library(struct filter_lib_matcher *matcher, struct library *lib) ++{ ++ switch (matcher->type) { ++ case FLM_SONAME: ++ return re_match_or_error(&matcher->libname_re, lib->soname, ++ "library soname"); ++ case FLM_PATHNAME: ++ return re_match_or_error(&matcher->libname_re, lib->pathname, ++ "library pathname"); ++ case FLM_MAIN: ++ return lib->type == LT_LIBTYPE_MAIN; ++ } ++ assert(matcher->type != matcher->type); ++ abort(); ++} ++ ++int ++filter_matches_library(struct filter *filt, struct library *lib) ++{ ++ if (filt == NULL) ++ return 0; ++ ++ struct filter_rule *it; ++ for (it = filt->rules; it != NULL; it = it->next) ++ switch (it->type) { ++ case FR_ADD: ++ if (matcher_matches_library(it->lib_matcher, lib)) ++ return 1; ++ case FR_SUBTRACT: ++ continue; ++ }; ++ return 0; ++} ++ ++int ++filter_matches_symbol(struct filter *filt, ++ const char *sym_name, struct library *lib) ++{ ++ for (; filt != NULL; filt = filt->next) { ++ int matches = 0; ++ struct filter_rule *it; ++ for (it = filt->rules; it != NULL; it = it->next) { ++ switch (it->type) { ++ case FR_ADD: ++ if (matches) ++ continue; ++ break; ++ case FR_SUBTRACT: ++ if (!matches) ++ continue; ++ } ++ ++ if (matcher_matches_library(it->lib_matcher, lib) ++ && re_match_or_error(&it->symbol_re, sym_name, ++ "symbol name")) ++ matches = !matches; ++ } ++ if (matches) ++ return 1; ++ } ++ return 0; ++} +diff --git a/filter.h b/filter.h +new file mode 100644 +index 0000000..65c575a +--- /dev/null ++++ b/filter.h +@@ -0,0 +1,99 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * 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 ++ */ ++ ++/* This file contains declarations and types for working with symbol ++ * filters. */ ++ ++#ifndef FILTER_H ++#define FILTER_H ++ ++#include ++#include ++ ++struct library; ++struct library_symbol; ++ ++enum filter_lib_matcher_type { ++ /* Match by soname. */ ++ FLM_SONAME, ++ /* Match by path name. */ ++ FLM_PATHNAME, ++ /* Match main binary. */ ++ FLM_MAIN, ++}; ++ ++struct filter_lib_matcher { ++ enum filter_lib_matcher_type type; ++ regex_t libname_re; ++}; ++ ++enum filter_rule_type { ++ FR_ADD, ++ FR_SUBTRACT, ++}; ++ ++struct filter_rule { ++ struct filter_rule *next; ++ struct filter_lib_matcher *lib_matcher; ++ regex_t symbol_re; /* Regex for matching symbol name. */ ++ enum filter_rule_type type; ++}; ++ ++struct filter { ++ struct filter *next; ++ struct filter_rule *rules; ++}; ++ ++void filter_init(struct filter *filt); ++void filter_destroy(struct filter *filt); ++ ++/* Both SYMBOL_RE and MATCHER are owned and destroyed by RULE. */ ++void filter_rule_init(struct filter_rule *rule, enum filter_rule_type type, ++ struct filter_lib_matcher *matcher, ++ regex_t symbol_re); ++ ++void filter_rule_destroy(struct filter_rule *rule); ++ ++/* RULE is added to FILT and owned and destroyed by it. */ ++void filter_add_rule(struct filter *filt, struct filter_rule *rule); ++ ++/* Create a matcher that matches library name. RE is owned and ++ * destroyed by MATCHER. TYPE shall be FLM_SONAME or ++ * FLM_PATHNAME. */ ++void filter_lib_matcher_name_init(struct filter_lib_matcher *matcher, ++ enum filter_lib_matcher_type type, ++ regex_t re); ++ ++/* Create a matcher that matches main binary. */ ++void filter_lib_matcher_main_init(struct filter_lib_matcher *matcher); ++ ++void filter_lib_matcher_destroy(struct filter_lib_matcher *matcher); ++ ++/* Ask whether FILTER might match a symbol in LIB. 0 if no, non-0 if ++ * yes. Note that positive answer doesn't mean that anything will ++ * actually be matched, just that potentially it could. */ ++int filter_matches_library(struct filter *filt, struct library *lib); ++ ++/* Ask whether FILTER matches this symbol. Returns 0 if it doesn't, ++ * or non-0 value if it does. */ ++int filter_matches_symbol(struct filter *filt, const char *sym_name, ++ struct library *lib); ++ ++#endif /* FILTER_H */ +diff --git a/glob.c b/glob.c +new file mode 100644 +index 0000000..6c5c9ef +--- /dev/null ++++ b/glob.c +@@ -0,0 +1,275 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2007, 2008, 2012 Petr Machata, Red Hat Inc. ++ * ++ * 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 ++#include ++ ++static ssize_t ++match_character_class(const char *glob, size_t length, size_t from) ++{ ++ size_t i; ++ if (length > 0) ++ for (i = from + 2; i < length - 1 && glob[++i] != ':'; ) ++ ; ++ if (i >= length || glob[++i] != ']') ++ return -1; ++ return i; ++} ++ ++static ssize_t ++match_brack(const char *glob, size_t length, size_t from, int *exclmp) ++{ ++ size_t i = from + 1; ++ ++ if (i >= length) ++ return -1; ++ ++ /* Complement operator. */ ++ *exclmp = 0; ++ if (glob[i] == '^' || glob[i] == '!') { ++ *exclmp = glob[i++] == '!'; ++ if (i >= length) ++ return -1; ++ } ++ ++ /* On first character, both [ and ] are legal. But when [ is ++ * followed with :, it's character class. */ ++ if (glob[i] == '[' && glob[i + 1] == ':') { ++ ssize_t j = match_character_class(glob, length, i); ++ if (j < 0) ++ fail: ++ return -1; ++ i = j; ++ } ++ ++i; /* skip any character, including [ or ] */ ++ ++ int escape = 0; ++ for (; i < length; ++i) { ++ char c = glob[i]; ++ if (escape) { ++ ++i; ++ escape = 0; ++ ++ } else if (c == '[' && glob[i + 1] == ':') { ++ ssize_t j = match_character_class(glob, length, i); ++ if (j < 0) ++ goto fail; ++ i = j; ++ ++ } else if (c == ']') { ++ return i; ++ } ++ } ++ return -1; ++} ++ ++static int ++append(char **bufp, const char *str, size_t str_size, ++ size_t *sizep, size_t *allocp) ++{ ++ if (str_size == 0) ++ str_size = strlen(str); ++ size_t nsize = *sizep + str_size; ++ if (nsize > *allocp) { ++ size_t nalloc = nsize * 2; ++ char *nbuf = realloc(*bufp, nalloc); ++ if (nbuf == NULL) ++ return -1; ++ *allocp = nalloc; ++ *bufp = nbuf; ++ } ++ ++ memcpy(*bufp + *sizep, str, str_size); ++ *sizep = nsize; ++ return 0; ++} ++ ++static int ++glob_to_regex(const char *glob, char **retp) ++{ ++ size_t allocd = 0; ++ size_t size = 0; ++ char *buf = NULL; ++ ++ size_t length = strlen(glob); ++ int escape = 0; ++ size_t i; ++ for(i = 0; i < length; ++i) { ++ char c = glob[i]; ++ if (escape) { ++ if (c == '\\') { ++ if (append(&buf, "\\\\", 0, ++ &size, &allocd) < 0) { ++ fail: ++ free(buf); ++ return REG_ESPACE; ++ } ++ ++ } else if (c == '*') { ++ if (append(&buf, "\\*", 0, &size, &allocd) < 0) ++ goto fail; ++ } else if (c == '?') { ++ if (append(&buf, "?", 0, &size, &allocd) < 0) ++ goto fail; ++ } else if (append(&buf, (char[]){ '\\', c }, 2, ++ &size, &allocd) < 0) ++ goto fail; ++ escape = 0; ++ } else { ++ if (c == '\\') ++ escape = 1; ++ else if (c == '[') { ++ int exclm; ++ ssize_t j = match_brack(glob, length, i, &exclm); ++ if (j < 0) ++ return REG_EBRACK; ++ if (exclm ++ && append(&buf, "[^", 2, ++ &size, &allocd) < 0) ++ goto fail; ++ if (append(&buf, glob + i + 2*exclm, ++ j - i + 1 - 2*exclm, ++ &size, &allocd) < 0) ++ goto fail; ++ i = j; ++ ++ } else if (c == '*') { ++ if (append(&buf, ".*", 0, &size, &allocd) < 0) ++ goto fail; ++ } else if (c == '?') { ++ if (append(&buf, ".", 0, &size, &allocd) < 0) ++ goto fail; ++ } else if (c == '.') { ++ if (append(&buf, "\\.", 0, &size, &allocd) < 0) ++ goto fail; ++ } else if (append(&buf, &c, 1, &size, &allocd) < 0) ++ goto fail; ++ } ++ } ++ ++ if (escape) { ++ free(buf); ++ return REG_EESCAPE; ++ } ++ ++ { ++ char c = 0; ++ if (append(&buf, &c, 1, &size, &allocd) < 0) ++ goto fail; ++ } ++ *retp = buf; ++ return 0; ++} ++ ++int ++globcomp(regex_t *preg, const char *glob, int cflags) ++{ ++ char *regex; ++ int status = glob_to_regex(glob, ®ex); ++ if (status != 0) ++ return status; ++ status = regcomp(preg, regex, cflags); ++ free(regex); ++ return status; ++} ++ ++#ifdef TEST ++#include ++#include ++ ++static void ++translate(const char *glob, int exp_status, const char *expect) ++{ ++ char *pattern = NULL; ++ int status = glob_to_regex(glob, &pattern); ++ if (status != exp_status) { ++ fprintf(stderr, "translating %s, expected status %d, got %d\n", ++ glob, exp_status, status); ++ return; ++ } ++ ++ if (status == 0) { ++ assert(pattern != NULL); ++ if (strcmp(pattern, expect) != 0) ++ fprintf(stderr, "translating %s, expected %s, got %s\n", ++ glob, expect, pattern); ++ free(pattern); ++ } else { ++ assert(pattern == NULL); ++ } ++} ++ ++static void ++try_match(const char *glob, const char *str, int expect) ++{ ++ regex_t preg; ++ int status = globcomp(&preg, glob, 0); ++ assert(status == 0); ++ status = regexec(&preg, str, 0, NULL, 0); ++ assert(status == expect); ++ regfree(&preg); ++} ++ ++int ++main(void) ++{ ++ translate("*", 0, ".*"); ++ translate("?", 0, "."); ++ translate(".*", 0, "\\..*"); ++ translate("*.*", 0, ".*\\..*"); ++ translate("*a*", 0, ".*a.*"); ++ translate("[abc]", 0, "[abc]"); ++ translate("[^abc]", 0, "[^abc]"); ++ translate("[!abc]", 0, "[^abc]"); ++ translate("[]]", 0, "[]]"); ++ translate("[[]", 0, "[[]"); ++ translate("[^]]", 0, "[^]]"); ++ translate("[^a-z]", 0, "[^a-z]"); ++ translate("[abc\\]]", 0, "[abc\\]]"); ++ translate("[abc\\]def]", 0, "[abc\\]def]"); ++ translate("[[:space:]]", 0, "[[:space:]]"); ++ translate("[^[:space:]]", 0, "[^[:space:]]"); ++ translate("[![:space:]]", 0, "[^[:space:]]"); ++ translate("[^a-z]*", 0, "[^a-z].*"); ++ translate("[^a-z]bar*", 0, "[^a-z]bar.*"); ++ translate("*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.", 0, ++ ".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\." ++ ".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\."); ++ ++ translate("\\", REG_EESCAPE, NULL); ++ translate("[^[:naotuh\\", REG_EBRACK, NULL); ++ translate("[^[:", REG_EBRACK, NULL); ++ translate("[^[", REG_EBRACK, NULL); ++ translate("[^", REG_EBRACK, NULL); ++ translate("[\\", REG_EBRACK, NULL); ++ translate("[", REG_EBRACK, NULL); ++ ++ try_match("abc*def", "abc012def", 0); ++ try_match("abc*def", "ab012def", REG_NOMATCH); ++ try_match("[abc]*def", "a1def", 0); ++ try_match("[abc]*def", "b1def", 0); ++ try_match("[abc]*def", "d1def", REG_NOMATCH); ++ ++ return 0; ++} ++ ++#endif +diff --git a/glob.h b/glob.h +new file mode 100644 +index 0000000..d60c0a2 +--- /dev/null ++++ b/glob.h +@@ -0,0 +1,32 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2007, 2008, 2012 Petr Machata, Red Hat Inc. ++ * ++ * 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 _GLOB_H_ ++#define _GLOB_H_ ++ ++#include ++#include ++ ++/* This is akin to regcomp(3), except it compiles a glob expression ++ * passed in GLOB. See glob(7) for more information about the syntax ++ * supported by globcomp. */ ++int globcomp(regex_t *preg, const char *glob, int cflags); ++ ++#endif /* _GLOB_H_ */ +diff --git a/handle_event.c b/handle_event.c +index ec4c9f3..73c118a 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -1,20 +1,18 @@ + #define _GNU_SOURCE + #include "config.h" + ++#include ++#include ++#include + #include +-#include + #include +-#include +-#include ++#include + #include +-#include +- +-#ifdef __powerpc__ +-#include +-#endif + +-#include "common.h" + #include "breakpoint.h" ++#include "common.h" ++#include "library.h" ++#include "proc.h" + + static void handle_signal(Event *event); + static void handle_exit(Event *event); +@@ -42,7 +40,7 @@ call_handler(Process * proc, Event * event) + { + assert(proc != NULL); + +- Event_Handler * handler = proc->event_handler; ++ struct event_handler *handler = proc->event_handler; + if (handler == NULL) + return event; + +@@ -86,60 +84,72 @@ handle_event(Event *event) + debug(1, "event: none"); + return; + case EVENT_SIGNAL: +- debug(1, "event: signal (%s [%d])", ++ debug(1, "[%d] event: signal (%s [%d])", ++ event->proc->pid, + shortsignal(event->proc, event->e_un.signum), + event->e_un.signum); + handle_signal(event); + return; + case EVENT_EXIT: +- debug(1, "event: exit (%d)", event->e_un.ret_val); ++ debug(1, "[%d] event: exit (%d)", ++ event->proc->pid, ++ event->e_un.ret_val); + handle_exit(event); + return; + case EVENT_EXIT_SIGNAL: +- debug(1, "event: exit signal (%s [%d])", ++ debug(1, "[%d] event: exit signal (%s [%d])", ++ event->proc->pid, + shortsignal(event->proc, event->e_un.signum), + event->e_un.signum); + handle_exit_signal(event); + return; + case EVENT_SYSCALL: +- debug(1, "event: syscall (%s [%d])", ++ debug(1, "[%d] event: syscall (%s [%d])", ++ event->proc->pid, + sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_syscall(event); + return; + case EVENT_SYSRET: +- debug(1, "event: sysret (%s [%d])", ++ debug(1, "[%d] event: sysret (%s [%d])", ++ event->proc->pid, + sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_sysret(event); + return; + case EVENT_ARCH_SYSCALL: +- debug(1, "event: arch_syscall (%s [%d])", +- arch_sysname(event->proc, event->e_un.sysnum), +- event->e_un.sysnum); ++ debug(1, "[%d] event: arch_syscall (%s [%d])", ++ event->proc->pid, ++ arch_sysname(event->proc, event->e_un.sysnum), ++ event->e_un.sysnum); + handle_arch_syscall(event); + return; + case EVENT_ARCH_SYSRET: +- debug(1, "event: arch_sysret (%s [%d])", +- arch_sysname(event->proc, event->e_un.sysnum), +- event->e_un.sysnum); ++ debug(1, "[%d] event: arch_sysret (%s [%d])", ++ event->proc->pid, ++ arch_sysname(event->proc, event->e_un.sysnum), ++ event->e_un.sysnum); + handle_arch_sysret(event); + return; + case EVENT_CLONE: + case EVENT_VFORK: +- debug(1, "event: clone (%u)", event->e_un.newpid); ++ debug(1, "[%d] event: clone (%u)", ++ event->proc->pid, event->e_un.newpid); + handle_clone(event); + return; + case EVENT_EXEC: +- debug(1, "event: exec()"); ++ debug(1, "[%d] event: exec()", ++ event->proc->pid); + handle_exec(event); + return; + case EVENT_BREAKPOINT: +- debug(1, "event: breakpoint"); ++ debug(1, "[%d] event: breakpoint %p", ++ event->proc->pid, event->e_un.brk_addr); + handle_breakpoint(event); + return; + case EVENT_NEW: +- debug(1, "event: new process"); ++ debug(1, "[%d] event: new process", ++ event->e_un.newpid); + handle_new(event); + return; + default: +@@ -148,37 +158,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; +@@ -240,83 +219,45 @@ pending_new_remove(pid_t pid) { + } + } + +-static int +-clone_breakpoints(Process * proc, Process * orig_proc) +-{ +- /* When copying breakpoints, we also have to copy the +- * referenced symbols, and link them properly. */ +- Dict * map = dict_init(&dict_key2hash_int, &dict_key_cmp_int); +- struct library_symbol * it = proc->list_of_symbols; +- proc->list_of_symbols = NULL; +- for (; it != NULL; it = it->next) { +- struct library_symbol * libsym = clone_library_symbol(it); +- if (libsym == NULL) { +- int save_errno; +- err: +- save_errno = errno; +- destroy_library_symbol_chain(proc->list_of_symbols); +- dict_clear(map); +- errno = save_errno; +- return -1; +- } +- libsym->next = proc->list_of_symbols; +- proc->list_of_symbols = libsym; +- if (dict_enter(map, it, libsym) != 0) +- goto err; +- } +- +- proc->breakpoints = dict_clone2(orig_proc->breakpoints, +- address_clone, breakpoint_clone, map); +- if (proc->breakpoints == NULL) +- goto err; +- +- dict_clear(map); +- return 0; +-} +- + 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; +- + if (event->type == EVENT_VFORK) +- continue_after_vfork(p); ++ continue_after_vfork(proc); + else + continue_process(event->proc->pid); + } +@@ -445,14 +386,38 @@ handle_exit_signal(Event *event) { + } + + static void ++output_syscall(struct Process *proc, const char *name, ++ void (*output)(enum tof, struct Process *, ++ struct library_symbol *)) ++{ ++ struct library_symbol syscall; ++ if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { ++ (*output)(LT_TOF_SYSCALL, proc, &syscall); ++ library_symbol_destroy(&syscall); ++ } ++} ++ ++static void ++output_syscall_left(struct Process *proc, const char *name) ++{ ++ output_syscall(proc, name, &output_left); ++} ++ ++static void ++output_syscall_right(struct Process *proc, const char *name) ++{ ++ output_syscall(proc, name, &output_right); ++} ++ ++static void + handle_syscall(Event *event) { + debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + callstack_push_syscall(event->proc, event->e_un.sysnum); +- if (options.syscalls) { +- output_left(LT_TOF_SYSCALL, event->proc, +- sysname(event->proc, event->e_un.sysnum)); +- } ++ if (options.syscalls) ++ output_syscall_left(event->proc, ++ sysname(event->proc, ++ event->e_un.sysnum)); + } + continue_after_syscall(event->proc, event->e_un.sysnum, 0); + } +@@ -461,20 +426,36 @@ static void + handle_exec(Event * event) { + Process * proc = event->proc; + ++ /* Save the PID so that we can use it after unsuccessful ++ * process_exec. */ ++ pid_t pid = proc->pid; ++ + debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); + if (proc->state == STATE_IGNORED) { +- untrace_pid(proc->pid); ++ untrace: ++ untrace_pid(pid); + remove_process(proc); + return; + } + output_line(proc, "--- Called exec() ---"); +- proc->mask_32bit = 0; +- proc->personality = 0; +- proc->arch_ptr = NULL; +- free(proc->filename); +- proc->filename = pid2name(proc->pid); +- breakpoints_init(proc, 0); +- proc->callstack_depth = 0; ++ ++ if (process_exec(proc) < 0) { ++ fprintf(stderr, ++ "couldn't reinitialize process %d after exec\n", pid); ++ goto untrace; ++ } ++ ++ continue_process(proc->pid); ++ ++ /* After the exec, we expect to hit the first executable ++ * instruction. ++ * ++ * XXX TODO It would be nice to have this removed, but then we ++ * need to do that also for initial call to wait_for_proc in ++ * execute_program. In that case we could generate a ++ * EVENT_FIRST event or something, or maybe this could somehow ++ * be rolled into EVENT_NEW. */ ++ wait_for_proc(proc->pid); + continue_process(proc->pid); + } + +@@ -484,8 +465,9 @@ handle_arch_syscall(Event *event) { + if (event->proc->state != STATE_IGNORED) { + callstack_push_syscall(event->proc, 0xf0000 + event->e_un.sysnum); + if (options.syscalls) { +- output_left(LT_TOF_SYSCALL, event->proc, +- arch_sysname(event->proc, event->e_un.sysnum)); ++ output_syscall_left(event->proc, ++ arch_sysname(event->proc, ++ event->e_un.sysnum)); + } + } + continue_process(event->proc->pid); +@@ -522,10 +504,11 @@ handle_sysret(Event *event) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } +- if (options.syscalls) { +- output_right(LT_TOF_SYSCALLR, event->proc, +- sysname(event->proc, event->e_un.sysnum)); +- } ++ if (options.syscalls) ++ output_syscall_right(event->proc, ++ sysname(event->proc, ++ event->e_un.sysnum)); ++ + assert(event->proc->callstack_depth > 0); + unsigned d = event->proc->callstack_depth - 1; + assert(event->proc->callstack[d].is_syscall); +@@ -541,10 +524,10 @@ handle_arch_sysret(Event *event) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } +- if (options.syscalls) { +- output_right(LT_TOF_SYSCALLR, event->proc, +- arch_sysname(event->proc, event->e_un.sysnum)); +- } ++ if (options.syscalls) ++ output_syscall_right(event->proc, ++ arch_sysname(event->proc, ++ event->e_un.sysnum)); + callstack_pop(event->proc); + } + continue_process(event->proc->pid); +@@ -556,7 +539,7 @@ output_right_tos(struct Process *proc) + size_t d = proc->callstack_depth; + struct callstack_element *elem = &proc->callstack[d - 1]; + if (proc->state != STATE_IGNORED) +- output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc->name); ++ output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc); + } + + static void +@@ -564,43 +564,7 @@ handle_breakpoint(Event *event) + + for (i = event->proc->callstack_depth - 1; i >= 0; i--) { + if (brk_addr == event->proc->callstack[i].return_addr) { +- struct library_symbol *libsym = +- event->proc->callstack[i].c_un.libfunc; +-#ifdef __powerpc__ +- /* +- * PPC HACK! (XXX FIXME TODO) +- * The PLT gets modified during the first call, +- * so be sure to re-enable the breakpoint. +- */ +- unsigned long a; +- void *addr = sym2addr(event->proc, libsym); +- +- if (libsym->plt_type != LS_TOPLT_POINT) { +- unsigned char break_insn[] = BREAKPOINT_VALUE; +- +- sbp = address2bpstruct(leader, addr); +- assert(sbp); +- a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, +- addr); +- +- if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { +- sbp->enabled--; +- insert_breakpoint(event->proc, addr, +- libsym, 1); +- } +- } else { +- sbp = dict_find_entry(leader->breakpoints, addr); +- /* On powerpc, the breakpoint address +- may end up being actual entry point +- of the library symbol, not the PLT +- address we computed. In that case, +- sbp is NULL. */ +- if (sbp == NULL || addr != sbp->addr) { +- insert_breakpoint(event->proc, addr, +- libsym, 1); +- } +- } +-#elif defined(__mips__) ++#if defined(__mips__) + void *addr = NULL; + struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; + struct library_symbol *new_sym; +@@ -624,14 +571,14 @@ handle_breakpoint(Event *event) + sbp = dict_find_entry(leader->breakpoints, addr); + if (sbp) { + if (addr != sbp->addr) { +- insert_breakpoint(event->proc, addr, sym, 1); ++ insert_breakpoint(event->proc, addr, sym); + } + } else { + new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); + memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); + new_sym->next = leader->list_of_symbols; + leader->list_of_symbols = new_sym; +- insert_breakpoint(event->proc, addr, new_sym, 1); ++ insert_breakpoint(event->proc, addr, new_sym); + } + #endif + for (j = event->proc->callstack_depth - 1; j > i; j--) { +@@ -644,6 +591,9 @@ handle_breakpoint(Event *event) + } + event->proc->return_addr = brk_addr; + ++ struct library_symbol *libsym = ++ event->proc->callstack[i].c_un.libfunc; ++ + output_right_tos(event->proc); + callstack_pop(event->proc); + +@@ -666,41 +616,44 @@ handle_breakpoint(Event *event) + callstack_pop(event->proc); + } + +- sbp = address2bpstruct(leader, brk_addr); +- continue_after_breakpoint(event->proc, sbp); ++ /* Maybe the previous callstack_pop's got rid ++ * of the breakpoint, but if we are in a ++ * recursive call, it's still enabled. In ++ * that case we need to skip it properly. */ ++ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { ++ continue_after_breakpoint(event->proc, sbp); ++ } else { ++ set_instruction_pointer(event->proc, brk_addr); ++ continue_process(event->proc->pid); ++ } + return; + } + } + +- if ((sbp = address2bpstruct(leader, brk_addr))) { ++ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) + 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) { ++ else if (event->proc->state != STATE_IGNORED) ++ output_line(event->proc, ++ "unexpected breakpoint at %p", brk_addr); ++ ++ /* breakpoint_on_hit may delete its own breakpoint, so we have ++ * to look it up again. */ ++ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { ++ 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); + callstack_push_symfunc(event->proc, sbp->libsym); +- output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); ++ output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); + } + +- continue_after_breakpoint(event->proc, sbp); ++ breakpoint_on_continue(sbp, event->proc); + return; ++ } else { ++ set_instruction_pointer(event->proc, brk_addr); + } + +- if (event->proc->state != STATE_IGNORED && !options.no_plt) { +- output_line(event->proc, "unexpected breakpoint at %p", +- brk_addr); +- } + continue_process(event->proc->pid); + } + +@@ -745,9 +698,8 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + elem->c_un.libfunc = sym; + + elem->return_addr = proc->return_addr; +- if (elem->return_addr) { +- insert_breakpoint(proc, elem->return_addr, NULL, 1); +- } ++ if (elem->return_addr) ++ insert_breakpoint(proc, elem->return_addr, NULL); + + /* handle functions like atexit() on mips which have no return */ + if (opt_T || options.summary) { +diff --git a/libltrace.c b/libltrace.c +index 777ad1b..92f2701 100644 +--- a/libltrace.c ++++ b/libltrace.c +@@ -1,22 +1,23 @@ + #include "config.h" + ++#include ++#include ++#include ++#include + #include + #include +-#include + #include +-#include +-#include +-#include +-#include ++#include + + #include "common.h" ++#include "proc.h" + + 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; + +@@ -38,7 +39,7 @@ stop_non_p_processes (Process * proc, void * data) + kill(proc->pid, SIGSTOP); + } + +- return pcb_cont; ++ return CBS_CONT; + } + + static void +@@ -104,20 +105,22 @@ ltrace_init(int argc, char **argv) { + } + opt_F = opt_F->next; + } +- if (opt_e) { +- struct opt_e_t *tmp = opt_e; +- while (tmp) { +- debug(1, "Option -e: %s\n", tmp->name); +- tmp = tmp->next; +- } +- } + if (command) { + /* Check that the binary ABI is supported before + * calling execute_program. */ + struct ltelf lte = {}; + open_elf(<e, command); + +- open_program(command, execute_program(command, argv), 0); ++ pid_t pid = execute_program(command, argv); ++ struct Process *proc = open_program(command, pid); ++ if (proc == NULL) { ++ fprintf(stderr, "couldn't open program '%s': %s\n", ++ command, strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ ++ trace_set_options(proc); ++ continue_process(pid); + } + opt_p_tmp = opt_p; + while (opt_p_tmp) { +diff --git a/library.c b/library.c +new file mode 100644 +index 0000000..92fccea +--- /dev/null ++++ b/library.c +@@ -0,0 +1,354 @@ ++/* ++ * 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 ++ ++#include "library.h" ++#include "proc.h" // for enum callback_status ++#include "debug.h" ++#include "common.h" // for arch_library_symbol_init, arch_library_init ++ ++#ifndef ARCH_HAVE_LIBRARY_DATA ++void ++arch_library_init(struct library *lib) ++{ ++} ++ ++void ++arch_library_destroy(struct library *lib) ++{ ++} ++ ++void ++arch_library_clone(struct library *retp, struct library *lib) ++{ ++} ++#endif ++ ++#ifndef ARCH_HAVE_LIBRARY_SYMBOL_DATA ++int ++arch_library_symbol_init(struct library_symbol *libsym) ++{ ++ return 0; ++} ++ ++void ++arch_library_symbol_destroy(struct library_symbol *libsym) ++{ ++} ++ ++int ++arch_library_symbol_clone(struct library_symbol *retp, ++ struct library_symbol *libsym) ++{ ++ return 0; ++} ++#endif ++ ++unsigned int ++target_address_hash(const void *key) ++{ ++ /* XXX this assumes that key is passed by value. */ ++ union { ++ target_address_t addr; ++ unsigned int ints[sizeof(target_address_t) ++ / sizeof(unsigned int)]; ++ } u = { .addr = (target_address_t)key }; ++ ++ size_t i; ++ unsigned int h = 0; ++ for (i = 0; i < sizeof(u.ints) / sizeof(*u.ints); ++i) ++ h ^= dict_key2hash_int((void *)(uintptr_t)u.ints[i]); ++ return h; ++} ++ ++int ++target_address_cmp(const void *key1, const void *key2) ++{ ++ /* XXX this assumes that key is passed by value. */ ++ target_address_t addr1 = (target_address_t)key1; ++ target_address_t addr2 = (target_address_t)key2; ++ return addr1 < addr2 ? 1 ++ : addr1 > addr2 ? -1 : 0; ++} ++ ++/* 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; ++ } ++} ++ ++static void ++private_library_symbol_init(struct library_symbol *libsym, ++ target_address_t addr, ++ const char *name, int own_name, ++ enum toplt type_of_plt) ++{ ++ libsym->next = NULL; ++ libsym->lib = NULL; ++ libsym->plt_type = type_of_plt; ++ libsym->name = name; ++ libsym->own_name = own_name; ++ libsym->enter_addr = (void *)(uintptr_t)addr; ++} ++ ++static void ++private_library_symbol_destroy(struct library_symbol *libsym) ++{ ++ library_symbol_set_name(libsym, NULL, 0); ++} ++ ++int ++library_symbol_init(struct library_symbol *libsym, ++ target_address_t addr, const char *name, int own_name, ++ enum toplt type_of_plt) ++{ ++ private_library_symbol_init(libsym, addr, name, own_name, type_of_plt); ++ ++ /* If arch init fails, we've already set libsym->name and ++ * own_name. But we return failure, and the client code isn't ++ * supposed to call library_symbol_destroy in such a case. */ ++ return arch_library_symbol_init(libsym); ++} ++ ++void ++library_symbol_destroy(struct library_symbol *libsym) ++{ ++ if (libsym != NULL) { ++ private_library_symbol_destroy(libsym); ++ arch_library_symbol_destroy(libsym); ++ } ++} ++ ++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; ++ ++ private_library_symbol_init(retp, libsym->enter_addr, ++ name, libsym->own_name, libsym->plt_type); ++ ++ if (arch_library_symbol_clone(retp, libsym) < 0) { ++ private_library_symbol_destroy(retp); ++ return -1; ++ } ++ ++ 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; ++} ++ ++void ++library_symbol_set_name(struct library_symbol *libsym, ++ const char *name, int own_name) ++{ ++ if (libsym->own_name) ++ free((char *)libsym->name); ++ libsym->name = name; ++ libsym->own_name = own_name; ++} ++ ++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; ++} ++ ++ ++static void ++private_library_init(struct library *lib, enum library_type type) ++{ ++ lib->next = NULL; ++ lib->soname = NULL; ++ lib->own_soname = 0; ++ lib->pathname = NULL; ++ lib->own_pathname = 0; ++ lib->symbols = NULL; ++ lib->type = type; ++} ++ ++void ++library_init(struct library *lib, enum library_type type) ++{ ++ private_library_init(lib, type); ++ arch_library_init(lib); ++} ++ ++int ++library_clone(struct library *retp, struct library *lib) ++{ ++ const char *soname = NULL; ++ const char *pathname; ++ if (strdup_if_owned(&soname, lib->soname, lib->own_soname) < 0 ++ || strdup_if_owned(&pathname, ++ lib->pathname, lib->own_pathname) < 0) { ++ if (lib->own_soname) ++ free((char *)soname); ++ return -1; ++ } ++ ++ private_library_init(retp, lib->type); ++ library_set_soname(retp, soname, lib->own_soname); ++ library_set_soname(retp, pathname, lib->own_pathname); ++ arch_library_clone(retp, lib); ++ ++ 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)->lib = retp; ++ nsymp = &(*nsymp)->next; ++ } ++ return 0; ++} ++ ++void ++library_destroy(struct library *lib) ++{ ++ if (lib == NULL) ++ return; ++ ++ arch_library_destroy(lib); ++ library_set_soname(lib, NULL, 0); ++ library_set_pathname(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_soname(struct library *lib, const char *new_name, int own_name) ++{ ++ if (lib->own_soname) ++ free((char *)lib->soname); ++ lib->soname = new_name; ++ lib->own_soname = own_name; ++} ++ ++void ++library_set_pathname(struct library *lib, const char *new_name, int own_name) ++{ ++ if (lib->own_pathname) ++ free((char *)lib->pathname); ++ lib->pathname = new_name; ++ lib->own_pathname = own_name; ++} ++ ++struct library_symbol * ++library_each_symbol(struct library *lib, struct library_symbol *start_after, ++ enum callback_status (*cb)(struct library_symbol *, void *), ++ void *data) ++{ ++ struct library_symbol *it = start_after == NULL ? lib->symbols ++ : start_after->next; ++ ++ while (it != NULL) { ++ struct library_symbol *next = it->next; ++ ++ switch ((*cb)(it, data)) { ++ case CBS_FAIL: ++ /* XXX handle me */ ++ case CBS_STOP: ++ return it; ++ case CBS_CONT: ++ break; ++ } ++ ++ it = next; ++ } ++ ++ return NULL; ++} ++ ++void ++library_add_symbol(struct library *lib, struct library_symbol *first) ++{ ++ struct library_symbol *last; ++ for (last = first; last != NULL; ) { ++ last->lib = lib; ++ if (last->next != NULL) ++ last = last->next; ++ else ++ break; ++ } ++ ++ assert(last->next == NULL); ++ last->next = lib->symbols; ++ lib->symbols = first; ++} ++ ++enum callback_status ++library_named_cb(struct Process *proc, struct library *lib, void *name) ++{ ++ if (name == lib->soname ++ || strcmp(lib->soname, (char *)name) == 0) ++ return CBS_STOP; ++ else ++ return CBS_CONT; ++} ++ ++enum callback_status ++library_with_key_cb(struct Process *proc, struct library *lib, void *keyp) ++{ ++ return lib->key == *(target_address_t *)keyp ? CBS_STOP : CBS_CONT; ++} +diff --git a/library.h b/library.h +new file mode 100644 +index 0000000..c387b02 +--- /dev/null ++++ b/library.h +@@ -0,0 +1,196 @@ ++/* ++ * 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 "sysdep.h" ++ ++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. */ ++}; ++ ++/* 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; ++ ++/* Dict interface. */ ++unsigned int target_address_hash(const void *key); ++int target_address_cmp(const void *key1, const void *key2); ++ ++struct library_symbol { ++ struct library_symbol *next; ++ struct library *lib; ++ const char *name; ++ target_address_t enter_addr; ++ enum toplt plt_type; ++ char own_name; ++ struct arch_library_symbol_data arch; ++}; ++ ++/* Init LIBSYM. NAME will be freed when LIBSYM is destroyed if ++ * OWN_NAME. ARCH has to be initialized by a separate call. */ ++int library_symbol_init(struct library_symbol *libsym, ++ target_address_t addr, const char *name, int own_name, ++ enum toplt type_of_plt); ++ ++/* 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); ++ ++/* Set a name for library symbol. This frees the old name, if ++ * that is owned. */ ++void library_symbol_set_name(struct library_symbol *libsym, ++ const char *name, int own_name); ++ ++/* 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); ++ ++enum library_type { ++ LT_LIBTYPE_MAIN, ++ LT_LIBTYPE_DSO, ++}; ++ ++/* 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; ++ ++ /* Unique key. Two library objects are considered equal, if ++ * they have the same key. */ ++ target_address_t key; ++ ++ /* 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 suppose the value might be useful for the ++ * dynamic linker, too (in case we ever want to do early ++ * process tracing). */ ++ target_address_t entry; ++ ++ /* Address of PT_DYNAMIC segment. */ ++ target_address_t dyn_addr; ++ ++ /* Symbols associated with the library. */ ++ struct library_symbol *symbols; ++ ++ const char *soname; ++ const char *pathname; ++ ++ enum library_type type; ++ ++ char own_soname : 1; ++ char own_pathname : 1; ++ ++ struct arch_library_data arch; ++}; ++ ++/* Init LIB. */ ++void library_init(struct library *lib, enum library_type type); ++ ++/* 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. Symbols are destroyed ++ * and freed. */ ++void library_destroy(struct library *lib); ++ ++/* Set library soname. Frees the old name if necessary. */ ++void library_set_soname(struct library *lib, ++ const char *new_name, int own_name); ++ ++/* Set library pathname. Frees the old name if necessary. */ ++void library_set_pathname(struct library *lib, ++ const char *new_name, int own_name); ++ ++/* Iterate through list of symbols of library LIB. Restarts are ++ * supported via START_AFTER (see each_process for details of ++ * iteration interface). */ ++struct library_symbol *library_each_symbol ++ (struct library *lib, struct library_symbol *start_after, ++ 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 key 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_key_cb(struct Process *proc, ++ struct library *lib, void *keyp); ++ ++/* XXX this should really be in backend.h (as on pmachata/revamp ++ * branch), or, on this branch, in common.h. But we need ++ * target_address_t (which should also be in backend.h, I reckon), so ++ * stuff it here for the time being. */ ++/* This function is implemented in the back end. It is called for all ++ * raw addresses as read from symbol tables etc. If necessary on ++ * given architecture, this function should translate the address ++ * according to .opd or other indirection mechanism. Returns 0 on ++ * success and a negative value on failure. */ ++struct ltelf; ++int arch_translate_address(struct ltelf *lte, ++ target_address_t addr, target_address_t *ret); ++/* This is the same function as arch_translate_address, except it's ++ * used at the point that we don't have ELF available anymore. */ ++int arch_translate_address_dyn(struct Process *proc, ++ target_address_t addr, target_address_t *ret); ++ ++#endif /* _LIBRARY_H_ */ +diff --git a/ltrace-elf.c b/ltrace-elf.c +index f7fc239..a311c5f 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -1,68 +1,96 @@ + #include "config.h" + ++#include + #include + #include +-#include + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +-#include + + #include "common.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 "proc.h" ++#include "library.h" ++#include "filter.h" + + #ifdef PLT_REINITALISATION_BP + extern char *PLTs_initialized_by_here; + #endif + +-#ifndef DT_PPC_GOT +-# define DT_PPC_GOT (DT_LOPROC + 0) +-#endif +- +-#define PPC_PLT_STUB_SIZE 16 +- +-static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr) ++#ifndef ARCH_HAVE_LTELF_DATA ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) + { +- Elf_Data *data = elf_getdata(scn, NULL); +- if (data == NULL || elf_getdata(scn, data) != NULL +- || data->d_off || data->d_size != shdr->sh_size) +- return NULL; +- return data; ++ return 0; + } + +-static int inside(GElf_Addr addr, GElf_Shdr *shdr) ++void ++arch_elf_destroy(struct ltelf *lte) + { +- return addr >= shdr->sh_addr +- && addr < shdr->sh_addr + shdr->sh_size; + } ++#endif + +-static int maybe_pick_section(GElf_Addr addr, +- Elf_Scn *in_sec, GElf_Shdr *in_shdr, +- Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) ++int ++default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++ const char *a_name, GElf_Rela *rela, size_t ndx, ++ struct library_symbol **ret) + { +- if (inside (addr, in_shdr)) { +- *tgt_sec = in_sec; +- *tgt_shdr = *in_shdr; +- return 1; ++ char *name = strdup(a_name); ++ if (name == NULL) { ++ fail: ++ free(name); ++ return -1; ++ } ++ ++ GElf_Addr addr = arch_plt_sym_val(lte, ndx, rela); ++ ++ struct library_symbol *libsym = malloc(sizeof(*libsym)); ++ if (libsym == NULL) ++ goto fail; ++ ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ target_address_t taddr = (target_address_t) ++ (uintptr_t)(addr + lte->bias); ++ ++ if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) { ++ free(libsym); ++ goto fail; + } ++ ++ *ret = libsym; + return 0; + } + +-static int get_section_covering(struct ltelf *lte, GElf_Addr addr, +- Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) ++#ifndef ARCH_HAVE_ADD_PLT_ENTRY ++enum plt_status ++arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++ const char *a_name, GElf_Rela *rela, size_t ndx, ++ struct library_symbol **ret) ++{ ++ return plt_default; ++} ++#endif ++ ++Elf_Data * ++elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr) ++{ ++ Elf_Data *data = elf_getdata(scn, NULL); ++ if (data == NULL || elf_getdata(scn, data) != NULL ++ || data->d_off || data->d_size != shdr->sh_size) ++ return NULL; ++ return data; ++} ++ ++static int ++elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr, ++ int (*predicate)(Elf_Scn *, GElf_Shdr *, void *data), ++ void *data) + { + int i; + for (i = 1; i < lte->ehdr.e_shnum; ++i) { +@@ -72,69 +100,115 @@ static int get_section_covering(struct ltelf *lte, GElf_Addr addr, + scn = elf_getscn(lte->elf, i); + if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) { + debug(1, "Couldn't read section or header."); ++ return -1; ++ } ++ if (predicate(scn, &shdr, data)) { ++ *tgt_sec = scn; ++ *tgt_shdr = shdr; + return 0; + } +- +- if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr)) +- return 1; + } ++ return -1; + +- return 0; + } + +-static GElf_Addr read32be(Elf_Data *data, size_t offset) ++static int ++inside_p(Elf_Scn *scn, GElf_Shdr *shdr, void *data) + { +- if (data->d_size < offset + 4) { +- debug(1, "Not enough data to read 32bit value at offset %zd.", +- offset); +- return 0; +- } ++ GElf_Addr addr = *(GElf_Addr *)data; ++ return addr >= shdr->sh_addr ++ && addr < shdr->sh_addr + shdr->sh_size; ++} ++ ++int ++elf_get_section_covering(struct ltelf *lte, GElf_Addr addr, ++ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) ++{ ++ return elf_get_section_if(lte, tgt_sec, tgt_shdr, ++ &inside_p, &addr); ++} + +- unsigned char const *buf = data->d_buf + offset; +- return ((Elf32_Word)buf[0] << 24) +- | ((Elf32_Word)buf[1] << 16) +- | ((Elf32_Word)buf[2] << 8) +- | ((Elf32_Word)buf[3]); ++static int ++type_p(Elf_Scn *scn, GElf_Shdr *shdr, void *data) ++{ ++ GElf_Word type = *(GElf_Word *)data; ++ return shdr->sh_type == type; + } + +-static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, +- Elf_Data *plt_data) ++int ++elf_get_section_type(struct ltelf *lte, GElf_Word type, ++ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) + { +- Elf_Scn *ppcgot_sec = NULL; +- GElf_Shdr ppcgot_shdr; +- if (ppcgot != 0 +- && !get_section_covering(lte, ppcgot, &ppcgot_sec, &ppcgot_shdr)) +- // xxx should be the log out +- fprintf(stderr, +- "DT_PPC_GOT=%#" PRIx64 ", but no such section found.\n", +- ppcgot); +- +- if (ppcgot_sec != NULL) { +- Elf_Data *data = loaddata(ppcgot_sec, &ppcgot_shdr); +- if (data == NULL +- || data->d_size < 8 ) +- debug(1, "Couldn't read GOT data."); +- else { +- // where PPCGOT begins in .got +- size_t offset = ppcgot - ppcgot_shdr.sh_addr; +- GElf_Addr glink_vma = read32be(data, offset + 4); +- if (glink_vma != 0) { +- debug(1, "PPC GOT glink_vma address: %#" PRIx64, +- glink_vma); +- return glink_vma; +- } +- } +- } ++ return elf_get_section_if(lte, tgt_sec, tgt_shdr, ++ &type_p, &type); ++} + +- if (plt_data != NULL) { +- GElf_Addr glink_vma = read32be(plt_data, 0); +- debug(1, ".plt glink_vma address: %#" PRIx64, glink_vma); +- return glink_vma; +- } ++struct section_named_data { ++ struct ltelf *lte; ++ const char *name; ++}; ++ ++static int ++name_p(Elf_Scn *scn, GElf_Shdr *shdr, void *d) ++{ ++ struct section_named_data *data = d; ++ const char *name = elf_strptr(data->lte->elf, ++ data->lte->ehdr.e_shstrndx, ++ shdr->sh_name); ++ return strcmp(name, data->name) == 0; ++} + ++int ++elf_get_section_named(struct ltelf *lte, const char *name, ++ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) ++{ ++ struct section_named_data data = { ++ .lte = lte, ++ .name = name, ++ }; ++ return elf_get_section_if(lte, tgt_sec, tgt_shdr, ++ &name_p, &data); ++} ++ ++static int ++need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) ++{ ++ assert(data != NULL); ++ if (data->d_size < size || offset > data->d_size - size) { ++ debug(1, "Not enough data to read %zd-byte value" ++ " at offset %zd.", size, offset); ++ return -1; ++ } + return 0; + } + ++#define DEF_READER(NAME, SIZE) \ ++ int \ ++ NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp) \ ++ { \ ++ if (!need_data(data, offset, SIZE / 8) < 0) \ ++ return -1; \ ++ \ ++ if (data->d_buf == NULL) /* NODATA section */ { \ ++ *retp = 0; \ ++ return 0; \ ++ } \ ++ \ ++ union { \ ++ uint##SIZE##_t dst; \ ++ char buf[0]; \ ++ } u; \ ++ memcpy(u.buf, data->d_buf + offset, sizeof(u.dst)); \ ++ *retp = u.dst; \ ++ return 0; \ ++ } ++ ++DEF_READER(elf_read_u16, 16) ++DEF_READER(elf_read_u32, 32) ++DEF_READER(elf_read_u64, 64) ++ ++#undef DEF_READER ++ + int + open_elf(struct ltelf *lte, const char *filename) + { +@@ -150,17 +224,22 @@ open_elf(struct ltelf *lte, const char *filename) + lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL); + #endif + +- if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF) +- error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename); ++ if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF) { ++ fprintf(stderr, "\"%s\" is not an ELF file\n", filename); ++ exit(EXIT_FAILURE); ++ } + +- if (gelf_getehdr(lte->elf, <e->ehdr) == NULL) +- error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"", +- filename); ++ if (gelf_getehdr(lte->elf, <e->ehdr) == NULL) { ++ fprintf(stderr, "can't read ELF header of \"%s\": %s\n", ++ filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } + +- if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN) +- error(EXIT_FAILURE, 0, +- "\"%s\" is not an ELF executable nor shared library", +- filename); ++ if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN) { ++ fprintf(stderr, "\"%s\" is neither an ELF executable" ++ " nor a shared library\n", filename); ++ exit(EXIT_FAILURE); ++ } + + if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS + || lte->ehdr.e_machine != LT_ELF_MACHINE) +@@ -172,18 +251,58 @@ open_elf(struct ltelf *lte, const char *filename) + && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3 + || lte->ehdr.e_machine != LT_ELF_MACHINE3) + #endif +- ) +- error(EXIT_FAILURE, 0, +- "\"%s\" is ELF from incompatible architecture", filename); ++ ) { ++ fprintf(stderr, ++ "\"%s\" is ELF from incompatible architecture\n", ++ filename); ++ exit(EXIT_FAILURE); ++ } + + return 0; + } + +-int +-do_init_elf(struct ltelf *lte, const char *filename) { ++static void ++read_symbol_table(struct ltelf *lte, const char *filename, ++ Elf_Scn *scn, GElf_Shdr *shdr, const char *name, ++ Elf_Data **datap, size_t *countp, const char **strsp) ++{ ++ *datap = elf_getdata(scn, NULL); ++ *countp = shdr->sh_size / shdr->sh_entsize; ++ if ((*datap == NULL || elf_getdata(scn, *datap) != NULL) ++ && options.static_filter != NULL) { ++ fprintf(stderr, "Couldn't get data of section" ++ " %s from \"%s\": %s\n", ++ name, filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } ++ ++ scn = elf_getscn(lte->elf, shdr->sh_link); ++ GElf_Shdr shdr2; ++ if (scn == NULL || gelf_getshdr(scn, &shdr2) == NULL) { ++ fprintf(stderr, "Couldn't get header of section" ++ " #%d from \"%s\": %s\n", ++ shdr2.sh_link, filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } ++ ++ Elf_Data *data = elf_getdata(scn, NULL); ++ if (data == NULL || elf_getdata(scn, data) != NULL ++ || shdr2.sh_size != data->d_size || data->d_off) { ++ fprintf(stderr, "Couldn't get data of section" ++ " #%d from \"%s\": %s\n", ++ shdr2.sh_link, filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } ++ ++ *strsp = data->d_buf; ++} ++ ++static int ++do_init_elf(struct ltelf *lte, const char *filename, GElf_Addr bias) ++{ + int i; + GElf_Addr relplt_addr = 0; +- size_t relplt_size = 0; ++ GElf_Addr soname_offset = 0; + + debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); + debug(1, "Reading ELF from %s...", filename); +@@ -191,8 +310,25 @@ do_init_elf(struct ltelf *lte, const char *filename) { + if (open_elf(lte, filename) < 0) + return -1; + +- Elf_Data *plt_data = NULL; +- GElf_Addr ppcgot = 0; ++ /* Find out the base address. */ ++ { ++ GElf_Phdr phdr; ++ for (i = 0; gelf_getphdr (lte->elf, i, &phdr) != NULL; ++i) { ++ if (phdr.p_type == PT_LOAD) { ++ lte->base_addr = phdr.p_vaddr + bias; ++ break; ++ } ++ } ++ } ++ ++ if (lte->base_addr == 0) { ++ fprintf(stderr, "Couldn't determine base address of %s\n", ++ filename); ++ return -1; ++ } ++ ++ lte->bias = bias; ++ lte->entry_addr = lte->ehdr.e_entry + lte->bias; + + for (i = 1; i < lte->ehdr.e_shnum; ++i) { + Elf_Scn *scn; +@@ -200,68 +336,29 @@ do_init_elf(struct ltelf *lte, const char *filename) { + const char *name; + + scn = elf_getscn(lte->elf, i); +- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get section header from \"%s\"", +- filename); ++ if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) { ++ fprintf(stderr, "Couldn't get section #%d from" ++ " \"%s\": %s\n", i, filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } + + name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name); +- if (name == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get section header from \"%s\"", +- filename); ++ if (name == NULL) { ++ fprintf(stderr, "Couldn't get name of section #%d from" ++ " \"%s\": %s\n", i, filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } + + if (shdr.sh_type == SHT_SYMTAB) { +- Elf_Data *data; +- +- lte->symtab = elf_getdata(scn, NULL); +- lte->symtab_count = shdr.sh_size / shdr.sh_entsize; +- if ((lte->symtab == NULL +- || elf_getdata(scn, lte->symtab) != NULL) +- && opt_x != NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get .symtab data from \"%s\"", +- filename); +- +- scn = elf_getscn(lte->elf, shdr.sh_link); +- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get section header from \"%s\"", +- filename); ++ read_symbol_table(lte, filename, ++ scn, &shdr, name, <e->symtab, ++ <e->symtab_count, <e->strtab); + +- data = elf_getdata(scn, NULL); +- if (data == NULL || elf_getdata(scn, data) != NULL +- || shdr.sh_size != data->d_size || data->d_off) +- error(EXIT_FAILURE, 0, +- "Couldn't get .strtab data from \"%s\"", +- filename); +- +- lte->strtab = data->d_buf; + } else if (shdr.sh_type == SHT_DYNSYM) { +- Elf_Data *data; +- +- lte->dynsym = elf_getdata(scn, NULL); +- lte->dynsym_count = shdr.sh_size / shdr.sh_entsize; +- if (lte->dynsym == NULL +- || elf_getdata(scn, lte->dynsym) != NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get .dynsym data from \"%s\"", +- filename); ++ read_symbol_table(lte, filename, ++ scn, &shdr, name, <e->dynsym, ++ <e->dynsym_count, <e->dynstr); + +- scn = elf_getscn(lte->elf, shdr.sh_link); +- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get section header from \"%s\"", +- filename); +- +- data = elf_getdata(scn, NULL); +- if (data == NULL || elf_getdata(scn, data) != NULL +- || shdr.sh_size != data->d_size || data->d_off) +- error(EXIT_FAILURE, 0, +- "Couldn't get .dynstr data from \"%s\"", +- filename); +- +- lte->dynstr = data->d_buf; + } else if (shdr.sh_type == SHT_DYNAMIC) { + Elf_Data *data; + size_t j; +@@ -270,135 +367,39 @@ do_init_elf(struct ltelf *lte, const char *filename) { + lte->dyn_sz = shdr.sh_size; + + data = elf_getdata(scn, NULL); +- if (data == NULL || elf_getdata(scn, data) != NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get .dynamic data from \"%s\"", +- filename); ++ if (data == NULL || elf_getdata(scn, data) != NULL) { ++ fprintf(stderr, "Couldn't get .dynamic data" ++ " from \"%s\": %s\n", ++ filename, strerror(errno)); ++ exit(EXIT_FAILURE); ++ } + + for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { + GElf_Dyn dyn; + +- if (gelf_getdyn(data, j, &dyn) == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get .dynamic data from \"%s\"", +- filename); +-#ifdef __mips__ +-/** +- MIPS ABI Supplement: +- +- DT_PLTGOT This member holds the address of the .got section. +- +- DT_MIPS_SYMTABNO This member holds the number of entries in the +- .dynsym section. +- +- DT_MIPS_LOCAL_GOTNO This member holds the number of local global +- offset table entries. +- +- DT_MIPS_GOTSYM This member holds the index of the first dyamic +- symbol table entry that corresponds to an entry in the gobal offset +- table. +- +- */ +- if(dyn.d_tag==DT_PLTGOT){ +- lte->pltgot_addr=dyn.d_un.d_ptr; +- } +- if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){ +- lte->mips_local_gotno=dyn.d_un.d_val; ++ if (gelf_getdyn(data, j, &dyn) == NULL) { ++ fprintf(stderr, "Couldn't get .dynamic" ++ " data from \"%s\": %s\n", ++ filename, strerror(errno)); ++ exit(EXIT_FAILURE); + } +- if(dyn.d_tag==DT_MIPS_GOTSYM){ +- lte->mips_gotsym=dyn.d_un.d_val; +- } +-#endif // __mips__ + if (dyn.d_tag == DT_JMPREL) + relplt_addr = dyn.d_un.d_ptr; + else if (dyn.d_tag == DT_PLTRELSZ) +- relplt_size = dyn.d_un.d_val; +- else if (dyn.d_tag == DT_PPC_GOT) { +- ppcgot = dyn.d_un.d_val; +- debug(1, "ppcgot %#" PRIx64, ppcgot); +- } +- } +- } 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); ++ lte->relplt_size = dyn.d_un.d_val; ++ else if (dyn.d_tag == DT_SONAME) ++ soname_offset = dyn.d_un.d_val; + } +- +- 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) { + lte->plt_addr = shdr.sh_addr; + lte->plt_size = shdr.sh_size; +- if (shdr.sh_flags & SHF_EXECINSTR) { +- lte->lte_flags |= LTE_PLT_EXECUTABLE; +- } +- if (lte->ehdr.e_machine == EM_PPC) { +- plt_data = loaddata(scn, &shdr); +- if (plt_data == NULL) +- fprintf(stderr, +- "Can't load .plt data\n"); +- } ++ lte->plt_data = elf_loaddata(scn, &shdr); ++ if (lte->plt_data == NULL) ++ fprintf(stderr, ++ "Can't load .plt data\n"); ++ lte->plt_flags = shdr.sh_flags; + } + #ifdef ARCH_SUPPORTS_OPD + else if (strcmp(name, ".opd") == 0) { +@@ -410,449 +411,376 @@ do_init_elf(struct ltelf *lte, const char *filename) { + } + } + +- if (lte->dynsym == NULL || lte->dynstr == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't find .dynsym or .dynstr in \"%s\"", filename); ++ if (lte->dynsym == NULL || lte->dynstr == NULL) { ++ fprintf(stderr, "Couldn't find .dynsym or .dynstr in \"%s\"\n", ++ filename); ++ exit(EXIT_FAILURE); ++ } + + if (!relplt_addr || !lte->plt_addr) { + debug(1, "%s has no PLT relocations", filename); + lte->relplt = NULL; + lte->relplt_count = 0; +- } else if (relplt_size == 0) { ++ } else if (lte->relplt_size == 0) { + debug(1, "%s has unknown PLT size", filename); + lte->relplt = NULL; + lte->relplt_count = 0; + } else { +- if (lte->ehdr.e_machine == EM_PPC) { +- GElf_Addr glink_vma +- = get_glink_vma(lte, ppcgot, plt_data); +- +- assert (relplt_size % 12 == 0); +- size_t count = relplt_size / 12; // size of RELA entry +- lte->plt_stub_vma = glink_vma +- - (GElf_Addr)count * PPC_PLT_STUB_SIZE; +- debug(1, "stub_vma is %#" PRIx64, lte->plt_stub_vma); +- } + + for (i = 1; i < lte->ehdr.e_shnum; ++i) { + Elf_Scn *scn; + GElf_Shdr shdr; + + scn = elf_getscn(lte->elf, i); +- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get section header from \"%s\"", +- filename); ++ if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) { ++ fprintf(stderr, "Couldn't get section header" ++ " from \"%s\": %s\n", ++ filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } + if (shdr.sh_addr == relplt_addr +- && shdr.sh_size == relplt_size) { ++ && shdr.sh_size == lte->relplt_size) { + lte->relplt = elf_getdata(scn, NULL); + lte->relplt_count = + shdr.sh_size / shdr.sh_entsize; + if (lte->relplt == NULL +- || elf_getdata(scn, lte->relplt) != NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get .rel*.plt data from \"%s\"", +- filename); ++ || elf_getdata(scn, lte->relplt) != NULL) { ++ fprintf(stderr, "Couldn't get .rel*.plt" ++ " data from \"%s\": %s\n", ++ filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } + break; + } + } + +- if (i == lte->ehdr.e_shnum) +- error(EXIT_FAILURE, 0, +- "Couldn't find .rel*.plt section in \"%s\"", +- filename); ++ if (i == lte->ehdr.e_shnum) { ++ fprintf(stderr, ++ "Couldn't find .rel*.plt section in \"%s\"\n", ++ filename); ++ exit(EXIT_FAILURE); ++ } + + 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); ++ arch_elf_destroy(lte); + elf_end(lte->elf); + close(lte->fd); + } + +-static struct library_symbol * +-create_library_symbol(const char * name, GElf_Addr addr) +-{ +- size_t namel = strlen(name) + 1; +- struct library_symbol * sym = calloc(sizeof(*sym) + namel, 1); +- if (sym == NULL) { +- perror("create_library_symbol"); +- return NULL; +- } +- sym->name = (char *)(sym + 1); +- memcpy(sym->name, name, namel); +- sym->enter_addr = (void *)(uintptr_t) addr; +- return sym; +-} +- +-void +-add_library_symbol(GElf_Addr addr, const char *name, +- struct library_symbol **library_symbolspp, +- enum toplt type_of_plt, int is_weak) ++static int ++populate_plt(struct Process *proc, const char *filename, ++ struct ltelf *lte, struct library *lib) + { +- struct library_symbol *s; +- +- debug(DEBUG_FUNCTION, "add_library_symbol()"); +- +- s = create_library_symbol(name, addr); +- if (s == NULL) +- error(EXIT_FAILURE, errno, "add_library_symbol failed"); ++ size_t i; ++ for (i = 0; i < lte->relplt_count; ++i) { ++ GElf_Rel rel; ++ GElf_Rela rela; ++ GElf_Sym sym; ++ void *ret; + +- s->needs_init = 1; +- s->is_weak = is_weak; +- s->plt_type = type_of_plt; ++ 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); ++ } + +- s->next = *library_symbolspp; +- *library_symbolspp = s; ++ if (ret == NULL ++ || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count ++ || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info), ++ &sym) == NULL) { ++ fprintf(stderr, ++ "Couldn't get relocation from \"%s\": %s\n", ++ filename, elf_errmsg(-1)); ++ exit(EXIT_FAILURE); ++ } + +- debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name); +-} ++ char const *name = lte->dynstr + sym.st_name; + +-struct library_symbol * +-clone_library_symbol(struct library_symbol * sym) +-{ +- struct library_symbol * copy +- = create_library_symbol(sym->name, +- (GElf_Addr)(uintptr_t)sym->enter_addr); +- if (copy == NULL) +- return NULL; +- +- copy->needs_init = sym->needs_init; +- copy->is_weak = sym->is_weak; +- copy->plt_type = sym->plt_type; ++ if (!filter_matches_symbol(options.plt_filter, name, lib)) ++ continue; + +- return copy; ++ struct library_symbol *libsym = NULL; ++ switch (arch_elf_add_plt_entry(proc, lte, name, ++ &rela, i, &libsym)) { ++ case plt_default: ++ if (default_elf_add_plt_entry(proc, lte, name, ++ &rela, i, &libsym) < 0) ++ /* fall-through */ ++ case plt_fail: ++ return -1; ++ /* fall-through */ ++ case plt_ok: ++ if (libsym != NULL) ++ library_add_symbol(lib, libsym); ++ } ++ } ++ return 0; + } + +-void +-destroy_library_symbol(struct library_symbol * sym) +-{ +- free(sym); +-} ++/* When -x rules result in request to trace several aliases, we only ++ * want to add such symbol once. The only way that those symbols ++ * differ in is their name, e.g. in glibc you have __GI___libc_free, ++ * __cfree, __free, __libc_free, cfree and free all defined on the ++ * same address. So instead we keep this unique symbol struct for ++ * each address, and replace name in libsym with a shorter variant if ++ * we find it. */ ++struct unique_symbol { ++ target_address_t addr; ++ struct library_symbol *libsym; ++}; + +-void +-destroy_library_symbol_chain(struct library_symbol * sym) ++static int ++unique_symbol_cmp(const void *key, const void *val) + { +- 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; ++ const struct unique_symbol *sym_key = key; ++ const struct unique_symbol *sym_val = val; ++ return sym_key->addr != sym_val->addr; + } + + static int +-symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym, +- size_t symidx, const char *name) ++populate_this_symtab(struct Process *proc, const char *filename, ++ struct ltelf *lte, struct library *lib, ++ Elf_Data *symtab, const char *strtab, size_t size) + { +- 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); ++ /* Using sorted array would be arguably better, but this ++ * should be well enough for the number of symbols that we ++ * typically deal with. */ ++ size_t num_symbols = 0; ++ struct unique_symbol *symbols = malloc(sizeof(*symbols) * size); ++ if (symbols == NULL) { ++ fprintf(stderr, "couldn't insert symbols for -x: %s\n", ++ strerror(errno)); ++ return -1; + } +- 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) { ++ GElf_Word secflags[lte->ehdr.e_shnum]; + size_t i; +- unsigned long hash; +- unsigned long gnu_hash; +- +- if (!count) +- return 1; +- +-#ifdef ELF_HASH_TAKES_SIGNED_CHAR +- 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) ++ for (i = 1; i < lte->ehdr.e_shnum; ++i) { ++ Elf_Scn *scn = elf_getscn(lte->elf, i); ++ if (scn == 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; +- } ++ GElf_Shdr shdr; ++ if (gelf_getshdr(scn, &shdr) == NULL) ++ continue; ++ secflags[i] = shdr.sh_flags; + } +- return 0; +-} +- +-static GElf_Addr +-opd2addr(struct ltelf *lte, GElf_Addr addr) { +-#ifdef ARCH_SUPPORTS_OPD +- unsigned long base, offset; +- +- if (!lte->opd) +- return addr; +- +- base = (unsigned long)lte->opd->d_buf; +- offset = (unsigned long)addr - (unsigned long)lte->opd_addr; +- if (offset > lte->opd_size) +- error(EXIT_FAILURE, 0, "static plt not in .opd"); +- +- return *(GElf_Addr*)(base + offset); +-#else //!ARCH_SUPPORTS_OPD +- return addr; +-#endif +-} + +-struct library_symbol * +-read_elf(Process *proc, GElf_Addr *entryp) +-{ +- 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; ++ size_t lib_len = strlen(lib->soname); ++ for (i = 0; i < size; ++i) { ++ GElf_Sym sym; ++ if (gelf_getsym(symtab, i, &sym) == NULL) { ++ fail: ++ fprintf(stderr, ++ "couldn't get symbol #%zd from %s: %s\n", ++ i, filename, elf_errmsg(-1)); ++ continue; ++ } + +- debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename); ++ /* XXX support IFUNC as well. */ ++ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC ++ || sym.st_value == 0) ++ continue; + +- memset(lte, 0, sizeof(*lte)); +- library_symbols = NULL; +- library_num = 0; +- proc->libdl_hooked = 0; ++ const char *orig_name = strtab + sym.st_name; ++ const char *version = strchr(orig_name, '@'); ++ size_t len = version != NULL ? (assert(version > orig_name), ++ (size_t)(version - orig_name)) ++ : strlen(orig_name); ++ char name[len + 1]; ++ memcpy(name, orig_name, len); ++ name[len] = 0; + +- if (do_init_elf(lte, proc->filename)) +- return NULL; ++ if (!filter_matches_symbol(options.static_filter, name, lib)) ++ continue; + +- memcpy(&main_lte, lte, sizeof(struct ltelf)); ++ target_address_t addr = (target_address_t) ++ (uintptr_t)(sym.st_value + lte->bias); ++ target_address_t naddr; ++ ++ /* On arches that support OPD, the value of typical ++ * function symbol will be a pointer to .opd, but some ++ * will point directly to .text. We don't want to ++ * translate those. */ ++ if (secflags[sym.st_shndx] & SHF_EXECINSTR) { ++ naddr = addr; ++ } else if (arch_translate_address(lte, addr, &naddr) < 0) { ++ fprintf(stderr, ++ "couldn't translate address of %s@%s: %s\n", ++ name, lib->soname, strerror(errno)); ++ continue; ++ } + +- if (opt_p && opt_p->pid > 0) { +- linkmap_init(proc, lte); +- proc->libdl_hooked = 1; +- } ++ char *full_name; ++ if (lib->type != LT_LIBTYPE_MAIN) { ++ full_name = malloc(strlen(name) + 1 + lib_len + 1); ++ if (full_name == NULL) ++ goto fail; ++ sprintf(full_name, "%s@%s", name, lib->soname); ++ } else { ++ full_name = strdup(name); ++ if (full_name == NULL) ++ goto fail; ++ } + +- proc->e_machine = lte->ehdr.e_machine; ++ /* Look whether we already have a symbol for this ++ * address. If not, add this one. */ ++ struct unique_symbol key = { naddr, NULL }; ++ struct unique_symbol *unique ++ = lsearch(&key, symbols, &num_symbols, ++ sizeof(*symbols), &unique_symbol_cmp); ++ ++ if (unique->libsym == NULL) { ++ struct library_symbol *libsym = malloc(sizeof(*libsym)); ++ if (libsym == NULL ++ || library_symbol_init(libsym, naddr, full_name, ++ 1, LS_TOPLT_NONE) < 0) { ++ --num_symbols; ++ goto fail; ++ } ++ unique->libsym = libsym; ++ unique->addr = naddr; + +- 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]); +- } ++ } else if (strlen(full_name) < strlen(unique->libsym->name)) { ++ library_symbol_set_name(unique->libsym, full_name, 1); + +- 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 { ++ free(full_name); + } +-#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; +- +- 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 < num_symbols; ++i) { ++ assert(symbols[i].libsym != NULL); ++ library_add_symbol(lib, symbols[i].libsym); + } + +- for (i = 0; i < lte->symtab_count; ++i) { +- GElf_Sym sym; +- GElf_Addr addr; +- const char *name; ++ free(symbols); + +- if (gelf_getsym(lte->symtab, i, &sym) == NULL) +- error(EXIT_FAILURE, 0, +- "Couldn't get symbol from \"%s\"", +- proc->filename); ++ return 0; ++} + +- name = lte->strtab + sym.st_name; +- addr = sym.st_value; +- if (!addr) +- continue; ++static int ++populate_symtab(struct Process *proc, const char *filename, ++ struct ltelf *lte, struct library *lib) ++{ ++ if (lte->symtab != NULL && lte->strtab != NULL) ++ return populate_this_symtab(proc, filename, lte, lib, ++ lte->symtab, lte->strtab, ++ lte->symtab_count); ++ else ++ return populate_this_symtab(proc, filename, lte, lib, ++ lte->dynsym, lte->dynstr, ++ lte->dynsym_count); ++} + +- 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; +- } ++int ++ltelf_read_library(struct library *lib, struct Process *proc, ++ const char *filename, GElf_Addr bias) ++{ ++ struct ltelf lte = {}; ++ if (do_init_elf(<e, filename, bias) < 0) ++ return -1; ++ if (arch_elf_init(<e, lib) < 0) { ++ fprintf(stderr, "Backend initialization failed.\n"); ++ return -1; + } + +- unsigned found_count = 0; ++ proc->e_machine = lte.ehdr.e_machine; + +- for (xptr = opt_x_loc; xptr; xptr = xptr->next) { +- if (xptr->found) +- continue; ++ int status = 0; ++ if (lib == NULL) ++ goto fail; + +- 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; +- } ++ /* Note that we set soname and pathname as soon as they are ++ * allocated, so in case of further errors, this get released ++ * when LIB is release, which should happen in the caller when ++ * we return error. */ ++ ++ if (lib->pathname == NULL) { ++ char *pathname = strdup(filename); ++ if (pathname == NULL) ++ goto fail; ++ library_set_pathname(lib, pathname, 1); + } + +- if (lte->ehdr.e_entry != 0) { +- *entryp = opd2addr(lte, lte->ehdr.e_entry); ++ if (lte.soname != NULL) { ++ char *soname = strdup(lte.soname); ++ if (soname == NULL) ++ goto fail; ++ library_set_soname(lib, soname, 1); + } else { ++ const char *soname = rindex(lib->pathname, '/') + 1; ++ if (soname == NULL) ++ soname = lib->pathname; ++ library_set_soname(lib, soname, 0); + } + +- 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); +- } +- if (exit_out) { +- exit (1); +- } ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ target_address_t entry = (target_address_t)(uintptr_t)lte.entry_addr; ++ if (arch_translate_address(<e, entry, &entry) < 0) ++ goto fail; ++ ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ lib->base = (target_address_t)(uintptr_t)lte.base_addr; ++ lib->entry = entry; ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ lib->dyn_addr = (target_address_t)(uintptr_t)lte.dyn_addr; ++ ++ if (filter_matches_library(options.plt_filter, lib) ++ && populate_plt(proc, filename, <e, lib) < 0) ++ goto fail; ++ ++ if (filter_matches_library(options.static_filter, lib) ++ && populate_symtab(proc, filename, <e, lib) < 0) ++ goto fail; ++ ++done: ++ do_close_elf(<e); ++ return status; ++ ++fail: ++ status = -1; ++ goto done; ++} + +- for (i = 0; i < library_num + 1; ++i) +- do_close_elf(<e[i]); ++struct library * ++ltelf_read_main_binary(struct Process *proc, const char *path) ++{ ++ struct library *lib = malloc(sizeof(*lib)); ++ if (lib == NULL) ++ return NULL; ++ library_init(lib, LT_LIBTYPE_MAIN); ++ library_set_pathname(lib, path, 0); ++ ++ /* There is a race between running the process and reading its ++ * binary for internal consumption. So open the binary from ++ * the /proc filesystem. XXX Note that there is similar race ++ * for libraries, but there we don't have a nice answer like ++ * that. Presumably we could read the DSOs from the process ++ * memory image, but that's not currently done. */ ++ char *fname = pid2name(proc->pid); ++ if (ltelf_read_library(lib, proc, fname, 0) < 0) { ++ library_destroy(lib); ++ free(lib); ++ return NULL; ++ } + +- return library_symbols; ++ return lib; + } +diff --git a/ltrace-elf.h b/ltrace-elf.h +index 4da8a0a..64d1cb8 100644 +--- a/ltrace-elf.h ++++ b/ltrace-elf.h +@@ -3,7 +3,18 @@ + + #include + #include ++#include "sysdep.h" + ++struct Process; ++struct library; ++struct library_symbol; ++ ++/* 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; +@@ -12,46 +23,59 @@ struct ltelf { + size_t dynsym_count; + const char *dynstr; + GElf_Addr plt_addr; ++ GElf_Word plt_flags; + size_t plt_size; + Elf_Data *relplt; ++ Elf_Data *plt_data; + 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; ++ size_t relplt_size; ++ GElf_Addr bias; ++ GElf_Addr entry_addr; + GElf_Addr base_addr; +-#ifdef __mips__ +- size_t pltgot_addr; +- size_t mips_local_gotno; +- size_t mips_gotsym; +-#endif // __mips__ +- GElf_Addr plt_stub_vma; ++ struct arch_ltelf_data arch; + }; + +-#define ELF_MAX_SEGMENTS 50 +-#define LTE_HASH_MALLOCED 1 +-#define LTE_PLT_EXECUTABLE 2 ++int open_elf(struct ltelf *lte, const char *filename); + +-#define PLTS_ARE_EXECUTABLE(lte) ((lte->lte_flags & LTE_PLT_EXECUTABLE) != 0) ++/* 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. */ ++int ltelf_read_library(struct library *lib, struct Process *proc, ++ const char *filename, GElf_Addr bias); + +-extern size_t library_num; +-extern char *library[MAX_LIBRARIES]; ++/* 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); + +-extern int open_elf(struct ltelf *lte, const char *filename); +-extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp); ++GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); + +-extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); ++Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr); ++int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr, ++ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); ++int elf_get_section_type(struct ltelf *lte, GElf_Word type, ++ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); ++int elf_get_section_named(struct ltelf *lte, const char *name, ++ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); + +-#ifndef SHT_GNU_HASH +-#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +-#endif ++/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET, ++ * and store it in *RETP. Returns 0 on success or a negative value if ++ * there's not enough data. */ ++int elf_read_u16(Elf_Data *data, GElf_Xword offset, uint16_t *retp); ++int elf_read_u32(Elf_Data *data, GElf_Xword offset, uint32_t *retp); ++int elf_read_u64(Elf_Data *data, GElf_Xword offset, uint64_t *retp); ++ ++int default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++ const char *a_name, GElf_Rela *rela, size_t ndx, ++ struct library_symbol **ret); + + #if __WORDSIZE == 32 + #define PRI_ELF_ADDR PRIx32 +diff --git a/ltrace.1 b/ltrace.1 +index 4d320e6..3a908be 100644 +--- a/ltrace.1 ++++ b/ltrace.1 +@@ -1,3 +1,4 @@ ++.\" Copyright (c) 2012 Petr Machata, Red Hat Inc. + .\" Copyright (c) 1997-2005 Juan Cespedes + .\" This file is covered by the GNU GPL + .TH ltrace 1 +@@ -64,22 +65,12 @@ carries upon a traced process + DEBUG_FUNCTION. Shows every entry to internal functions + .RE + .TP +-.I \-e expr +-A qualifying expression which modifies which events to trace. +-The format of the expression is: +-.br +-[!]value1[,value2]... +-.br +-where the values are the functions to trace. Using an exclamation +-mark negates the set of values. For example +-.I \-e printf +-means to trace only the printf library call. By contrast, +-.I \-e !printf +-means to trace every library call except printf. +-.IP +-Note that some shells use the exclamation point for history +-expansion; even inside quoted arguments. If so, you must escape +-the exclamation point with a backslash. ++.I \-e filter ++A qualifying expression which modifies which library calls to trace. ++The format of the filter expression is described in the section ++\fBFILTER EXPRESSIONS\fR. If more than one \-e option appears on the ++command line, the library calls that match any of them are traced. If ++no \-e is given, \fB@MAIN\fR is assumed as a default. + .TP + .I \-f + Trace child processes as they are created by +@@ -175,16 +166,72 @@ is set at + which must be an external function. By default, '_start' is used. + NOTE: this flag is only available on the architectures that need it. + .TP +-.I \-x extern +-Trace the external function +-.IR extern . +-This option will search the symbol table and lib-dl loaded libraries when +-attempting to match the given symbol name. +-This option may be repeated. ++.I \-x filter ++A qualifying expression which modifies which symbol table entry points ++to trace. The format of the filter expression is described in the ++section \fBFILTER EXPRESSIONS\fR. If more than one \-x option appears ++on the command line, the symbols that match any of them are traced. ++No entry points are traced if no \-x is given. + .TP + .I \-V, \-\-version + Show the version number of ltrace and exit. + ++.SH FILTER EXPRESSIONS ++ ++Filter expression is a chain of glob- or regexp-based rules that are ++used to pick symbols for tracing from libraries that the process uses. ++Most of it is intuitive, so as an example, the following would trace ++calls to malloc and free, except those done by libc: ++ ++-e malloc+free-@libc.so* ++ ++This reads: trace malloc and free, but don't trace anything that comes ++from libc. Semi-formally, the syntax of the above example looks ++approximately like this: ++ ++{[+-][\fIsymbol pattern\fR][@\fIlibrary pattern\fR]} ++ ++\fISymbol pattern\fR is used to match symbol names, \fIlibrary ++pattern\fR to match library SONAMEs. Both are implicitly globs, but ++can be regular expressions as well (see below). The glob syntax ++supports meta-characters \fB*\fR and \fB?\fR and character classes, ++similarly to what basic bash globs support. \fB^\fR and \fB$\fR are ++recognized to mean, respectively, start and end of given name. ++ ++Both \fIsymbol pattern\fR and \fIlibrary pattern\fR have to match the ++whole name. If you want to match only a part of name, surround it ++with one or two *'s as appropriate. The exception is if the pattern ++is not mentioned at all, in which case it's as if the corresponding ++pattern were \fB*\fR. (So \fBmalloc\fR is really \fBmalloc@*\fR and ++\fB@libc.*\fR is really \fB*@libc.*\fR.) ++ ++In libraries that don't have an explicit SONAME, basename is taken for ++SONAME. That holds for main binary as well: \fB/bin/echo\fR has an ++implicit SONAME of \fBecho\fR. In addition to that, special library ++pattern \fBMAIN\fR always matches symbols in the main binary and never ++a library with actual SONAME \fBMAIN\fR (use e.g. \fB^MAIN\fR or ++\fB[M]AIN\fR for that). ++ ++If the symbol or library pattern is surrounded in slashes (/like ++this/), then it is considered a regular expression instead. As a ++shorthand, instead of writing \fB/x/@/y/\fR, you can write ++\fB/x@y/\fR. ++ ++If the library pattern starts with a slash, it is not a SONAME ++expression, but a path expression, and is matched against the library ++path name. ++ ++The first rule may lack a sign, in which case \fB+\fR is assumed. If, ++on the other hand, the first rule has a \fB-\fR sign, it is as if ++there was another rule \fB@*\fR in front of it. ++ ++The above rules are used to construct the set of traced symbols. Each ++candidate symbol is passed through the chain of above rules. ++Initially, the symbol is \fIunmarked\fR. If it symbol matches a ++\fB+\fR rule, it becomes \fImarked\fR, if it matches a \fB-\fR rule, ++it becomes \fIunmarked\fR. If, after applying all rules, the symbol ++is \fImarked\fR, it will be traced. ++ + .SH BUGS + It has most of the bugs stated in + .BR strace(1) . +@@ -212,6 +259,8 @@ Personal config file, overrides + + .SH AUTHOR + Juan Cespedes ++.br ++Petr Machata + + .SH "SEE ALSO" + .BR strace(1) , +diff --git a/ltrace.h b/ltrace.h +index 194704d..fe3d6ed 100644 +--- a/ltrace.h ++++ b/ltrace.h +@@ -1,3 +1,6 @@ ++#ifndef _LTRACE_H_ ++#define _LTRACE_H_ ++ + typedef enum Event_type Event_type; + enum Event_type { + EVENT_NONE=0, +@@ -18,11 +21,10 @@ enum Event_type { + EVENT_MAX + }; + +-typedef struct Process Process; + typedef struct Event Event; + struct Event { + struct Event * next; +- Process * proc; ++ struct Process * proc; + Event_type type; + union { + int ret_val; /* EVENT_EXIT */ +@@ -38,3 +40,5 @@ typedef void (*callback_func) (Event *); + extern void ltrace_init(int argc, char **argv); + extern void ltrace_add_callback(callback_func f, Event_type type); + extern void ltrace_main(void); ++ ++#endif /* _LTRACE_H_ */ +diff --git a/options.c b/options.c +index 74c28bd..d5edc1a 100644 +--- a/options.c ++++ b/options.c +@@ -1,16 +1,19 @@ + #include "config.h" + +-#include +-#include +-#include +-#include +-#include +-#include + #include +- ++#include ++#include ++#include + #include ++#include ++#include ++#include ++#include ++#include + + #include "common.h" ++#include "filter.h" ++#include "glob.h" + + #ifndef SYSCONFDIR + #define SYSCONFDIR "/etc" +@@ -23,7 +26,6 @@ struct options_t options = { + .align = DEFAULT_ALIGN, /* alignment column for results */ + .user = NULL, /* username to run command as */ + .syscalls = 0, /* display syscalls */ +- .libcalls = 1, /* display library calls */ + #ifdef USE_DEMANGLE + .demangle = 0, /* Demangle low-level symbol names */ + #endif +@@ -36,8 +38,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 */ +@@ -47,14 +47,6 @@ int opt_T = 0; /* show the time spent inside each call */ + /* List of pids given to option -p: */ + struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ + +-/* List of function names given to option -e: */ +-struct opt_e_t *opt_e = NULL; +-int opt_e_enable = 1; +- +-/* List of global function names given to -x: */ +-struct opt_x_t *opt_x = NULL; +-unsigned int opt_x_cnt = 0; +- + /* List of filenames give to option -F: */ + struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */ + +@@ -181,11 +173,221 @@ guess_cols(void) { + } + } + ++static void ++add_filter_rule(struct filter *filt, const char *expr, ++ enum filter_rule_type type, ++ const char *a_sym, int sym_re_p, ++ const char *a_lib, int lib_re_p) ++{ ++ struct filter_rule *rule = malloc(sizeof(*rule)); ++ struct filter_lib_matcher *matcher = malloc(sizeof(*matcher)); ++ ++ if (rule == NULL || matcher == NULL) { ++ fprintf(stderr, "rule near '%s' will be ignored: %s\n", ++ expr, strerror(errno)); ++ fail: ++ free(rule); ++ free(matcher); ++ return; ++ } ++ ++ regex_t symbol_re; ++ int status; ++ { ++ /* Add ^ to the start of expression and $ to the end, so that ++ * we match the whole symbol name. Let the user write the "*" ++ * explicitly if they wish. */ ++ char sym[strlen(a_sym) + 3]; ++ sprintf(sym, "^%s$", a_sym); ++ status = (sym_re_p ? regcomp : globcomp)(&symbol_re, sym, 0); ++ if (status != 0) { ++ char buf[100]; ++ regerror(status, &symbol_re, buf, sizeof buf); ++ fprintf(stderr, "rule near '%s' will be ignored: %s\n", ++ expr, buf); ++ goto fail; ++ } ++ } ++ ++ if (strcmp(a_lib, "MAIN") == 0) { ++ filter_lib_matcher_main_init(matcher); ++ } else { ++ /* Add ^ and $ to the library expression as well. */ ++ char lib[strlen(a_lib) + 3]; ++ sprintf(lib, "^%s$", a_lib); ++ ++ enum filter_lib_matcher_type type ++ = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME; ++ ++ regex_t lib_re; ++ status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0); ++ if (status != 0) { ++ char buf[100]; ++ regerror(status, &lib_re, buf, sizeof buf); ++ fprintf(stderr, "rule near '%s' will be ignored: %s\n", ++ expr, buf); ++ ++ regfree(&symbol_re); ++ goto fail; ++ } ++ filter_lib_matcher_name_init(matcher, type, lib_re); ++ } ++ ++ filter_rule_init(rule, type, matcher, symbol_re); ++ filter_add_rule(filt, rule); ++} ++ ++static int ++parse_filter(struct filter *filt, char *expr) ++{ ++ /* Filter is a chain of sym@lib rules separated by '-'. If ++ * the filter expression starts with '-', the missing initial ++ * rule is implicitly *@*. */ ++ ++ enum filter_rule_type type = FR_ADD; ++ ++ while (*expr != 0) { ++ size_t s = strcspn(expr, "@-+"); ++ char *symname = expr; ++ char *libname; ++ char *next = expr + s + 1; ++ enum filter_rule_type this_type = type; ++ ++ if (expr[s] == 0) { ++ libname = "*"; ++ expr = next - 1; ++ ++ } else if (expr[s] == '-' || expr[s] == '+') { ++ type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD; ++ expr[s] = 0; ++ libname = "*"; ++ expr = next; ++ ++ } else { ++ assert(expr[s] == '@'); ++ expr[s] = 0; ++ s = strcspn(next, "-+"); ++ if (s == 0) { ++ libname = "*"; ++ expr = next; ++ } else if (next[s] == 0) { ++ expr = next + s; ++ libname = next; ++ } else { ++ assert(next[s] == '-' || next[s] == '+'); ++ type = next[s] == '-' ? FR_SUBTRACT : FR_ADD; ++ next[s] = 0; ++ expr = next + s + 1; ++ libname = next; ++ } ++ } ++ ++ assert(*libname != 0); ++ char *symend = symname + strlen(symname) - 1; ++ char *libend = libname + strlen(libname) - 1; ++ int sym_is_re = 0; ++ int lib_is_re = 0; ++ ++ /* ++ * /xxx/@... and ...@/xxx/ means that xxx are regular ++ * expressions. They are globs otherwise. ++ * ++ * /xxx@yyy/ is the same as /xxx/@/yyy/ ++ * ++ * @/xxx matches library path name ++ * @.xxx matches library relative path name ++ */ ++ if (symname[0] == '/') { ++ if (symname != symend && symend[0] == '/') { ++ ++symname; ++ *symend-- = 0; ++ sym_is_re = 1; ++ ++ } else { ++ sym_is_re = 1; ++ lib_is_re = 1; ++ ++symname; ++ ++ /* /XXX@YYY/ is the same as ++ * /XXX/@/YYY/. */ ++ if (libend[0] != '/') ++ fprintf(stderr, "unmatched '/'" ++ " in symbol name\n"); ++ else ++ *libend-- = 0; ++ } ++ } ++ ++ /* If libname ends in '/', then we expect '/' in the ++ * beginning too. Otherwise the initial '/' is part ++ * of absolute file name. */ ++ if (!lib_is_re && libend[0] == '/') { ++ lib_is_re = 1; ++ *libend-- = 0; ++ if (libname != libend && libname[0] == '/') ++ ++libname; ++ else ++ fprintf(stderr, "unmatched '/'" ++ " in library name\n"); ++ } ++ ++ if (*symname == 0) /* /@AA/ */ ++ symname = "*"; ++ if (*libname == 0) /* /aa@/ */ ++ libname = "*"; ++ ++ add_filter_rule(filt, expr, this_type, ++ symname, sym_is_re, ++ libname, lib_is_re); ++ } ++ ++ return 0; ++} ++ ++static struct filter * ++recursive_parse_chain(char *expr) ++{ ++ struct filter *filt = malloc(sizeof(*filt)); ++ if (filt == NULL) { ++ fprintf(stderr, "(part of) filter will be ignored: '%s': %s\n", ++ expr, strerror(errno)); ++ return NULL; ++ } ++ ++ filter_init(filt); ++ if (parse_filter(filt, expr) < 0) { ++ fprintf(stderr, "Filter '%s' will be ignored.\n", expr); ++ free(filt); ++ filt = NULL; ++ } ++ ++ return filt; ++} ++ ++static void ++parse_filter_chain(const char *expr, struct filter **retp) ++{ ++ char *str = strdup(expr); ++ if (str == NULL) { ++ fprintf(stderr, "filter '%s' will be ignored: %s\n", ++ expr, strerror(errno)); ++ return; ++ } ++ /* Support initial '!' for backward compatibility. */ ++ if (str[0] == '!') ++ str[0] = '-'; ++ ++ struct filter **tailp; ++ for (tailp = retp; *tailp != NULL; tailp = &(*tailp)->next) ++ ; ++ *tailp = recursive_parse_chain(str); ++} ++ + char ** +-process_options(int argc, char **argv) { ++process_options(int argc, char **argv) ++{ + progname = argv[0]; + options.output = stderr; +- options.no_plt = 0; + options.no_signals = 0; + #if defined(HAVE_LIBUNWIND) + options.bt_depth = -1; +@@ -193,6 +395,8 @@ process_options(int argc, char **argv) { + + guess_cols(); + ++ int libcalls = 1; ++ + while (1) { + int c; + char *p; +@@ -209,14 +413,13 @@ process_options(int argc, char **argv) { + {"library", 1, 0, 'l'}, + {"output", 1, 0, 'o'}, + {"version", 0, 0, 'V'}, +- {"no-plt", 0, 0, 'g'}, + {"no-signals", 0, 0, 'b'}, + #if defined(HAVE_LIBUNWIND) + {"where", 1, 0, 'w'}, + #endif /* defined(HAVE_LIBUNWIND) */ + {0, 0, 0, 0} + }; +- c = getopt_long(argc, argv, "+cfhiLrStTVgb" ++ c = getopt_long(argc, argv, "+cfhiLrStTVb" + # ifdef USE_DEMANGLE + "C" + # endif +@@ -258,39 +461,11 @@ process_options(int argc, char **argv) { + err_usage(); + } + break; ++ + case 'e': +- { +- char *str_e = strdup(optarg); +- if (!str_e) { +- perror("ltrace: strdup"); +- exit(1); +- } +- if (str_e[0] == '!') { +- opt_e_enable = 0; +- str_e++; +- } +- while (*str_e) { +- struct opt_e_t *tmp; +- char *str2 = strchr(str_e, ','); +- if (str2) { +- *str2 = '\0'; +- } +- tmp = malloc(sizeof(struct opt_e_t)); +- if (!tmp) { +- perror("ltrace: malloc"); +- exit(1); +- } +- tmp->name = str_e; +- tmp->next = opt_e; +- opt_e = tmp; +- if (str2) { +- str_e = str2 + 1; +- } else { +- break; +- } +- } +- break; +- } ++ parse_filter_chain(optarg, &options.plt_filter); ++ break; ++ + case 'f': + options.follow = 1; + break; +@@ -306,9 +481,6 @@ process_options(int argc, char **argv) { + opt_F = tmp; + break; + } +- case 'g': +- options.no_plt = 1; +- break; + case 'h': + usage(); + exit(0); +@@ -316,16 +488,11 @@ 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; ++ // XXX TODO ++ fprintf(stderr, "-l support not yet implemented\n"); + break; + case 'L': +- options.libcalls = 0; ++ libcalls = 0; + break; + case 'n': + options.indent = atoi(optarg); +@@ -334,7 +501,7 @@ process_options(int argc, char **argv) { + options.output = fopen(optarg, "w"); + if (!options.output) { + fprintf(stderr, +- "Can't open %s for output: %s\n", ++ "can't open %s for writing: %s\n", + optarg, strerror(errno)); + exit(1); + } +@@ -393,31 +560,8 @@ process_options(int argc, char **argv) { + /* Fall Thru */ + + case 'x': +- { +- struct opt_x_t *p = opt_x; +- +- /* First, check for duplicate. */ +- while (p && strcmp(p->name, optarg)) { +- p = p->next; +- } +- if (p) { +- break; +- } +- +- /* If not duplicate, add to list. */ +- p = malloc(sizeof(struct opt_x_t)); +- if (!p) { +- perror("ltrace: malloc"); +- exit(1); +- } +- opt_x_cnt++; +- p->name = optarg; +- p->found = 0; +- p->next = opt_x; +- p->hash = ~(0UL); +- opt_x = p; +- break; +- } ++ parse_filter_chain(optarg, &options.static_filter); ++ break; + + default: + err_usage(); +@@ -449,6 +593,14 @@ process_options(int argc, char **argv) { + opt_F = egg; + } + ++ /* Set default filter. Use @MAIN for now, as that's what ++ * ltrace used to have in the past. XXX Maybe we should make ++ * this "*" instead. */ ++ if (options.plt_filter == NULL && libcalls) { ++ parse_filter_chain("@MAIN", &options.plt_filter); ++ options.hide_caller = 1; ++ } ++ + if (!opt_p && argc < 1) { + fprintf(stderr, "%s: too few arguments\n", progname); + err_usage(); +diff --git a/options.h b/options.h +index 9a00629..3ffee71 100644 +--- a/options.h ++++ b/options.h +@@ -1,11 +1,12 @@ + #include + #include + ++struct filter; ++ + struct options_t { + int align; /* -a: default alignment column for results */ + char * user; /* -u: username to run command as */ + int syscalls; /* -S: display system calls */ +- int libcalls; /* -L: display library calls */ + int demangle; /* -C: demangle low-level names into user-level names */ + int indent; /* -n: indent trace output according to program flow */ + FILE *output; /* output to a specific file */ +@@ -14,11 +15,13 @@ struct options_t { + size_t arraylen; /* default maximum # of array elements printed */ + size_t strlen; /* default maximum # of bytes printed in strings */ + int follow; /* trace child processes */ +- int no_plt; /* set bps on PLT entries */ + int no_signals; /* don't print signals */ + #if defined(HAVE_LIBUNWIND) + int bt_depth; /* how may levels of stack frames to show */ + #endif /* defined(HAVE_LIBUNWIND) */ ++ struct filter *plt_filter; ++ struct filter *static_filter; ++ int hide_caller; /* Whether caller library should be hidden. */ + }; + extern struct options_t options; + +@@ -32,31 +35,13 @@ struct opt_p_t { + struct opt_p_t *next; + }; + +-struct opt_e_t { +- char *name; +- struct opt_e_t *next; +-}; +- + struct opt_F_t { + char *filename; + struct opt_F_t *next; + }; + +-struct opt_x_t { +- char *name; +- int found; +- unsigned long hash; +- struct opt_x_t *next; +-}; +- + extern struct opt_p_t *opt_p; /* attach to process with a given pid */ + +-extern struct opt_e_t *opt_e; /* list of function names to display */ +-extern int opt_e_enable; /* 0 if '!' is used, 1 otherwise */ +- + extern struct opt_F_t *opt_F; /* alternate configuration file(s) */ + +-extern struct opt_x_t *opt_x; /* list of functions to break at */ +-extern unsigned int opt_x_cnt; +- + extern char **process_options(int argc, char **argv); +diff --git a/output.c b/output.c +index 1e2e709..ac8c9d0 100644 +--- a/output.c ++++ b/output.c +@@ -9,6 +9,8 @@ + #include + + #include "common.h" ++#include "proc.h" ++#include "library.h" + + /* TODO FIXME XXX: include in common.h: */ + extern struct timeval current_time_spent; +@@ -153,7 +155,10 @@ tabto(int col) { + } + + void +-output_left(enum tof type, Process *proc, char const *function_name) { ++output_left(enum tof type, struct Process *proc, ++ struct library_symbol *libsym) ++{ ++ const char *function_name = libsym->name; + Function *func; + static arg_type_info *arg_unknown = NULL; + if (arg_unknown == NULL) +@@ -169,10 +174,15 @@ output_left(enum tof type, Process *proc, char const *function_name) { + current_proc = proc; + current_depth = proc->callstack_depth; + begin_of_line(type, proc); ++ if (!options.hide_caller && libsym->lib != NULL ++ && libsym->plt_type != LS_TOPLT_NONE) ++ current_column += fprintf(options.output, "%s->", ++ libsym->lib->soname); + #ifdef USE_DEMANGLE + current_column += +- fprintf(options.output, "%s(", +- options.demangle ? my_demangle(function_name) : function_name); ++ fprintf(options.output, "%s(", ++ (options.demangle ++ ? my_demangle(function_name) : function_name)); + #else + current_column += fprintf(options.output, "%s(", function_name); + #endif +@@ -210,7 +220,9 @@ output_left(enum tof type, Process *proc, char const *function_name) { + } + + void +-output_right(enum tof type, Process *proc, char *function_name) { ++output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) ++{ ++ const char *function_name = libsym->name; + Function *func = name2func(function_name); + static arg_type_info *arg_unknown = NULL; + if (arg_unknown == NULL) +diff --git a/output.h b/output.h +index fa840c7..714078f 100644 +--- a/output.h ++++ b/output.h +@@ -1,3 +1,7 @@ +-void output_line(Process *proc, char *fmt, ...); +-void output_left(enum tof type, Process *proc, char const *function_name); +-void output_right(enum tof type, Process *proc, char *function_name); ++struct Process; ++struct library_symbol; ++void output_line(struct Process *proc, char *fmt, ...); ++void output_left(enum tof type, struct Process *proc, ++ struct library_symbol *libsym); ++void output_right(enum tof type, struct Process *proc, ++ struct library_symbol *libsym); +diff --git a/proc.c b/proc.c +index 47086b4..51833fe 100644 +--- a/proc.c ++++ b/proc.c +@@ -11,45 +11,296 @@ + #include + #include + #include +-#include + + #include "common.h" + #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); ++#ifndef ARCH_HAVE_PROCESS_DATA ++int ++arch_process_init(struct Process *proc) ++{ ++ return 0; ++} ++ ++void ++arch_process_destroy(struct Process *proc) ++{ ++} ++ ++int ++arch_process_clone(struct Process *retp, struct Process *proc) ++{ ++ return 0; ++} ++ ++int ++arch_process_exec(struct Process *proc) ++{ ++ return 0; ++} ++#endif ++ ++#ifndef ARCH_HAVE_DYNLINK_DONE ++void ++arch_dynlink_done(struct Process *proc) ++{ ++} ++#endif ++ ++static void add_process(struct Process *proc, int was_exec); ++ ++static int ++process_bare_init(struct Process *proc, const char *filename, ++ pid_t pid, int was_exec) ++{ ++ if (!was_exec) { ++ 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; ++ } + } + +- proc->filename = strdup(filename); ++ /* Add process so that we know who the leader is. */ + proc->pid = pid; ++ add_process(proc, was_exec); ++ if (proc->leader == NULL) ++ goto fail; ++ ++ if (proc->leader == proc) { ++ proc->breakpoints = dict_init(target_address_hash, ++ target_address_cmp); ++ 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, int was_exec) ++{ ++ dict_clear(proc->breakpoints); ++ if (!was_exec) { ++ free(proc->filename); ++ remove_process(proc); ++ } ++} ++ ++static int ++process_init_main(struct Process *proc) ++{ ++ target_address_t entry; ++ target_address_t interp_bias; ++ if (process_get_entry(proc, &entry, &interp_bias) < 0) { ++ fprintf(stderr, "Couldn't get entry points of process %d\n", ++ proc->pid); ++ return -1; ++ } ++ ++ if (breakpoints_init(proc) < 0) { ++ fprintf(stderr, "failed to init breakpoints %d\n", ++ proc->pid); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int ++process_init(struct Process *proc, const char *filename, pid_t pid) ++{ ++ if (process_bare_init(proc, filename, pid, 0) < 0) { ++ fail: ++ fprintf(stderr, "failed to initialize process %d: %s\n", ++ pid, strerror(errno)); ++ return -1; ++ } ++ ++ if (arch_process_init(proc) < 0) { ++ process_bare_destroy(proc, 0); ++ goto fail; ++ } ++ ++ if (proc->leader != proc) ++ return 0; ++ if (process_init_main(proc) < 0) { ++ process_bare_destroy(proc, 0); ++ goto fail; ++ } ++ return 0; ++} ++ ++static enum callback_status ++destroy_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) ++{ ++ breakpoint_destroy(bp); ++ free(bp); ++ return CBS_CONT; ++} ++ ++static void ++private_process_destroy(struct Process *proc, int keep_filename) ++{ ++ if (!keep_filename) ++ free(proc->filename); ++ ++ /* Libraries and symbols. This is only relevant in ++ * leader. */ ++ struct library *lib; ++ for (lib = proc->libraries; lib != NULL; ) { ++ struct library *next = lib->next; ++ library_destroy(lib); ++ free(lib); ++ lib = next; ++ } ++ proc->libraries = NULL; ++ ++ /* Breakpoints. */ ++ if (proc->breakpoints != NULL) { ++ proc_each_breakpoint(proc, NULL, destroy_breakpoint_cb, NULL); ++ dict_clear(proc->breakpoints); ++ proc->breakpoints = NULL; ++ } ++} ++ ++void ++process_destroy(struct Process *proc) ++{ ++ private_process_destroy(proc, 0); ++ arch_process_destroy(proc); ++} ++ ++int ++process_exec(struct Process *proc) ++{ ++ /* Call exec first, before we destroy the main state. */ ++ if (arch_process_exec(proc) < 0) ++ return -1; ++ ++ private_process_destroy(proc, 1); ++ if (process_bare_init(proc, NULL, proc->pid, 1) < 0) ++ return -1; ++ if (process_init_main(proc) < 0) { ++ process_bare_destroy(proc, 1); ++ return -1; ++ } ++ return 0; ++} ++ ++struct Process * ++open_program(const char *filename, pid_t pid) ++{ ++ assert(pid != 0); ++ struct Process *proc = malloc(sizeof(*proc)); ++ if (proc == NULL || process_init(proc, filename, pid) < 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; ++}; ++ ++static void ++clone_single_bp(void *key, void *value, void *u) ++{ ++ struct breakpoint *bp = value; ++ struct clone_single_bp_data *data = u; ++ ++ data->error = 0; ++ struct breakpoint *clone = malloc(sizeof(*clone)); ++ if (clone == NULL ++ || breakpoint_clone(clone, data->new_proc, ++ bp, data->old_proc) < 0) { ++ fail: ++ free(clone); ++ data->error = -1; ++ } ++ if (proc_add_breakpoint(data->new_proc->leader, clone) < 0) { ++ breakpoint_destroy(clone); ++ goto fail; ++ } ++} ++ ++int ++process_clone(struct Process *retp, struct Process *proc, pid_t pid) ++{ ++ if (process_bare_init(retp, proc->filename, pid, 0) < 0) { ++ fail: ++ fprintf(stderr, "failed to clone process %d->%d : %s\n", ++ proc->pid, pid, strerror(errno)); ++ return -1; ++ } ++ ++ retp->tracesysgood = proc->tracesysgood; ++ retp->e_machine = proc->e_machine; ++ ++ /* For non-leader processes, that's all we need to do. */ ++ if (retp->leader != retp) ++ 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, 0); ++ ++ /* 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); ++ ++ /* And finally the call stack. */ ++ memcpy(retp->callstack, proc->callstack, sizeof(retp->callstack)); ++ retp->callstack_depth = proc->callstack_depth; ++ ++ if (data.error < 0) ++ goto fail2; ++ ++ if (arch_process_clone(retp, proc) < 0) ++ goto fail2; ++ ++ return 0; + } + + static int +@@ -67,19 +318,19 @@ open_one_pid(pid_t pid) + return -1; + } + +- proc = open_program(filename, pid, 0); ++ proc = open_program(filename, pid); + if (proc == NULL) + return -1; +- trace_set_options(proc, pid); ++ trace_set_options(proc); + + 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 +@@ -134,20 +385,21 @@ open_pid(pid_t pid) + old_ntasks = ntasks; + } + +- /* Done. Now initialize breakpoints and then continue +- * everyone. */ +- Process * leader; +- leader = pid2proc(pid)->leader; +- enable_all_breakpoints(leader); ++ struct Process *leader = pid2proc(pid)->leader; + +- each_task(pid2proc(pid)->leader, start_one_pid, NULL); ++ /* XXX Is there a way to figure out whether _start has ++ * actually already been hit? */ ++ arch_dynlink_done(leader); ++ ++ /* Done. Continue everyone. */ ++ each_task(leader, NULL, 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 * +@@ -179,41 +431,60 @@ 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 *start_after, ++ enum callback_status(*cb)(struct Process *proc, void *data), ++ void *data) + { +- Process * it = proc ?: list_of_processes; +- for (; it != NULL; ) { ++ struct Process *it = start_after == NULL ? list_of_processes ++ : start_after->next; ++ ++ while (it != NULL) { + /* Callback might call remove_process. */ +- Process * next = it->next; +- if ((*cb) (it, data) == pcb_stop) ++ struct Process *next = it->next; ++ switch ((*cb)(it, data)) { ++ case CBS_FAIL: ++ /* XXX handle me */ ++ 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 *proc, struct Process *start_after, ++ enum callback_status(*cb)(struct Process *proc, void *data), ++ void *data) + { ++ assert(proc != NULL); ++ struct Process *it = start_after == NULL ? proc->leader ++ : start_after->next; ++ + if (it != NULL) { +- Process * leader = it->leader; +- for (; it != NULL && it->leader == leader; ) { ++ struct Process *leader = it->leader; ++ while (it != NULL && it->leader == leader) { + /* Callback might call remove_process. */ +- Process * next = it->next; +- if ((*cb) (it, data) == pcb_stop) ++ struct Process *next = it->next; ++ switch ((*cb)(it, data)) { ++ case CBS_FAIL: ++ /* XXX handle me */ ++ case CBS_STOP: + return it; ++ case CBS_CONT: ++ break; ++ } + it = next; + } + } + return NULL; + } + +-void +-add_process(Process * proc) ++static void ++add_process(struct Process *proc, int was_exec) + { + Process ** leaderp = &list_of_processes; + if (proc->pid) { +@@ -231,8 +502,11 @@ add_process(Process * proc) + leaderp = &leader->next; + } + } +- proc->next = *leaderp; +- *leaderp = proc; ++ ++ if (!was_exec) { ++ proc->next = *leaderp; ++ *leaderp = proc; ++ } + } + + void +@@ -252,13 +526,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 +@@ -284,15 +558,16 @@ remove_process(Process *proc) + debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); + + if (proc->leader == proc) +- each_task(proc, &clear_leader, NULL); ++ each_task(proc, NULL, &clear_leader, NULL); + + unlist_process(proc); + delete_events_for(proc); ++ process_destroy(proc); + free(proc); + } + + void +-install_event_handler(Process * proc, Event_Handler * handler) ++install_event_handler(Process *proc, struct event_handler *handler) + { + debug(DEBUG_FUNCTION, "install_event_handler(pid=%d, %p)", proc->pid, handler); + assert(proc->event_handler == NULL); +@@ -302,7 +577,7 @@ install_event_handler(Process * proc, Event_Handler * handler) + void + destroy_event_handler(Process * proc) + { +- Event_Handler * handler = proc->event_handler; ++ struct event_handler *handler = proc->event_handler; + debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); + assert(handler != NULL); + if (handler->destroy != NULL) +@@ -310,3 +585,197 @@ 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; ++ assert(proc->leader == proc); ++ ++ /* If there is an artificial breakpoint on the same address, ++ * its libsym will be NULL, and we can smuggle our libsym ++ * there. That artificial breakpoint is there presumably for ++ * the callbacks, which we don't touch. If there is a real ++ * breakpoint, then this is a bug. ltrace-elf.c should filter ++ * symbols and ignore extra symbol aliases. ++ * ++ * The other direction is more complicated and currently not ++ * supported. If a breakpoint has custom callbacks, it might ++ * be also custom-allocated, and we would really need to swap ++ * the two: delete the one now in the dictionary, swap values ++ * around, and put the new breakpoint back in. */ ++ struct breakpoint *bp = dict_find_entry(proc->breakpoints, ++ libsym->enter_addr); ++ if (bp != NULL) { ++ assert(bp->libsym == NULL); ++ bp->libsym = libsym; ++ return CBS_CONT; ++ } ++ ++ bp = malloc(sizeof(*bp)); ++ if (bp == NULL ++ || breakpoint_init(bp, proc, libsym->enter_addr, libsym) < 0) { ++ fail: ++ free(bp); ++ return CBS_FAIL; ++ } ++ if (proc_add_breakpoint(proc, bp) < 0) { ++ breakpoint_destroy(bp); ++ goto fail; ++ } ++ ++ if (breakpoint_turn_on(bp, proc) < 0) { ++ proc_remove_breakpoint(proc, bp); ++ breakpoint_destroy(bp); ++ goto fail; ++ } ++ ++ return CBS_CONT; ++} ++ ++void ++proc_add_library(struct Process *proc, struct library *lib) ++{ ++ assert(lib->next == NULL); ++ lib->next = proc->libraries; ++ proc->libraries = lib; ++ debug(DEBUG_PROCESS, "added library %s@%p (%s) to %d", ++ lib->soname, lib->base, lib->pathname, proc->pid); ++ ++ struct library_symbol *libsym = NULL; ++ while ((libsym = library_each_symbol(lib, libsym, breakpoint_for_symbol, ++ proc)) != NULL) ++ fprintf(stderr, "couldn't insert breakpoint for %s to %d: %s", ++ libsym->name, proc->pid, strerror(errno)); ++} ++ ++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_FAIL: ++ /* XXX handle me */ ++ case CBS_STOP: ++ return it; ++ case CBS_CONT: ++ break; ++ } ++ ++ it = next; ++ } ++ ++ return NULL; ++} ++ ++static void ++check_leader(struct Process *proc) ++{ ++ /* Only the group leader should be getting the breakpoints and ++ * thus have ->breakpoint initialized. */ ++ assert(proc->leader != NULL); ++ assert(proc->leader == proc); ++ assert(proc->breakpoints != NULL); ++} ++ ++int ++proc_add_breakpoint(struct Process *proc, struct breakpoint *bp) ++{ ++ debug(DEBUG_FUNCTION, "proc_add_breakpoint(pid=%d, %s@%p)", ++ proc->pid, breakpoint_name(bp), bp->addr); ++ check_leader(proc); ++ ++ /* XXX We might merge bp->libsym instead of the following ++ * assert, but that's not necessary right now. Read the ++ * comment in breakpoint_for_symbol. */ ++ assert(dict_find_entry(proc->breakpoints, bp->addr) == NULL); ++ ++ if (dict_enter(proc->breakpoints, bp->addr, bp) < 0) { ++ fprintf(stderr, ++ "couldn't enter breakpoint %s@%p to dictionary: %s\n", ++ breakpoint_name(bp), bp->addr, strerror(errno)); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++void ++proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp) ++{ ++ debug(DEBUG_FUNCTION, "proc_remove_breakpoint(pid=%d, %s@%p)", ++ proc->pid, breakpoint_name(bp), bp->addr); ++ check_leader(proc); ++ struct breakpoint *removed = dict_remove(proc->breakpoints, bp->addr); ++ assert(removed == bp); ++} ++ ++/* Dict doesn't support iteration restarts, so here's this contraption ++ * for now. XXX add restarts to dict. */ ++struct each_breakpoint_data ++{ ++ void *start; ++ void *end; ++ struct Process *proc; ++ enum callback_status (*cb)(struct Process *proc, ++ struct breakpoint *bp, ++ void *data); ++ void *cb_data; ++}; ++ ++static void ++each_breakpoint_cb(void *key, void *value, void *d) ++{ ++ struct each_breakpoint_data *data = d; ++ if (data->end != NULL) ++ return; ++ if (data->start == key) ++ data->start = NULL; ++ ++ if (data->start == NULL) { ++ switch (data->cb(data->proc, value, data->cb_data)) { ++ case CBS_FAIL: ++ /* XXX handle me */ ++ case CBS_STOP: ++ data->end = key; ++ case CBS_CONT: ++ return; ++ } ++ } ++} ++ ++void * ++proc_each_breakpoint(struct Process *proc, void *start, ++ enum callback_status (*cb)(struct Process *proc, ++ struct breakpoint *bp, ++ void *data), void *data) ++{ ++ struct each_breakpoint_data dd = { ++ .start = start, ++ .proc = proc, ++ .cb = cb, ++ .cb_data = data, ++ }; ++ dict_apply_to_all(proc->breakpoints, &each_breakpoint_cb, &dd); ++ return dd.end; ++} +diff --git a/proc.h b/proc.h +new file mode 100644 +index 0000000..443bd8e +--- /dev/null ++++ b/proc.h +@@ -0,0 +1,234 @@ ++/* ++ * 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_ ++ ++#if defined(HAVE_LIBUNWIND) ++# include ++#endif /* defined(HAVE_LIBUNWIND) */ ++ ++#include "ltrace.h" ++#include "dict.h" ++#include "sysdep.h" ++ ++struct library; ++struct breakpoint; ++ ++/* 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. */ ++ CBS_FAIL, /* There was an error. The iteration should stop ++ * and return error. */ ++}; ++ ++struct event_handler { ++ /* Event handler that overrides the default one. Should ++ * return NULL if the event was handled, otherwise the ++ * returned event is passed to the default handler. */ ++ Event *(*on_event)(struct event_handler *self, Event *event); ++ ++ /* Called when the event handler removal is requested. */ ++ void (*destroy)(struct event_handler *self); ++}; ++ ++enum process_state { ++ STATE_ATTACHED = 0, ++ STATE_BEING_CREATED, ++ STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ ++}; ++ ++struct callstack_element { ++ union { ++ int syscall; ++ struct library_symbol * libfunc; ++ } c_un; ++ int is_syscall; ++ void * return_addr; ++ struct timeval time_spent; ++ void * arch_ptr; ++}; ++ ++/* XXX We should get rid of this. */ ++#define MAX_CALLDEPTH 64 ++ ++/* XXX We would rather have this all organized a little differently, ++ * have Process for the whole group and Task for what's there for ++ * per-thread stuff. But for now this is the less invasive way of ++ * structuring it. */ ++typedef struct Process Process; ++struct Process { ++ enum process_state state; ++ Process * parent; /* needed by STATE_BEING_CREATED */ ++ char * filename; ++ pid_t pid; ++ ++ /* Dictionary of breakpoints (which is a mapping ++ * address->breakpoint). This is NULL for non-leader ++ * processes. XXX note that we store addresses (keys) by ++ * value. That assumes that target_address_t fits in host ++ * pointer. */ ++ Dict * breakpoints; ++ ++ int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ ++ unsigned int personality; ++ int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ ++ ++ int callstack_depth; ++ struct callstack_element callstack[MAX_CALLDEPTH]; ++ ++ /* Linked list of libraries in backwards order of mapping. ++ * The last element is the executed binary itself. */ ++ struct library *libraries; ++ ++ /* Arch-dependent: */ ++ void *debug; /* arch-dep process debug struct XXX move to ++ * os_process_data after it's invented. */ ++ void * instruction_pointer; ++ void * stack_pointer; /* To get return addr, args... */ ++ void * return_addr; ++ void * arch_ptr; ++ short e_machine; ++#ifdef __arm__ ++ int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ ++#endif ++ ++#if defined(HAVE_LIBUNWIND) ++ /* libunwind address space */ ++ unw_addr_space_t unwind_as; ++ void *unwind_priv; ++#endif /* defined(HAVE_LIBUNWIND) */ ++ ++ /* Set in leader. */ ++ struct event_handler *event_handler; ++ ++ /** ++ * Process chaining. ++ **/ ++ Process * next; ++ ++ /* LEADER points to the leader thread of the POSIX.1 process. ++ If X->LEADER == X, then X is the leader thread and the ++ Process structures chained by NEXT represent other threads, ++ up until, but not including, the next leader thread. ++ LEADER may be NULL after the leader has already exited. In ++ that case this process is waiting to be collected. */ ++ Process * leader; ++ ++ struct arch_process_data arch; ++}; ++ ++/* Initialize a process given a path to binary FILENAME, with a PID, ++ * and add the process to an internal chain of traced processes. */ ++int process_init(struct Process *proc, const char *filename, pid_t pid); ++ ++/* PROC underwent an exec. This is a bit like process_destroy ++ * followed by process_init, except that some state is kept and the ++ * process doesn't lose it's place in the list of processes. */ ++int process_exec(struct Process *proc); ++ ++/* Release any memory allocated for PROC (but not PROC itself). Does ++ * NOT remove PROC from internal chain. ++ * ++ * XXX clearly this init/destroy pair is different than others and ++ * should be fixed. process_init should presumably be separate from ++ * process_add. */ ++void process_destroy(struct Process *proc); ++ ++struct Process *open_program(const char *filename, pid_t pid); ++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: The iteration starts after the ++ * process designated by START_AFTER, or at the first process if ++ * START_AFTER is NULL. 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. 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_after, ++ enum callback_status (*cb)(struct Process *proc, ++ void *data), ++ void *data); ++ ++/* Iterate through list of tasks of given process PROC. Restarts are ++ * supported via START_AFTER (see each_process for details of ++ * iteration interface). */ ++Process *each_task(struct Process *proc, struct Process *start_after, ++ enum callback_status (*cb)(struct Process *proc, ++ void *data), ++ void *data); ++ ++void change_process_leader(Process *proc, Process *leader); ++ ++/* Remove process from the list of traced processes, drop any events ++ * in the event queue, destroy it and free memory. */ ++void remove_process(struct 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); ++ ++/* Insert BP into PROC. */ ++int proc_add_breakpoint(struct Process *proc, struct breakpoint *bp); ++ ++/* Remove BP from PROC. This has no reason to fail in runtime. If it ++ * does not find BP in PROC, it's hard error guarded by assertion. */ ++void proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp); ++ ++/* Iterate through the libraries of PROC. See each_process for ++ * detailed description of the iteration interface. */ ++void *proc_each_breakpoint(struct Process *proc, void *start, ++ enum callback_status (*cb)(struct Process *proc, ++ struct breakpoint *bp, ++ void *data), ++ void *data); ++ ++#endif /* _PROC_H_ */ +diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am +index bd52092..e6fd7ef 100644 +--- a/sysdeps/linux-gnu/Makefile.am ++++ b/sysdeps/linux-gnu/Makefile.am +@@ -28,7 +28,8 @@ ___libos_la_LIBADD = \ + noinst_HEADERS = \ + arch_syscallent.h \ + signalent1.h \ +- syscallent1.h ++ syscallent1.h \ ++ trace.h + + EXTRA_DIST = \ + arch_mksyscallent \ +diff --git a/sysdeps/linux-gnu/alpha/plt.c b/sysdeps/linux-gnu/alpha/plt.c +index 83337b2..8ef456e 100644 +--- a/sysdeps/linux-gnu/alpha/plt.c ++++ b/sysdeps/linux-gnu/alpha/plt.c +@@ -1,4 +1,5 @@ + #include ++#include "proc.h" + #include "common.h" + + GElf_Addr +diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c +index 9554e48..3c02a5d 100644 +--- a/sysdeps/linux-gnu/alpha/regs.c ++++ b/sysdeps/linux-gnu/alpha/regs.c +@@ -4,6 +4,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c +index e4d4063..18fe395 100644 +--- a/sysdeps/linux-gnu/alpha/trace.c ++++ b/sysdeps/linux-gnu/alpha/trace.c +@@ -6,6 +6,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + #include "debug.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..324ff07 100644 +--- a/sysdeps/linux-gnu/arm/breakpoint.c ++++ b/sysdeps/linux-gnu/arm/breakpoint.c +@@ -82,3 +82,29 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); + } + } ++ ++int ++arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) ++{ ++ /* XXX That uintptr_t cast is there temporarily until ++ * target_address_t becomes integral type. */ ++ int thumb_mode = ((uintptr_t)sbp->addr) & 1; ++ if (thumb_mode) ++ sbp->addr = (void *)((uintptr_t)sbp->addr & ~1); ++ sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode; ++ /* XXX This doesn't seem like it belongs here. */ ++ proc->thumb_mode = 0; ++ return 0; ++} ++ ++void ++arch_breakpoint_destroy(struct breakpoint *sbp) ++{ ++} ++ ++int ++arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) ++{ ++ retp->arch.thumb_mode = sbp->arch.thumb_mode; ++ return 0; ++} +diff --git a/sysdeps/linux-gnu/arm/plt.c b/sysdeps/linux-gnu/arm/plt.c +index 76f4f4c..fb98d7b 100644 +--- a/sysdeps/linux-gnu/arm/plt.c ++++ b/sysdeps/linux-gnu/arm/plt.c +@@ -1,4 +1,5 @@ + #include ++#include "proc.h" + #include "common.h" + + static int +diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c +index b8aed6e..22bc4bf 100644 +--- a/sysdeps/linux-gnu/arm/regs.c ++++ b/sysdeps/linux-gnu/arm/regs.c +@@ -4,6 +4,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +@@ -39,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/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c +index 39b8264..f465b72 100644 +--- a/sysdeps/linux-gnu/arm/trace.c ++++ b/sysdeps/linux-gnu/arm/trace.c +@@ -7,6 +7,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + #include "output.h" + #include "ptrace.h" +diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c +index b98374b..e05e730 100644 +--- a/sysdeps/linux-gnu/breakpoint.c ++++ b/sysdeps/linux-gnu/breakpoint.c +@@ -1,11 +1,15 @@ + #include "config.h" + + #include ++#include + #include ++#include + + #include "common.h" +-#include "arch.h" ++#include "sysdep.h" + #include "breakpoint.h" ++#include "proc.h" ++#include "library.h" + + #ifdef ARCH_HAVE_ENABLE_BREAKPOINT + extern void arch_enable_breakpoint(pid_t, struct breakpoint *); +@@ -16,15 +20,20 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) + static unsigned char break_insn[] = BREAKPOINT_VALUE; + unsigned int i, j; + +- if (sbp->libsym) { +- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); +- } else { +- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); +- } ++ debug(DEBUG_PROCESS, ++ "arch_enable_breakpoint: pid=%d, addr=%p, symbol=%s", ++ pid, sbp->addr, breakpoint_name(sbp)); + + for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { + long a = ptrace(PTRACE_PEEKTEXT, pid, + sbp->addr + i * sizeof(long), 0); ++ if (a == -1 && errno) { ++ fprintf(stderr, "enable_breakpoint" ++ " pid=%d, addr=%p, symbol=%s: %s\n", ++ pid, sbp->addr, breakpoint_name(sbp), ++ strerror(errno)); ++ return; ++ } + for (j = 0; + j < sizeof(long) + && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { +@@ -33,7 +42,15 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) + sbp->orig_value[i * sizeof(long) + j] = bytes[j]; + bytes[j] = break_insn[i * sizeof(long) + j]; + } +- ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); ++ a = ptrace(PTRACE_POKETEXT, pid, ++ sbp->addr + i * sizeof(long), a); ++ if (a == -1) { ++ fprintf(stderr, "enable_breakpoint" ++ " pid=%d, addr=%p, symbol=%s: %s\n", ++ pid, sbp->addr, breakpoint_name(sbp), ++ strerror(errno)); ++ return; ++ } + } + } + #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ +@@ -41,11 +58,8 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) + void + 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 { +- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); +- } ++ debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", ++ proc->pid, sbp->addr, breakpoint_name(sbp)); + arch_enable_breakpoint(proc->pid, sbp); + } + +@@ -57,16 +71,19 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + { + unsigned int i, j; + +- if (sbp->libsym) { +- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); +- } else { +- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); +- } ++ debug(DEBUG_PROCESS, ++ "arch_disable_breakpoint: pid=%d, addr=%p, symbol=%s", ++ pid, sbp->addr, breakpoint_name(sbp)); + + for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { +- long a = +- ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), +- 0); ++ long a = ptrace(PTRACE_PEEKTEXT, pid, ++ sbp->addr + i * sizeof(long), 0); ++ if (a == -1 && errno) { ++ fprintf(stderr, ++ "disable_breakpoint pid=%d, addr=%p: %s\n", ++ pid, sbp->addr, strerror(errno)); ++ return; ++ } + for (j = 0; + j < sizeof(long) + && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { +@@ -74,7 +91,14 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + + bytes[j] = sbp->orig_value[i * sizeof(long) + j]; + } +- ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); ++ a = ptrace(PTRACE_POKETEXT, pid, ++ sbp->addr + i * sizeof(long), a); ++ if (a == -1 && errno) { ++ fprintf(stderr, ++ "disable_breakpoint pid=%d, addr=%p: %s\n", ++ pid, sbp->addr, strerror(errno)); ++ return; ++ } + } + } + #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ +@@ -82,10 +106,7 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) + void + 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 { +- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); +- } ++ debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", ++ proc->pid, sbp->addr, breakpoint_name(sbp)); + arch_disable_breakpoint(proc->pid, sbp); + } +diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c +index 0167049..91d873e 100644 +--- a/sysdeps/linux-gnu/events.c ++++ b/sysdeps/linux-gnu/events.c +@@ -13,6 +13,7 @@ + + #include "common.h" + #include "breakpoint.h" ++#include "proc.h" + + static Event event; + +@@ -21,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 +@@ -174,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/i386/plt.c b/sysdeps/linux-gnu/i386/plt.c +index b53ff44..daaf15a 100644 +--- a/sysdeps/linux-gnu/i386/plt.c ++++ b/sysdeps/linux-gnu/i386/plt.c +@@ -1,12 +1,16 @@ + #include +-#include "common.h" ++#include "proc.h" ++#include "library.h" ++#include "ltrace-elf.h" + + GElf_Addr +-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { ++arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) ++{ + return lte->plt_addr + (ndx + 1) * 16; + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { ++sym2addr(struct Process *proc, struct library_symbol *sym) ++{ + return sym->enter_addr; + } +diff --git a/sysdeps/linux-gnu/i386/regs.c b/sysdeps/linux-gnu/i386/regs.c +index 6777f17..a1584ac 100644 +--- a/sysdeps/linux-gnu/i386/regs.c ++++ b/sysdeps/linux-gnu/i386/regs.c +@@ -4,7 +4,7 @@ + #include + #include + +-#include "common.h" ++#include "proc.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR +diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c +index 76f1105..f0c1e50 100644 +--- a/sysdeps/linux-gnu/i386/trace.c ++++ b/sysdeps/linux-gnu/i386/trace.c +@@ -1,12 +1,14 @@ + #include "config.h" + +-#include ++#include + #include + #include +-#include +-#include + #include ++#include ++#include ++#include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +@@ -24,20 +26,32 @@ get_arch_dep(Process *proc) { + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct Process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { ++ struct callstack_element *elem = NULL; ++ if (proc->callstack_depth > 0) ++ elem = proc->callstack + proc->callstack_depth - 1; ++ + *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ORIG_EAX, 0); ++ if (*sysnum == -1) { ++ if (errno) ++ return -1; ++ /* Otherwise, ORIG_EAX == -1 means that the ++ * system call should not be restarted. In ++ * that case rely on what we have on ++ * stack. */ ++ if (elem != NULL && elem->is_syscall) ++ *sysnum = elem->c_un.syscall; ++ } + +- if (proc->callstack_depth > 0 && +- proc->callstack[proc->callstack_depth - 1].is_syscall && +- proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { ++ if (elem != NULL && elem->is_syscall ++ && elem->c_un.syscall == *sysnum) + return 2; +- } + +- if (*sysnum >= 0) { ++ if (*sysnum >= 0) + return 1; +- } + } + return 0; + } +diff --git a/sysdeps/linux-gnu/ia64/plt.c b/sysdeps/linux-gnu/ia64/plt.c +index 7fd451b..323df65 100644 +--- a/sysdeps/linux-gnu/ia64/plt.c ++++ b/sysdeps/linux-gnu/ia64/plt.c +@@ -1,4 +1,5 @@ + #include ++#include "proc.h" + #include "common.h" + + /* A bundle is 128 bits */ +diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c +index 3f5d951..64c0164 100644 +--- a/sysdeps/linux-gnu/ia64/regs.c ++++ b/sysdeps/linux-gnu/ia64/regs.c +@@ -8,6 +8,7 @@ + #include + + #include ++#include "proc.h" + #include "common.h" + + void * +diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c +index 079ed55..385fac1 100644 +--- a/sysdeps/linux-gnu/ia64/trace.c ++++ b/sysdeps/linux-gnu/ia64/trace.c +@@ -11,6 +11,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + /* What we think of as a bundle, ptrace thinks of it as two unsigned +diff --git a/sysdeps/linux-gnu/m68k/plt.c b/sysdeps/linux-gnu/m68k/plt.c +index 508d7fc..a1c2604 100644 +--- a/sysdeps/linux-gnu/m68k/plt.c ++++ b/sysdeps/linux-gnu/m68k/plt.c +@@ -1,4 +1,5 @@ + #include ++#include "proc.h" + #include "common.h" + + GElf_Addr +diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c +index 959a60e..1542b5a 100644 +--- a/sysdeps/linux-gnu/m68k/regs.c ++++ b/sysdeps/linux-gnu/m68k/regs.c +@@ -4,6 +4,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c +index 2f89fdf..c63702d 100644 +--- a/sysdeps/linux-gnu/m68k/trace.c ++++ b/sysdeps/linux-gnu/m68k/trace.c +@@ -6,6 +6,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h +index dd0ca35..f7e2316 100644 +--- a/sysdeps/linux-gnu/mipsel/arch.h ++++ b/sysdeps/linux-gnu/mipsel/arch.h +@@ -1,3 +1,8 @@ ++#ifndef LTRACE_MIPS_ARCH_H ++#define LTRACE_MIPS_ARCH_H ++ ++#include ++ + #define BREAKPOINT_VALUE { 0x0d, 0x00, 0x00, 0x00 } + #define BREAKPOINT_LENGTH 4 + #define DECR_PC_AFTER_BREAK 0 +@@ -7,3 +12,12 @@ + + #define PLTs_INIT_BY_HERE "_start" + #define E_ENTRY_NAME "_start" ++ ++#define ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++ size_t pltgot_addr; ++ size_t mips_local_gotno; ++ size_t mips_gotsym; ++}; ++ ++#endif /* LTRACE_MIPS_ARCH_H */ +diff --git a/sysdeps/linux-gnu/mipsel/plt.c b/sysdeps/linux-gnu/mipsel/plt.c +index 57dfb9a..3ffaddf 100644 +--- a/sysdeps/linux-gnu/mipsel/plt.c ++++ b/sysdeps/linux-gnu/mipsel/plt.c +@@ -1,6 +1,8 @@ + #include "debug.h" + #include + #include ++#include ++#include "proc.h" + #include "common.h" + + /** +@@ -69,4 +71,62 @@ sym2addr(Process *proc, struct library_symbol *sym) { + return (void *)ret;; + } + ++/** ++ MIPS ABI Supplement: ++ ++ DT_PLTGOT This member holds the address of the .got section. ++ ++ DT_MIPS_SYMTABNO This member holds the number of entries in the ++ .dynsym section. ++ ++ DT_MIPS_LOCAL_GOTNO This member holds the number of local global ++ offset table entries. ++ ++ DT_MIPS_GOTSYM This member holds the index of the first dyamic ++ symbol table entry that corresponds to an entry in the gobal offset ++ table. ++ ++ */ ++int ++arch_elf_init(struct ltelf *lte) ++{ ++ Elf_Scn *scn; ++ GElf_Shdr shdr; ++ if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0 ++ || scn == NULL) { ++ fail: ++ error(0, 0, "Couldn't get SHT_DYNAMIC: %s", ++ elf_errmsg(-1)); ++ return -1; ++ } ++ ++ Elf_Data *data = elf_loaddata(scn, &shdr); ++ if (data == NULL) ++ goto fail; ++ ++ size_t j; ++ for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { ++ GElf_Dyn dyn; ++ if (gelf_getdyn(data, j, &dyn) == NULL) ++ goto fail; ++ ++ if(dyn.d_tag == DT_PLTGOT) { ++ lte->arch.pltgot_addr = dyn.d_un.d_ptr; ++ } ++ if(dyn.d_tag == DT_MIPS_LOCAL_GOTNO){ ++ lte->arch.mips_local_gotno = dyn.d_un.d_val; ++ } ++ if(dyn.d_tag == DT_MIPS_GOTSYM){ ++ lte->arch.mips_gotsym = dyn.d_un.d_val; ++ } ++ } ++ ++ return 0; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++} ++ + /**@}*/ +diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c +index badbb10..a8a9b10 100644 +--- a/sysdeps/linux-gnu/mipsel/regs.c ++++ b/sysdeps/linux-gnu/mipsel/regs.c +@@ -5,6 +5,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + #include "mipsel.h" + +diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c +index 6553967..4b999e4 100644 +--- a/sysdeps/linux-gnu/mipsel/trace.c ++++ b/sysdeps/linux-gnu/mipsel/trace.c +@@ -6,6 +6,7 @@ + #include + #include + #include "debug.h" ++#include "proc.h" + #include "common.h" + #include "mipsel.h" + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index 64c1821..6e258e7 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -1,3 +1,8 @@ ++#ifndef LTRACE_PPC_ARCH_H ++#define LTRACE_PPC_ARCH_H ++ ++#include ++ + #define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } + #define BREAKPOINT_LENGTH 4 + #define DECR_PC_AFTER_BREAK 0 +@@ -13,13 +18,68 @@ + + #define PLT_REINITALISATION_BP "_start" + +-/* Start of arch-specific functions. */ + #define ARCH_ENDIAN_BIG + #define ARCH_HAVE_ATOMIC_SINGLESTEP ++#define ARCH_HAVE_ADD_PLT_ENTRY ++#define ARCH_HAVE_TRANSLATE_ADDRESS ++#define ARCH_HAVE_DYNLINK_DONE + +-#define PPC_NOP { 0x60, 0x00, 0x00, 0x00 } +-#define PPC_NOP_LENGTH 4 ++struct library_symbol; + +-#if (PPC_NOP_LENGTH != BREAKPOINT_LENGTH) +-#error "Length of the breakpoint value not equal to the length of a nop instruction" +-#endif ++#define ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++ GElf_Addr plt_stub_vma; ++ struct library_symbol *stubs; ++ Elf_Data *opd_data; ++ GElf_Addr opd_base; ++ GElf_Xword opd_size; ++ int secure_plt; ++}; ++ ++#define ARCH_HAVE_LIBRARY_DATA ++struct arch_library_data { ++ int bss_plt_prelinked; ++}; ++ ++enum ppc64_plt_type { ++ /* Either a non-PLT symbol, or PPC32 symbol. */ ++ PPC_DEFAULT = 0, ++ ++ /* PPC64 STUB, never resolved. */ ++ PPC64_PLT_STUB, ++ ++ /* Unresolved PLT symbol (.plt contains PLT address). */ ++ PPC_PLT_UNRESOLVED, ++ ++ /* Resolved PLT symbol. The corresponding .plt slot contained ++ * target address, which was changed to the address of ++ * corresponding PLT entry. The original is now saved in ++ * RESOLVED_VALUE. */ ++ PPC_PLT_RESOLVED, ++}; ++ ++#define ARCH_HAVE_LIBRARY_SYMBOL_DATA ++struct arch_library_symbol_data { ++ enum ppc64_plt_type type; ++ GElf_Addr resolved_value; ++ ++ /* Address of corresponding slot in .plt. */ ++ GElf_Addr plt_slot_addr; ++}; ++ ++#define ARCH_HAVE_BREAKPOINT_DATA ++struct arch_breakpoint_data { ++ /* We need this just for arch_breakpoint_init. */ ++}; ++ ++#define ARCH_HAVE_PROCESS_DATA ++struct arch_process_data { ++ /* Breakpoint that hits when the dynamic linker is about to ++ * update a .plt slot. NULL before that address is known. */ ++ struct breakpoint *dl_plt_update_bp; ++ ++ /* PLT update breakpoint looks here for the handler. */ ++ struct process_stopping_handler *handler; ++}; ++ ++#endif /* LTRACE_PPC_ARCH_H */ +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 668f63d..3b6a25f 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -1,55 +1,986 @@ + #include + #include ++#include ++#include ++#include ++#include ++#include ++ ++#include "proc.h" + #include "common.h" ++#include "library.h" ++#include "breakpoint.h" ++#include "linux-gnu/trace.h" ++ ++/* There are two PLT types on 32-bit PPC: old-style, BSS PLT, and ++ * new-style "secure" PLT. We can tell one from the other by the ++ * flags on the .plt section. If it's +X (executable), it's BSS PLT, ++ * otherwise it's secure. ++ * ++ * BSS PLT works the same way as most architectures: the .plt section ++ * contains trampolines and we put breakpoints to those. If not ++ * prelinked, .plt contains zeroes, and dynamic linker fills in the ++ * initial set of trampolines, which means that we need to delay ++ * enabling breakpoints until after binary entry point is hit. ++ * Additionally, after first call, dynamic linker updates .plt with ++ * branch to resolved address. That means that on first hit, we must ++ * do something similar to the PPC64 gambit described below. ++ * ++ * With secure PLT, the .plt section doesn't contain instructions but ++ * addresses. The real PLT table is stored in .text. Addresses of ++ * those PLT entries can be computed, and apart from the fact that ++ * they are in .text, they are ordinary PLT entries. ++ * ++ * 64-bit PPC is more involved. Program linker creates for each ++ * library call a _stub_ symbol named xxxxxxxx.plt_call. ++ * (where xxxxxxxx is a hexadecimal number). That stub does the call ++ * dispatch: it loads an address of a function to call from the ++ * section .plt, and branches. PLT entries themselves are essentially ++ * a curried call to the resolver. When the symbol is resolved, the ++ * resolver updates the value stored in .plt, and the next time ++ * around, the stub calls the library function directly. So we make ++ * at most one trip (none if the binary is prelinked) through each PLT ++ * entry, and correspondingly that is useless as a breakpoint site. ++ * ++ * Note the three confusing terms: stubs (that play the role of PLT ++ * entries), PLT entries, .plt section. ++ * ++ * We first check symbol tables and see if we happen to have stub ++ * symbols available. If yes we just put breakpoints to those, and ++ * treat them as usual breakpoints. The only tricky part is realizing ++ * that there can be more than one breakpoint per symbol. ++ * ++ * The case that we don't have the stub symbols available is harder. ++ * The following scheme uses two kinds of PLT breakpoints: unresolved ++ * and resolved (to some address). When the process starts (or when ++ * we attach), we distribute unresolved PLT breakpoints to the PLT ++ * entries (not stubs). Then we look in .plt, and for each entry ++ * whose value is different than the corresponding PLT entry address, ++ * we assume it was already resolved, and convert the breakpoint to ++ * resolved. We also rewrite the resolved value in .plt back to the ++ * PLT address. ++ * ++ * When a PLT entry hits a resolved breakpoint (which happens because ++ * we rewrite .plt with the original unresolved addresses), we move ++ * the instruction pointer to the corresponding address and continue ++ * the process as if nothing happened. ++ * ++ * When unresolved PLT entry is called for the first time, we need to ++ * catch the new value that the resolver will write to a .plt slot. ++ * We also need to prevent another thread from racing through and ++ * taking the branch without ltrace noticing. So when unresolved PLT ++ * entry hits, we have to stop all threads. We then single-step ++ * through the resolver, until the .plt slot changes. When it does, ++ * we treat it the same way as above: convert the PLT breakpoint to ++ * resolved, and rewrite the .plt value back to PLT address. We then ++ * start all threads again. ++ * ++ * As an optimization, we remember the address where the address was ++ * resolved, and put a breakpoint there. The next time around (when ++ * the next PLT entry is to be resolved), instead of single-stepping ++ * through half the dynamic linker, we just let the thread run and hit ++ * this breakpoint. When it hits, we know the PLT entry was resolved. ++ * ++ * XXX TODO If we have hardware watch point, we might put a read watch ++ * on .plt slot, and discover the offenders this way. I don't know ++ * the details, but I assume at most a handful (like, one or two, if ++ * available at all) addresses may be watched at a time, and thus this ++ * would be used as an amendment of the above rather than full-on ++ * solution to PLT tracing on PPC. ++ */ ++ ++#define PPC_PLT_STUB_SIZE 16 ++#define PPC64_PLT_STUB_SIZE 8 //xxx ++ ++static inline int ++host_powerpc64() ++{ ++#ifdef __powerpc64__ ++ return 1; ++#else ++ return 0; ++#endif ++} ++ ++int ++read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp) ++{ ++ unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); ++ if (l == -1UL && errno) ++ return -1; ++#ifdef __powerpc64__ ++ l >>= 32; ++#endif ++ *lp = l; ++ return 0; ++} ++ ++static int ++read_target_8(struct Process *proc, target_address_t addr, uint64_t *lp) ++{ ++ unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); ++ if (l == -1UL && errno) ++ return -1; ++ if (host_powerpc64()) { ++ *lp = l; ++ } else { ++ unsigned long l2 = ptrace(PTRACE_PEEKTEXT, proc->pid, ++ addr + 4, 0); ++ if (l2 == -1UL && errno) ++ return -1; ++ *lp = ((uint64_t)l << 32) | l2; ++ } ++ return 0; ++} ++ ++int ++read_target_long(struct Process *proc, target_address_t addr, uint64_t *lp) ++{ ++ if (proc->e_machine == EM_PPC) { ++ uint32_t w; ++ int ret = read_target_4(proc, addr, &w); ++ if (ret >= 0) ++ *lp = (uint64_t)w; ++ return ret; ++ } else { ++ return read_target_8(proc, addr, lp); ++ } ++} ++ ++static enum callback_status ++reenable_breakpoint(struct Process *proc, struct breakpoint *bp, void *data) ++{ ++ /* We don't need to re-enable non-PLT breakpoints and ++ * breakpoints that are not PPC32 BSS unprelinked. */ ++ if (bp->libsym == NULL ++ || bp->libsym->plt_type == LS_TOPLT_NONE ++ || bp->libsym->lib->arch.bss_plt_prelinked != 0) ++ return CBS_CONT; ++ ++ debug(DEBUG_PROCESS, "pid=%d reenable_breakpoint %s", ++ proc->pid, breakpoint_name(bp)); ++ ++ assert(proc->e_machine == EM_PPC); ++ uint64_t l; ++ if (read_target_8(proc, bp->addr, &l) < 0) { ++ error(0, errno, "couldn't read PLT value for %s(%p)", ++ breakpoint_name(bp), bp->addr); ++ return CBS_CONT; ++ } ++ ++ /* XXX double cast */ ++ bp->libsym->arch.plt_slot_addr = (GElf_Addr)(uintptr_t)bp->addr; ++ ++ /* If necessary, re-enable the breakpoint if it was ++ * overwritten by the dynamic linker. */ ++ union { ++ uint32_t insn; ++ char buf[4]; ++ } u = { .buf = BREAKPOINT_VALUE }; ++ if (l >> 32 == u.insn) ++ debug(DEBUG_PROCESS, "pid=%d, breakpoint still present" ++ " at %p, avoiding reenable", proc->pid, bp->addr); ++ else ++ enable_breakpoint(proc, bp); ++ ++ bp->libsym->arch.resolved_value = l; ++ ++ return CBS_CONT; ++} ++ ++void ++arch_dynlink_done(struct Process *proc) ++{ ++ /* On PPC32, .plt of objects that use BSS PLT are overwritten ++ * by the dynamic linker (unless that object was prelinked). ++ * We need to re-enable breakpoints in those objects. */ ++ proc_each_breakpoint(proc, NULL, reenable_breakpoint, NULL); ++} + + GElf_Addr +-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { +- return rela->r_offset; ++arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) ++{ ++ if (lte->ehdr.e_machine == EM_PPC && lte->arch.secure_plt) { ++ assert(lte->arch.plt_stub_vma != 0); ++ return lte->arch.plt_stub_vma + PPC_PLT_STUB_SIZE * ndx; ++ ++ } else if (lte->ehdr.e_machine == EM_PPC) { ++ return rela->r_offset; ++ ++ } else { ++ /* If we get here, we don't have stub symbols. In ++ * that case we put brakpoints to PLT entries the same ++ * as the PPC32 secure PLT case does. */ ++ assert(lte->arch.plt_stub_vma != 0); ++ return lte->arch.plt_stub_vma + PPC64_PLT_STUB_SIZE * ndx; ++ } ++} ++ ++/* This entry point is called when ltelf is not available ++ * anymore--during runtime. At that point we don't have to concern ++ * ourselves with bias, as the values in OPD have been resolved ++ * already. */ ++int ++arch_translate_address_dyn(struct Process *proc, ++ target_address_t addr, target_address_t *ret) ++{ ++ if (proc->e_machine == EM_PPC64) { ++ uint64_t value; ++ if (read_target_8(proc, addr, &value) < 0) { ++ error(0, errno, "dynamic .opd translation of %p", addr); ++ return -1; ++ } ++ *ret = (target_address_t)value; ++ return 0; ++ } ++ ++ *ret = addr; ++ return 0; ++} ++ ++int ++arch_translate_address(struct ltelf *lte, ++ target_address_t addr, target_address_t *ret) ++{ ++ if (lte->ehdr.e_machine == EM_PPC64) { ++ GElf_Xword offset = (GElf_Addr)addr - lte->arch.opd_base; ++ uint64_t value; ++ if (elf_read_u64(lte->arch.opd_data, offset, &value) < 0) { ++ error(0, 0, "static .opd translation of %p: %s", addr, ++ elf_errmsg(-1)); ++ return -1; ++ } ++ *ret = (target_address_t)(value + lte->bias); ++ return 0; ++ } ++ ++ *ret = addr; ++ return 0; ++} ++ ++static int ++load_opd_data(struct ltelf *lte, struct library *lib) ++{ ++ Elf_Scn *sec; ++ GElf_Shdr shdr; ++ if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) { ++ fail: ++ fprintf(stderr, "couldn't find .opd data\n"); ++ return -1; ++ } ++ ++ lte->arch.opd_data = elf_rawdata(sec, NULL); ++ if (lte->arch.opd_data == NULL) ++ goto fail; ++ ++ lte->arch.opd_base = shdr.sh_addr + lte->bias; ++ lte->arch.opd_size = shdr.sh_size; ++ ++ return 0; + } + + void * +-sym2addr(Process *proc, struct library_symbol *sym) { +- void *addr = sym->enter_addr; +- long pt_ret; ++sym2addr(struct Process *proc, struct library_symbol *sym) ++{ ++ return sym->enter_addr; ++} ++ ++static GElf_Addr ++get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data) ++{ ++ Elf_Scn *ppcgot_sec = NULL; ++ GElf_Shdr ppcgot_shdr; ++ if (ppcgot != 0 ++ && elf_get_section_covering(lte, ppcgot, ++ &ppcgot_sec, &ppcgot_shdr) < 0) ++ error(0, 0, "DT_PPC_GOT=%#"PRIx64", but no such section found", ++ ppcgot); ++ ++ if (ppcgot_sec != NULL) { ++ Elf_Data *data = elf_loaddata(ppcgot_sec, &ppcgot_shdr); ++ if (data == NULL || data->d_size < 8 ) { ++ error(0, 0, "couldn't read GOT data"); ++ } else { ++ // where PPCGOT begins in .got ++ size_t offset = ppcgot - ppcgot_shdr.sh_addr; ++ assert(offset % 4 == 0); ++ uint32_t glink_vma; ++ if (elf_read_u32(data, offset + 4, &glink_vma) < 0) { ++ error(0, 0, "couldn't read glink VMA address" ++ " at %zd@GOT", offset); ++ return 0; ++ } ++ if (glink_vma != 0) { ++ debug(1, "PPC GOT glink_vma address: %#" PRIx32, ++ glink_vma); ++ return (GElf_Addr)glink_vma; ++ } ++ } ++ } ++ ++ if (plt_data != NULL) { ++ uint32_t glink_vma; ++ if (elf_read_u32(plt_data, 0, &glink_vma) < 0) { ++ error(0, 0, "couldn't read glink VMA address"); ++ return 0; ++ } ++ debug(1, ".plt glink_vma address: %#" PRIx32, glink_vma); ++ return (GElf_Addr)glink_vma; ++ } ++ ++ return 0; ++} ++ ++static int ++load_dynamic_entry(struct ltelf *lte, int tag, GElf_Addr *valuep) ++{ ++ Elf_Scn *scn; ++ GElf_Shdr shdr; ++ if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0 ++ || scn == NULL) { ++ fail: ++ error(0, 0, "Couldn't get SHT_DYNAMIC: %s", ++ elf_errmsg(-1)); ++ return -1; ++ } + +- debug(3, 0); ++ Elf_Data *data = elf_loaddata(scn, &shdr); ++ if (data == NULL) ++ goto fail; + +- if (sym->plt_type != LS_TOPLT_POINT) { +- return addr; ++ size_t j; ++ for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { ++ GElf_Dyn dyn; ++ if (gelf_getdyn(data, j, &dyn) == NULL) ++ goto fail; ++ ++ if(dyn.d_tag == tag) { ++ *valuep = dyn.d_un.d_ptr; ++ return 0; ++ } + } + +- if (proc->pid == 0) { ++ return -1; ++} ++ ++static int ++load_ppcgot(struct ltelf *lte, GElf_Addr *ppcgotp) ++{ ++ return load_dynamic_entry(lte, DT_PPC_GOT, ppcgotp); ++} ++ ++static int ++load_ppc64_glink(struct ltelf *lte, GElf_Addr *glinkp) ++{ ++ return load_dynamic_entry(lte, DT_PPC64_GLINK, glinkp); ++} ++ ++static int ++nonzero_data(Elf_Data *data) ++{ ++ /* We are not supposed to get here if there's no PLT. */ ++ assert(data != NULL); ++ ++ unsigned char *buf = data->d_buf; ++ if (buf == NULL) + return 0; ++ ++ size_t i; ++ for (i = 0; i < data->d_size; ++i) ++ if (buf[i] != 0) ++ return 1; ++ return 0; ++} ++ ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) ++{ ++ if (lte->ehdr.e_machine == EM_PPC64 ++ && load_opd_data(lte, lib) < 0) ++ return -1; ++ ++ lte->arch.secure_plt = !(lte->plt_flags & SHF_EXECINSTR); ++ ++ /* For PPC32 BSS, it is important whether the binary was ++ * prelinked. If .plt section is NODATA, or if it contains ++ * zeroes, then this library is not prelinked, and we need to ++ * delay breakpoints. */ ++ if (lte->ehdr.e_machine == EM_PPC && !lte->arch.secure_plt) ++ lib->arch.bss_plt_prelinked = nonzero_data(lte->plt_data); ++ else ++ /* For cases where it's irrelevant, initialize the ++ * value to something conspicuous. */ ++ lib->arch.bss_plt_prelinked = -1; ++ ++ if (lte->ehdr.e_machine == EM_PPC && lte->arch.secure_plt) { ++ GElf_Addr ppcgot; ++ if (load_ppcgot(lte, &ppcgot) < 0) { ++ error(0, 0, "couldn't find DT_PPC_GOT"); ++ return -1; ++ } ++ GElf_Addr glink_vma = get_glink_vma(lte, ppcgot, lte->plt_data); ++ ++ assert (lte->relplt_size % 12 == 0); ++ size_t count = lte->relplt_size / 12; // size of RELA entry ++ lte->arch.plt_stub_vma = glink_vma ++ - (GElf_Addr)count * PPC_PLT_STUB_SIZE; ++ debug(1, "stub_vma is %#" PRIx64, lte->arch.plt_stub_vma); ++ ++ } else if (lte->ehdr.e_machine == EM_PPC64) { ++ GElf_Addr glink_vma; ++ if (load_ppc64_glink(lte, &glink_vma) < 0) { ++ error(0, 0, "couldn't find DT_PPC64_GLINK"); ++ return -1; ++ } ++ ++ /* The first glink stub starts at offset 32. */ ++ lte->arch.plt_stub_vma = glink_vma + 32; + } + +- if (options.debug >= 3) { +- xinfdump(proc->pid, (void *)(((long)addr-32)&0xfffffff0), +- sizeof(void*)*8); ++ /* On PPC64, look for stub symbols in symbol table. These are ++ * called: xxxxxxxx.plt_call.callee_name@version+addend. */ ++ if (lte->ehdr.e_machine == EM_PPC64 ++ && lte->symtab != NULL && lte->strtab != NULL) { ++ ++ /* N.B. We can't simply skip the symbols that we fail ++ * to read or malloc. There may be more than one stub ++ * per symbol name, and if we failed in one but ++ * succeeded in another, the PLT enabling code would ++ * have no way to tell that something is missing. We ++ * could work around that, of course, but it doesn't ++ * seem worth the trouble. So if anything fails, we ++ * just pretend that we don't have stub symbols at ++ * all, as if the binary is stripped. */ ++ ++ size_t i; ++ for (i = 0; i < lte->symtab_count; ++i) { ++ GElf_Sym sym; ++ if (gelf_getsym(lte->symtab, i, &sym) == NULL) { ++ struct library_symbol *sym, *next; ++ fail: ++ for (sym = lte->arch.stubs; sym != NULL; ) { ++ next = sym->next; ++ library_symbol_destroy(sym); ++ free(sym); ++ sym = next; ++ } ++ lte->arch.stubs = NULL; ++ break; ++ } ++ ++ const char *name = lte->strtab + sym.st_name; ++ ++#define STUBN ".plt_call." ++ if ((name = strstr(name, STUBN)) == NULL) ++ continue; ++ name += sizeof(STUBN) - 1; ++#undef STUBN ++ ++ size_t len; ++ const char *ver = strchr(name, '@'); ++ if (ver != NULL) { ++ len = ver - name; ++ ++ } else { ++ /* If there is "+" at all, check that ++ * the symbol name ends in "+0". */ ++ const char *add = strrchr(name, '+'); ++ if (add != NULL) { ++ assert(strcmp(add, "+0") == 0); ++ len = add - name; ++ } else { ++ len = strlen(name); ++ } ++ } ++ ++ char *sym_name = strndup(name, len); ++ struct library_symbol *libsym = malloc(sizeof(*libsym)); ++ if (sym_name == NULL || libsym == NULL) { ++ fail2: ++ free(sym_name); ++ free(libsym); ++ goto fail; ++ } ++ ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ target_address_t addr = (target_address_t) ++ (uintptr_t)sym.st_value + lte->bias; ++ if (library_symbol_init(libsym, addr, sym_name, 1, ++ LS_TOPLT_EXEC) < 0) ++ goto fail2; ++ libsym->arch.type = PPC64_PLT_STUB; ++ libsym->next = lte->arch.stubs; ++ lte->arch.stubs = libsym; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) ++{ ++ /* On PPC64, we read from .plt, which contains 8 byte ++ * addresses. On PPC32 we read from .plt, which contains 4 ++ * byte instructions, but the PLT is two instructions, and ++ * either can change. */ ++ uint64_t l; ++ /* XXX double cast. */ ++ if (read_target_8(proc, (target_address_t)(uintptr_t)addr, &l) < 0) { ++ error(0, errno, "ptrace .plt slot value @%#" PRIx64, addr); ++ return -1; + } + +- // On a PowerPC-64 system, a plt is three 64-bit words: the first is the +- // 64-bit address of the routine. Before the PLT has been initialized, +- // this will be 0x0. In fact, the symbol table won't have the plt's +- // address even. Ater the PLT has been initialized, but before it has +- // been resolved, the first word will be the address of the function in +- // the dynamic linker that will reslove the PLT. After the PLT is +- // resolved, this will will be the address of the routine whose symbol +- // is in the symbol table. ++ *valp = (GElf_Addr)l; ++ return 0; ++} ++ ++static int ++unresolve_plt_slot(struct Process *proc, GElf_Addr addr, GElf_Addr value) ++{ ++ /* We only modify plt_entry[0], which holds the resolved ++ * address of the routine. We keep the TOC and environment ++ * pointers intact. Hence the only adjustment that we need to ++ * do is to IP. */ ++ if (ptrace(PTRACE_POKETEXT, proc->pid, addr, value) < 0) { ++ error(0, errno, "unresolve .plt slot"); ++ return -1; ++ } ++ return 0; ++} ++ ++static void ++mark_as_resolved(struct library_symbol *libsym, GElf_Addr value) ++{ ++ libsym->arch.type = PPC_PLT_RESOLVED; ++ libsym->arch.resolved_value = value; ++} + +- // On a PowerPC-32 system, there are two types of PLTs: secure (new) and +- // non-secure (old). For the secure case, the PLT is simply a pointer +- // and we can treat it much as we do for the PowerPC-64 case. For the +- // non-secure case, the PLT is executable code and we can put the +- // break-point right in the PLT. ++enum plt_status ++arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, ++ const char *a_name, GElf_Rela *rela, size_t ndx, ++ struct library_symbol **ret) ++{ ++ if (lte->ehdr.e_machine == EM_PPC) ++ return plt_default; + +- pt_ret = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); ++ /* PPC64. If we have stubs, we return a chain of breakpoint ++ * sites, one for each stub that corresponds to this PLT ++ * entry. */ ++ struct library_symbol *chain = NULL; ++ struct library_symbol **symp; ++ for (symp = <e->arch.stubs; *symp != NULL; ) { ++ struct library_symbol *sym = *symp; ++ if (strcmp(sym->name, a_name) != 0) { ++ symp = &(*symp)->next; ++ continue; ++ } ++ ++ /* Re-chain the symbol from stubs to CHAIN. */ ++ *symp = sym->next; ++ sym->next = chain; ++ chain = sym; ++ } ++ ++ if (chain != NULL) { ++ *ret = chain; ++ return plt_ok; ++ } ++ ++ /* We don't have stub symbols. Find corresponding .plt slot, ++ * and check whether it contains the corresponding PLT address ++ * (or 0 if the dynamic linker hasn't run yet). N.B. we don't ++ * want read this from ELF file, but from process image. That ++ * makes a difference if we are attaching to a running ++ * process. */ ++ ++ GElf_Addr plt_entry_addr = arch_plt_sym_val(lte, ndx, rela); ++ GElf_Addr plt_slot_addr = rela->r_offset; ++ assert(plt_slot_addr >= lte->plt_addr ++ || plt_slot_addr < lte->plt_addr + lte->plt_size); ++ ++ GElf_Addr plt_slot_value; ++ if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) ++ return plt_fail; ++ ++ char *name = strdup(a_name); ++ struct library_symbol *libsym = malloc(sizeof(*libsym)); ++ if (name == NULL || libsym == NULL) { ++ error(0, errno, "allocation for .plt slot"); ++ fail: ++ free(name); ++ free(libsym); ++ return plt_fail; ++ } ++ ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ if (library_symbol_init(libsym, ++ (target_address_t)(uintptr_t)plt_entry_addr, ++ name, 1, LS_TOPLT_EXEC) < 0) ++ goto fail; ++ libsym->arch.plt_slot_addr = plt_slot_addr; ++ ++ if (plt_slot_value == plt_entry_addr || plt_slot_value == 0) { ++ libsym->arch.type = PPC_PLT_UNRESOLVED; ++ libsym->arch.resolved_value = plt_entry_addr; + +- if (proc->mask_32bit) { +- // Assume big-endian. +- addr = (void *)((pt_ret >> 32) & 0xffffffff); + } else { +- addr = (void *)pt_ret; ++ /* Unresolve the .plt slot. If the binary was ++ * prelinked, this makes the code invalid, because in ++ * case of prelinked binary, the dynamic linker ++ * doesn't update .plt[0] and .plt[1] with addresses ++ * of the resover. But we don't care, we will never ++ * need to enter the resolver. That just means that ++ * we have to un-un-resolve this back before we ++ * detach. */ ++ ++ if (unresolve_plt_slot(proc, plt_slot_addr, plt_entry_addr) < 0) { ++ library_symbol_destroy(libsym); ++ goto fail; ++ } ++ mark_as_resolved(libsym, plt_slot_value); + } + +- return addr; ++ *ret = libsym; ++ return plt_ok; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++ struct library_symbol *sym; ++ for (sym = lte->arch.stubs; sym != NULL; ) { ++ struct library_symbol *next = sym->next; ++ library_symbol_destroy(sym); ++ free(sym); ++ sym = next; ++ } ++} ++ ++static void ++dl_plt_update_bp_on_hit(struct breakpoint *bp, struct Process *proc) ++{ ++ debug(DEBUG_PROCESS, "pid=%d dl_plt_update_bp_on_hit %s(%p)", ++ proc->pid, breakpoint_name(bp), bp->addr); ++ struct process_stopping_handler *self = proc->arch.handler; ++ assert(self != NULL); ++ ++ struct library_symbol *libsym = self->breakpoint_being_enabled->libsym; ++ GElf_Addr value; ++ if (read_plt_slot_value(proc, libsym->arch.plt_slot_addr, &value) < 0) ++ return; ++ ++ /* On PPC64, we rewrite the slot value. */ ++ if (proc->e_machine == EM_PPC64) ++ unresolve_plt_slot(proc, libsym->arch.plt_slot_addr, ++ libsym->arch.resolved_value); ++ /* We mark the breakpoint as resolved on both arches. */ ++ mark_as_resolved(libsym, value); ++ ++ /* cb_on_all_stopped looks if HANDLER is set to NULL as a way ++ * to check that this was run. It's an error if it ++ * wasn't. */ ++ proc->arch.handler = NULL; ++ ++ breakpoint_turn_off(bp, proc); ++} ++ ++static void ++cb_on_all_stopped(struct process_stopping_handler *self) ++{ ++ /* Put that in for dl_plt_update_bp_on_hit to see. */ ++ assert(self->task_enabling_breakpoint->arch.handler == NULL); ++ self->task_enabling_breakpoint->arch.handler = self; ++ ++ linux_ptrace_disable_and_continue(self); ++} ++ ++static enum callback_status ++cb_keep_stepping_p(struct process_stopping_handler *self) ++{ ++ struct Process *proc = self->task_enabling_breakpoint; ++ struct library_symbol *libsym = self->breakpoint_being_enabled->libsym; ++ ++ GElf_Addr value; ++ if (read_plt_slot_value(proc, libsym->arch.plt_slot_addr, &value) < 0) ++ return CBS_FAIL; ++ ++ /* In UNRESOLVED state, the RESOLVED_VALUE in fact contains ++ * the PLT entry value. */ ++ if (value == libsym->arch.resolved_value) ++ return CBS_CONT; ++ ++ debug(DEBUG_PROCESS, "pid=%d PLT got resolved to value %#"PRIx64, ++ proc->pid, value); ++ ++ /* The .plt slot got resolved! We can migrate the breakpoint ++ * to RESOLVED and stop single-stepping. */ ++ if (proc->e_machine == EM_PPC64 ++ && unresolve_plt_slot(proc, libsym->arch.plt_slot_addr, ++ libsym->arch.resolved_value) < 0) ++ return CBS_FAIL; ++ ++ /* Resolving on PPC64 consists of overwriting a doubleword in ++ * .plt. That doubleword is than read back by a stub, and ++ * jumped on. Hopefully we can assume that double word update ++ * is done on a single place only, as it contains a final ++ * address. We still need to look around for any sync ++ * instruction, but essentially it is safe to optimize away ++ * the single stepping next time and install a post-update ++ * breakpoint. ++ * ++ * The situation on PPC32 BSS is more complicated. The ++ * dynamic linker here updates potentially several ++ * instructions (XXX currently we assume two) and the rules ++ * are more complicated. Sometimes it's enough to adjust just ++ * one of the addresses--the logic for generating optimal ++ * dispatch depends on relative addresses of the .plt entry ++ * and the jump destination. We can't assume that the some ++ * instruction block does the update every time. So on PPC32, ++ * we turn the optimization off and just step through it each ++ * time. */ ++ if (proc->e_machine == EM_PPC) ++ goto done; ++ ++ /* Install breakpoint to the address where the change takes ++ * place. If we fail, then that just means that we'll have to ++ * singlestep the next time around as well. */ ++ struct Process *leader = proc->leader; ++ if (leader == NULL || leader->arch.dl_plt_update_bp != NULL) ++ goto done; ++ ++ /* We need to install to the next instruction. ADDR points to ++ * a store instruction, so moving the breakpoint one ++ * instruction forward is safe. */ ++ target_address_t addr = get_instruction_pointer(proc) + 4; ++ leader->arch.dl_plt_update_bp = insert_breakpoint(proc, addr, NULL); ++ if (leader->arch.dl_plt_update_bp == NULL) ++ goto done; ++ ++ static struct bp_callbacks dl_plt_update_cbs = { ++ .on_hit = dl_plt_update_bp_on_hit, ++ }; ++ leader->arch.dl_plt_update_bp->cbs = &dl_plt_update_cbs; ++ ++ /* Turn it off for now. We will turn it on again when we hit ++ * the PLT entry that needs this. */ ++ breakpoint_turn_off(leader->arch.dl_plt_update_bp, proc); ++ ++done: ++ mark_as_resolved(libsym, value); ++ ++ return CBS_STOP; ++} ++ ++static void ++jump_to_entry_point(struct Process *proc, struct breakpoint *bp) ++{ ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ target_address_t rv = (target_address_t) ++ (uintptr_t)bp->libsym->arch.resolved_value; ++ set_instruction_pointer(proc, rv); ++} ++ ++static void ++ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) ++{ ++ switch (bp->libsym->arch.type) { ++ struct Process *leader; ++ void (*on_all_stopped)(struct process_stopping_handler *); ++ enum callback_status (*keep_stepping_p) ++ (struct process_stopping_handler *); ++ ++ case PPC_DEFAULT: ++ assert(proc->e_machine == EM_PPC); ++ assert(bp->libsym != NULL); ++ assert(bp->libsym->lib->arch.bss_plt_prelinked == 0); ++ /* fall-through */ ++ ++ case PPC_PLT_UNRESOLVED: ++ on_all_stopped = NULL; ++ keep_stepping_p = NULL; ++ leader = proc->leader; ++ ++ if (leader != NULL && leader->arch.dl_plt_update_bp != NULL ++ && breakpoint_turn_on(leader->arch.dl_plt_update_bp, ++ proc) >= 0) ++ on_all_stopped = cb_on_all_stopped; ++ else ++ keep_stepping_p = cb_keep_stepping_p; ++ ++ if (process_install_stopping_handler ++ (proc, bp, on_all_stopped, keep_stepping_p, NULL) < 0) { ++ error(0, 0, "ppc_plt_bp_continue: couldn't install" ++ " event handler"); ++ continue_after_breakpoint(proc, bp); ++ } ++ return; ++ ++ case PPC_PLT_RESOLVED: ++ if (proc->e_machine == EM_PPC) { ++ continue_after_breakpoint(proc, bp); ++ return; ++ } ++ ++ jump_to_entry_point(proc, bp); ++ continue_process(proc->pid); ++ return; ++ ++ case PPC64_PLT_STUB: ++ /* These should never hit here. */ ++ break; ++ } ++ ++ assert(bp->libsym->arch.type != bp->libsym->arch.type); ++ abort(); ++} ++ ++/* When a process is in a PLT stub, it may have already read the data ++ * in .plt that we changed. If we detach now, it will jump to PLT ++ * entry and continue to the dynamic linker, where it will SIGSEGV, ++ * because zeroth .plt slot is not filled in prelinked binaries, and ++ * the dynamic linker needs that data. Moreover, the process may ++ * actually have hit the breakpoint already. This functions tries to ++ * detect both cases and do any fix-ups necessary to mend this ++ * situation. */ ++static enum callback_status ++detach_task_cb(struct Process *task, void *data) ++{ ++ struct breakpoint *bp = data; ++ ++ if (get_instruction_pointer(task) == bp->addr) { ++ debug(DEBUG_PROCESS, "%d at %p, which is PLT slot", ++ task->pid, bp->addr); ++ jump_to_entry_point(task, bp); ++ return CBS_CONT; ++ } ++ ++ /* XXX There's still a window of several instructions where we ++ * might catch the task inside a stub such that it has already ++ * read destination address from .plt, but hasn't jumped yet, ++ * thus avoiding the breakpoint. */ ++ ++ return CBS_CONT; ++} ++ ++static void ++ppc_plt_bp_retract(struct breakpoint *bp, struct Process *proc) ++{ ++ /* On PPC64, we rewrite .plt with PLT entry addresses. This ++ * needs to be undone. Unfortunately, the program may have ++ * made decisions based on that value */ ++ if (proc->e_machine == EM_PPC64 ++ && bp->libsym != NULL ++ && bp->libsym->arch.type == PPC_PLT_RESOLVED) { ++ each_task(proc->leader, NULL, detach_task_cb, bp); ++ unresolve_plt_slot(proc, bp->libsym->arch.plt_slot_addr, ++ bp->libsym->arch.resolved_value); ++ } ++} ++ ++void ++arch_library_init(struct library *lib) ++{ ++} ++ ++void ++arch_library_destroy(struct library *lib) ++{ ++} ++ ++void ++arch_library_clone(struct library *retp, struct library *lib) ++{ ++} ++ ++int ++arch_library_symbol_init(struct library_symbol *libsym) ++{ ++ /* We set type explicitly in the code above, where we have the ++ * necessary context. This is for calls from ltrace-elf.c and ++ * such. */ ++ libsym->arch.type = PPC_DEFAULT; ++ return 0; ++} ++ ++void ++arch_library_symbol_destroy(struct library_symbol *libsym) ++{ ++} ++ ++int ++arch_library_symbol_clone(struct library_symbol *retp, ++ struct library_symbol *libsym) ++{ ++ retp->arch = libsym->arch; ++ return 0; ++} ++ ++/* For some symbol types, we need to set up custom callbacks. XXX we ++ * don't need PROC here, we can store the data in BP if it is of ++ * interest to us. */ ++int ++arch_breakpoint_init(struct Process *proc, struct breakpoint *bp) ++{ ++ /* Artificial and entry-point breakpoints are plain. */ ++ if (bp->libsym == NULL || bp->libsym->plt_type != LS_TOPLT_EXEC) ++ return 0; ++ ++ /* On PPC, secure PLT and prelinked BSS PLT are plain. */ ++ if (proc->e_machine == EM_PPC ++ && bp->libsym->lib->arch.bss_plt_prelinked != 0) ++ return 0; ++ ++ /* On PPC64, stub PLT breakpoints are plain. */ ++ if (proc->e_machine == EM_PPC64 ++ && bp->libsym->arch.type == PPC64_PLT_STUB) ++ return 0; ++ ++ static struct bp_callbacks cbs = { ++ .on_continue = ppc_plt_bp_continue, ++ .on_retract = ppc_plt_bp_retract, ++ }; ++ breakpoint_set_callbacks(bp, &cbs); ++ return 0; ++} ++ ++void ++arch_breakpoint_destroy(struct breakpoint *bp) ++{ ++} ++ ++int ++arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) ++{ ++ retp->arch = sbp->arch; ++ return 0; ++} ++ ++int ++arch_process_init(struct Process *proc) ++{ ++ proc->arch.dl_plt_update_bp = NULL; ++ proc->arch.handler = NULL; ++ return 0; ++} ++ ++void ++arch_process_destroy(struct Process *proc) ++{ ++} ++ ++int ++arch_process_clone(struct Process *retp, struct Process *proc) ++{ ++ retp->arch = proc->arch; ++ return 0; ++} ++ ++int ++arch_process_exec(struct Process *proc) ++{ ++ return arch_process_init(proc); + } +diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c +index eca58ff..26e962f 100644 +--- a/sysdeps/linux-gnu/ppc/regs.c ++++ b/sysdeps/linux-gnu/ppc/regs.c +@@ -3,7 +3,10 @@ + #include + #include + #include ++#include ++#include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +@@ -20,8 +23,10 @@ get_instruction_pointer(Process *proc) { + } + + void +-set_instruction_pointer(Process *proc, void *addr) { +- ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_NIP, addr); ++set_instruction_pointer(Process *proc, void *addr) ++{ ++ if (ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_NIP, addr) != 0) ++ error(0, errno, "set_instruction_pointer"); + } + + void * +diff --git a/sysdeps/linux-gnu/ppc/syscallent.h b/sysdeps/linux-gnu/ppc/syscallent.h +index 5ce5739..7537b3d 100644 +--- a/sysdeps/linux-gnu/ppc/syscallent.h ++++ b/sysdeps/linux-gnu/ppc/syscallent.h +@@ -1,4 +1,4 @@ +-"0", /* 0 */ ++ "restart_syscall", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ +@@ -177,8 +177,8 @@ + "rt_sigtimedwait", /* 176 */ + "rt_sigqueueinfo", /* 177 */ + "rt_sigsuspend", /* 178 */ +- "pread", /* 179 */ +- "pwrite", /* 180 */ ++ "pread64", /* 179 */ ++ "pwrite64", /* 180 */ + "chown", /* 181 */ + "getcwd", /* 182 */ + "capget", /* 183 */ +@@ -254,6 +254,12 @@ + "fstatfs64", /* 253 */ + "fadvise64_64", /* 254 */ + "rtas", /* 255 */ ++ "sys_debug_setcontext", /* 256 */ ++ "server", /* 257 */ ++ "migrate_pages", /* 258 */ ++ "mbind", /* 259 */ ++ "get_mempolicy", /* 260 */ ++ "set_mempolicy", /* 261 */ + "mq_open", /* 262 */ + "mq_unlink", /* 263 */ + "mq_timedsend", /* 264 */ +@@ -270,3 +276,78 @@ + "inotify_init", /* 275 */ + "inotify_add_watch", /* 276 */ + "inotify_rm_watch", /* 277 */ ++ "spu_run", /* 278 */ ++ "spu_create", /* 279 */ ++ "pselect6", /* 280 */ ++ "ppoll", /* 281 */ ++ "unshare", /* 282 */ ++ "splice", /* 283 */ ++ "tee", /* 284 */ ++ "vmsplice", /* 285 */ ++ "openat", /* 286 */ ++ "mkdirat", /* 287 */ ++ "mknodat", /* 288 */ ++ "fchownat", /* 289 */ ++ "futimesat", /* 290 */ ++ "fstatat64", /* 291 */ ++ "unlinkat", /* 292 */ ++ "renameat", /* 293 */ ++ "linkat", /* 294 */ ++ "symlinkat", /* 295 */ ++ "readlinkat", /* 296 */ ++ "fchmodat", /* 297 */ ++ "faccessat", /* 298 */ ++ "get_robust_list", /* 299 */ ++ "set_robust_list", /* 300 */ ++ "move_pages", /* 301 */ ++ "getcpu", /* 302 */ ++ "epoll_pwait", /* 303 */ ++ "utimensat", /* 304 */ ++ "signalfd", /* 305 */ ++ "timerfd_create", /* 306 */ ++ "eventfd", /* 307 */ ++ "sync_file_range2", /* 308 */ ++ "fallocate", /* 309 */ ++ "subpage_prot", /* 310 */ ++ "timerfd_settime", /* 311 */ ++ "timerfd_gettime", /* 312 */ ++ "signalfd4", /* 313 */ ++ "eventfd2", /* 314 */ ++ "epoll_create1", /* 315 */ ++ "dup3", /* 316 */ ++ "pipe2", /* 317 */ ++ "inotify_init1", /* 318 */ ++ "perf_event_open", /* 319 */ ++ "preadv", /* 320 */ ++ "pwritev", /* 321 */ ++ "rt_tgsigqueueinfo", /* 322 */ ++ "fanotify_init", /* 323 */ ++ "fanotify_mark", /* 324 */ ++ "prlimit64", /* 325 */ ++ "socket", /* 326 */ ++ "bind", /* 327 */ ++ "connect", /* 328 */ ++ "listen", /* 329 */ ++ "accept", /* 330 */ ++ "getsockname", /* 331 */ ++ "getpeername", /* 332 */ ++ "socketpair", /* 333 */ ++ "send", /* 334 */ ++ "sendto", /* 335 */ ++ "recv", /* 336 */ ++ "recvfrom", /* 337 */ ++ "shutdown", /* 338 */ ++ "setsockopt", /* 339 */ ++ "getsockopt", /* 340 */ ++ "sendmsg", /* 341 */ ++ "recvmsg", /* 342 */ ++ "recvmmsg", /* 343 */ ++ "accept4", /* 344 */ ++ "name_to_handle_at", /* 345 */ ++ "open_by_handle_at", /* 346 */ ++ "clock_adjtime", /* 347 */ ++ "syncfs", /* 348 */ ++ "sendmmsg", /* 349 */ ++ "setns", /* 350 */ ++ "process_vm_readv", /* 351 */ ++ "process_writev", /* 352 */ +diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c +index 05993de..742785a 100644 +--- a/sysdeps/linux-gnu/ppc/trace.c ++++ b/sysdeps/linux-gnu/ppc/trace.c +@@ -9,6 +9,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + #include "ptrace.h" + #include "breakpoint.h" +@@ -209,30 +211,46 @@ arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { + #define STWCX_INSTRUCTION 0x7c00012d + #define STDCX_INSTRUCTION 0x7c0001ad + #define BC_MASK 0xfc000000 +-#define BC_INSTRUCTION 0x40000000 ++#define BC_INSN 0x40000000 ++#define BRANCH_MASK 0xfc000000 ++ ++/* In plt.h. XXX make this official interface. */ ++int read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp); + + int + arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + int (*add_cb)(void *addr, void *data), + void *add_cb_data) + { +- void *addr = sbp->addr; +- debug(1, "pid=%d addr=%p", proc->pid, addr); ++ target_address_t ip = get_instruction_pointer(proc); ++ struct breakpoint *other = address2bpstruct(proc->leader, ip); ++ ++ debug(1, "arch_atomic_singlestep pid=%d addr=%p %s(%p)", ++ proc->pid, ip, breakpoint_name(sbp), sbp->addr); + + /* If the original instruction was lwarx/ldarx, we can't + * single-step over it, instead we have to execute the whole + * atomic block at once. */ + union { + uint32_t insn; +- char buf[4]; ++ char buf[BREAKPOINT_LENGTH]; + } u; +- memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH); ++ if (other != NULL) { ++ memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH); ++ } else if (read_target_4(proc, ip, &u.insn) < 0) { ++ fprintf(stderr, "couldn't read instruction at IP %p\n", ip); ++ /* Do the normal singlestep. */ ++ return 1; ++ } + + if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION + && (u.insn & LWARX_MASK) != LDARX_INSTRUCTION) + return 1; + ++ debug(1, "singlestep over atomic block at %p", ip); ++ + int insn_count; ++ target_address_t addr = ip; + for (insn_count = 0; ; ++insn_count) { + addr += 4; + unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); +@@ -245,27 +263,39 @@ arch_atomic_singlestep(struct Process *proc, Breakpoint *sbp, + insn = l; + #endif + +- /* If we hit a branch instruction, give up. The +- * computation could escape that way and we'd have to +- * treat that case specially. */ +- if ((insn & BC_MASK) == BC_INSTRUCTION) { +- debug(1, "pid=%d, found branch at %p, giving up", +- proc->pid, addr); +- return -1; ++ /* If a conditional branch is found, put a breakpoint ++ * in its destination address. */ ++ if ((insn & BRANCH_MASK) == BC_INSN) { ++ int immediate = ((insn & 0xfffc) ^ 0x8000) - 0x8000; ++ int absolute = insn & 2; ++ ++ /* XXX drop the following casts. */ ++ target_address_t branch_addr; ++ if (absolute) ++ branch_addr = (void *)(uintptr_t)immediate; ++ else ++ branch_addr = addr + (uintptr_t)immediate; ++ ++ debug(1, "pid=%d, branch in atomic block from %p to %p", ++ proc->pid, addr, branch_addr); ++ if (add_cb(branch_addr, add_cb_data) < 0) ++ return -1; + } + ++ /* Assume that the atomic sequence ends with a ++ * stwcx/stdcx instruction. */ + if ((insn & STWCX_MASK) == STWCX_INSTRUCTION + || (insn & STWCX_MASK) == STDCX_INSTRUCTION) { +- debug(1, "pid=%d, found end of atomic block at %p", +- proc->pid, addr); ++ debug(1, "pid=%d, found end of atomic block %p at %p", ++ proc->pid, ip, addr); + break; + } + + /* Arbitrary cut-off. If we didn't find the + * terminating instruction by now, just give up. */ + if (insn_count > 16) { +- debug(1, "pid=%d, couldn't find end of atomic block", +- proc->pid); ++ fprintf(stderr, "[%d] couldn't find end of atomic block" ++ " at %p\n", proc->pid, ip); + return -1; + } + } +diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c +index 3350117..b5123fe 100644 +--- a/sysdeps/linux-gnu/proc.c ++++ b/sysdeps/linux-gnu/proc.c +@@ -1,23 +1,24 @@ + #define _GNU_SOURCE /* For getline. */ + #include "config.h" + +-#include + #include ++#include ++#include ++#include ++#include ++#include + #include + #include + #include ++#include + #include + #include +-#include + #include +-#include +-#include +-#include +-#include +-#include + + #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 +@@ -77,27 +78,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 +@@ -113,13 +115,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 +@@ -135,15 +137,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) { +@@ -179,7 +181,8 @@ process_status(pid_t pid) + each_line_starting(file, "State:\t", &process_status_cb, &ret); + fclose(file); + if (ret == ps_invalid) +- error(0, errno, "process_status %d", pid); ++ fprintf(stderr, "process_status %d: %s", pid, ++ strerror(errno)); + } else + /* If the file is not present, the process presumably + * exited already. */ +@@ -242,51 +245,178 @@ process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) + return 0; + } + ++/* On native 64-bit system, we need to be careful when handling cross ++ * tracing. This select appropriate pointer depending on host and ++ * target architectures. XXX Really we should abstract this into the ++ * ABI object, as theorized about somewhere on pmachata/revamp ++ * branch. */ ++static void * ++select_32_64(struct Process *proc, void *p32, void *p64) ++{ ++ if (sizeof(long) == 4 || proc->mask_32bit) ++ return p32; ++ else ++ return p64; ++} ++ ++static int ++fetch_dyn64(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) ++{ ++ if (umovebytes(proc, *addr, ret, sizeof(*ret)) != sizeof(*ret)) ++ return -1; ++ *addr += sizeof(*ret); ++ return 0; ++} ++ + static int +-find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { +- int i = 0, done = 0; +- ElfW(Dyn) entry; ++fetch_dyn32(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) ++{ ++ Elf32_Dyn dyn; ++ if (umovebytes(proc, *addr, &dyn, sizeof(dyn)) != sizeof(dyn)) ++ return -1; ++ ++ *addr += sizeof(dyn); ++ ret->d_tag = dyn.d_tag; ++ ret->d_un.d_val = dyn.d_un.d_val; ++ ++ return 0; ++} + ++static int (* ++dyn_fetcher(struct Process *proc))(struct Process *, ++ target_address_t *, Elf64_Dyn *) ++{ ++ return select_32_64(proc, fetch_dyn32, fetch_dyn64); ++} ++ ++static int ++find_dynamic_entry_addr(struct Process *proc, target_address_t src_addr, ++ int d_tag, target_address_t *ret) ++{ + debug(DEBUG_FUNCTION, "find_dynamic_entry()"); + +- if (addr == NULL || pvAddr == NULL || d_tag < 0 || d_tag > DT_NUM) { ++ if (ret == NULL || src_addr == 0 || d_tag < 0 || d_tag > DT_NUM) + return -1; +- } + +- while ((!done) && (i < ELF_MAX_SEGMENTS) && +- (sizeof(entry) == umovebytes(proc, pvAddr, &entry, sizeof(entry))) && +- (entry.d_tag != DT_NULL)) { ++ int i = 0; ++ while (1) { ++ Elf64_Dyn entry; ++ if (dyn_fetcher(proc)(proc, &src_addr, &entry) < 0 ++ || entry.d_tag == DT_NULL ++ || i++ > 100) { /* Arbitrary cut-off so that we ++ * don't loop forever if the ++ * binary is corrupted. */ ++ debug(2, "Couldn't find address for dtag!"); ++ return -1; ++ } ++ + if (entry.d_tag == d_tag) { +- done = 1; +- *addr = (void *)entry.d_un.d_val; ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ *ret = (target_address_t)(uintptr_t)entry.d_un.d_val; ++ debug(2, "found address: %p in dtag %d", *ret, d_tag); ++ return 0; + } +- pvAddr += sizeof(entry); +- i++; + } ++} + +- if (done) { +- debug(2, "found address: 0x%p in dtag %d\n", *addr, d_tag); +- return 0; ++/* Our own type for representing 32-bit linkmap. We can't rely on the ++ * definition in link.h, because that's only accurate for our host ++ * architecture, not for target architecture (where the traced process ++ * runs). */ ++#define LT_LINK_MAP(BITS) \ ++ { \ ++ Elf##BITS##_Addr l_addr; \ ++ Elf##BITS##_Addr l_name; \ ++ Elf##BITS##_Addr l_ld; \ ++ Elf##BITS##_Addr l_next; \ ++ Elf##BITS##_Addr l_prev; \ + } +- else { +- debug(2, "Couldn't address for dtag!\n"); ++struct lt_link_map_32 LT_LINK_MAP(32); ++struct lt_link_map_64 LT_LINK_MAP(64); ++ ++static int ++fetch_lm64(struct Process *proc, target_address_t addr, ++ struct lt_link_map_64 *ret) ++{ ++ if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) ++ return -1; ++ return 0; ++} ++ ++static int ++fetch_lm32(struct Process *proc, target_address_t addr, ++ struct lt_link_map_64 *ret) ++{ ++ struct lt_link_map_32 lm; ++ if (umovebytes(proc, addr, &lm, sizeof(lm)) != sizeof(lm)) + return -1; ++ ++ ret->l_addr = lm.l_addr; ++ ret->l_name = lm.l_name; ++ ret->l_ld = lm.l_ld; ++ ret->l_next = lm.l_next; ++ ret->l_prev = lm.l_prev; ++ ++ return 0; ++} ++ ++static int (* ++lm_fetcher(struct Process *proc))(struct Process *, ++ target_address_t, struct lt_link_map_64 *) ++{ ++ return select_32_64(proc, fetch_lm32, fetch_lm64); ++} ++ ++/* The same as above holds for struct r_debug. */ ++#define LT_R_DEBUG(BITS) \ ++ { \ ++ int r_version; \ ++ Elf##BITS##_Addr r_map; \ ++ Elf##BITS##_Addr r_brk; \ ++ int r_state; \ ++ Elf##BITS##_Addr r_ldbase; \ + } ++ ++struct lt_r_debug_32 LT_R_DEBUG(32); ++struct lt_r_debug_64 LT_R_DEBUG(64); ++ ++static int ++fetch_rd64(struct Process *proc, target_address_t addr, ++ struct lt_r_debug_64 *ret) ++{ ++ if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) ++ return -1; ++ return 0; + } + +-struct cb_data { +- const char *lib_name; +- struct ltelf *lte; +- ElfW(Addr) addr; +- Process *proc; +-}; ++static int ++fetch_rd32(struct Process *proc, target_address_t addr, ++ struct lt_r_debug_64 *ret) ++{ ++ struct lt_r_debug_32 rd; ++ if (umovebytes(proc, addr, &rd, sizeof(rd)) != sizeof(rd)) ++ return -1; + +-static void +-crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), struct cb_data *data) { +- struct link_map rlm; +- char lib_name[BUFSIZ]; +- struct link_map *lm = NULL; ++ ret->r_version = rd.r_version; ++ ret->r_map = rd.r_map; ++ ret->r_brk = rd.r_brk; ++ ret->r_state = rd.r_state; ++ ret->r_ldbase = rd.r_ldbase; ++ ++ return 0; ++} + ++static int (* ++rdebug_fetcher(struct Process *proc))(struct Process *, ++ target_address_t, struct lt_r_debug_64 *) ++{ ++ return select_32_64(proc, fetch_rd32, fetch_rd64); ++} ++ ++static void ++crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) ++{ + debug (DEBUG_FUNCTION, "crawl_linkmap()"); + + if (!dbg || !dbg->r_map) { +@@ -294,201 +424,234 @@ crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), stru + return; + } + +- lm = dbg->r_map; ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ target_address_t addr = (target_address_t)(uintptr_t)dbg->r_map; + +- while (lm) { +- if (umovebytes(proc, lm, &rlm, sizeof(rlm)) != sizeof(rlm)) { +- debug(2, "Unable to read link map\n"); ++ while (addr != 0) { ++ struct lt_link_map_64 rlm; ++ if (lm_fetcher(proc)(proc, addr, &rlm) < 0) { ++ debug(2, "Unable to read link map"); + return; + } + +- lm = rlm.l_next; +- if (rlm.l_name == NULL) { +- debug(2, "Invalid library name referenced in dynamic linker map\n"); ++ target_address_t key = addr; ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ addr = (target_address_t)(uintptr_t)rlm.l_next; ++ if (rlm.l_name == 0) { ++ debug(2, "Name of mapped library is NULL"); + return; + } + +- umovebytes(proc, rlm.l_name, lib_name, sizeof(lib_name)); ++ char lib_name[BUFSIZ]; ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ umovebytes(proc, (target_address_t)(uintptr_t)rlm.l_name, ++ lib_name, sizeof(lib_name)); + +- if (lib_name[0] == '\0') { +- debug(2, "Library name is an empty string"); ++ 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); ++ /* Do we have that library already? */ ++ if (proc_each_library(proc, NULL, library_with_key_cb, &key)) ++ continue; ++ ++ struct library *lib = malloc(sizeof(*lib)); ++ if (lib == NULL) { ++ fail: ++ if (lib != NULL) ++ library_destroy(lib); ++ fprintf(stderr, "Couldn't load ELF object %s: %s\n", ++ lib_name, strerror(errno)); ++ continue; + } ++ library_init(lib, LT_LIBTYPE_DSO); ++ ++ if (ltelf_read_library(lib, proc, lib_name, rlm.l_addr) < 0) ++ goto fail; ++ ++ lib->key = key; ++ proc_add_library(proc, lib); + } + return; + } + +-static struct r_debug * +-load_debug_struct(Process *proc) { +- struct r_debug *rdbg = NULL; ++/* A struct stored at proc->debug. */ ++struct debug_struct ++{ ++ target_address_t debug_addr; ++ int state; ++}; + ++static int ++load_debug_struct(struct Process *proc, struct lt_r_debug_64 *ret) ++{ + debug(DEBUG_FUNCTION, "load_debug_struct"); + +- rdbg = malloc(sizeof(*rdbg)); +- if (!rdbg) { +- return NULL; +- } ++ struct debug_struct *debug = proc->debug; + +- if (umovebytes(proc, proc->debug, rdbg, sizeof(*rdbg)) != sizeof(*rdbg)) { ++ if (rdebug_fetcher(proc)(proc, debug->debug_addr, ret) < 0) { + debug(2, "This process does not have a debug structure!\n"); +- free(rdbg); +- return NULL; ++ return -1; + } + +- return rdbg; ++ return 0; + } + + 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) { +- struct r_debug *dbg = NULL; +- struct cb_data data; +- ++rdebug_bp_on_hit(struct breakpoint *bp, struct Process *proc) ++{ + debug(DEBUG_FUNCTION, "arch_check_dbg"); + +- if (!(dbg = load_debug_struct(proc))) { ++ struct lt_r_debug_64 rdbg; ++ if (load_debug_struct(proc, &rdbg) < 0) { + debug(2, "Unable to load debug structure!"); + return; + } + +- if (dbg->r_state == RT_CONSISTENT) { ++ struct debug_struct *debug = proc->debug; ++ if (rdbg.r_state == RT_CONSISTENT) { + debug(2, "Linkmap is now consistent"); +- if (proc->debug_state == RT_ADD) { ++ if (debug->state == RT_ADD) { + debug(2, "Adding DSO to linkmap"); +- data.proc = proc; +- crawl_linkmap(proc, dbg, linkmap_add_cb, &data); +- } else if (proc->debug_state == RT_DELETE) { ++ //data.proc = proc; ++ crawl_linkmap(proc, &rdbg); ++ //&data); ++ } else if (debug->state == RT_DELETE) { + debug(2, "Removing DSO from linkmap"); + } else { + debug(2, "Unexpected debug state!"); + } + } + +- proc->debug_state = dbg->r_state; +- +- return; ++ debug->state = rdbg.r_state; + } + +-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"); ++int ++linkmap_init(struct Process *proc, target_address_t dyn_addr) ++{ ++ debug(DEBUG_FUNCTION, "linkmap_init()"); + +- if (!data) { +- debug(2, "No callback data"); +- return; ++ struct debug_struct *debug = malloc(sizeof(*debug)); ++ if (debug == NULL) { ++ fprintf(stderr, "couldn't allocate debug struct: %s\n", ++ strerror(errno)); ++ fail: ++ proc->debug = NULL; ++ free(debug); ++ return -1; + } ++ proc->debug = debug; + +- lib_name = hook_data->lib_name; +- addr = hook_data->addr; +- lte = hook_data->lte; +- +- if (library_num < MAX_LIBRARIES) { +- library[library_num++] = strdup(lib_name); +- lte[library_num].base_addr = addr; ++ if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, ++ &debug->debug_addr) == -1) { ++ debug(2, "Couldn't find debug structure!"); ++ goto fail; + } +- else { +- fprintf (stderr, "MAX LIBS REACHED\n"); +- exit(EXIT_FAILURE); ++ ++ int status; ++ struct lt_r_debug_64 rdbg; ++ if ((status = load_debug_struct(proc, &rdbg)) < 0) { ++ debug(2, "No debug structure or no memory to allocate one!"); ++ return status; + } +-} + +-int +-linkmap_init(Process *proc, struct ltelf *lte) { +- void *dbg_addr = NULL, *dyn_addr = GELF_ADDR_CAST(lte->dyn_addr); +- struct r_debug *rdbg = NULL; +- struct cb_data data; ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ target_address_t addr = (target_address_t)(uintptr_t)rdbg.r_brk; ++ if (arch_translate_address_dyn(proc, addr, &addr) < 0) ++ goto fail; + +- debug(DEBUG_FUNCTION, "linkmap_init()"); ++ struct breakpoint *rdebug_bp = insert_breakpoint(proc, addr, NULL); ++ static struct bp_callbacks rdebug_callbacks = { ++ .on_hit = rdebug_bp_on_hit, ++ }; ++ rdebug_bp->cbs = &rdebug_callbacks; + +- if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, &dbg_addr) == -1) { +- debug(2, "Couldn't find debug structure!"); +- return -1; +- } ++ crawl_linkmap(proc, &rdbg); + +- proc->debug = dbg_addr; ++ return 0; ++} + +- if (!(rdbg = load_debug_struct(proc))) { +- debug(2, "No debug structure or no memory to allocate one!"); ++static int ++fetch_auxv64_entry(int fd, Elf64_auxv_t *ret) ++{ ++ /* Reaching EOF is as much problem as not reading whole ++ * entry. */ ++ return read(fd, ret, sizeof(*ret)) == sizeof(*ret) ? 0 : -1; ++} ++ ++static int ++fetch_auxv32_entry(int fd, Elf64_auxv_t *ret) ++{ ++ Elf32_auxv_t auxv; ++ if (read(fd, &auxv, sizeof(auxv)) != sizeof(auxv)) + return -1; ++ ++ ret->a_type = auxv.a_type; ++ ret->a_un.a_val = auxv.a_un.a_val; ++ return 0; ++} ++ ++static int (* ++auxv_fetcher(struct Process *proc))(int, Elf64_auxv_t *) ++{ ++ return select_32_64(proc, fetch_auxv32_entry, fetch_auxv64_entry); ++} ++ ++int ++process_get_entry(struct Process *proc, ++ target_address_t *entryp, ++ target_address_t *interp_biasp) ++{ ++ PROC_PID_FILE(fn, "/proc/%d/auxv", proc->pid); ++ int fd = open(fn, O_RDONLY); ++ if (fd == -1) { ++ fail: ++ fprintf(stderr, "couldn't read %s: %s", fn, strerror(errno)); ++ done: ++ if (fd != -1) ++ close(fd); ++ return fd == -1 ? -1 : 0; + } + +- data.lte = lte; ++ target_address_t at_entry = 0; ++ target_address_t at_bias = 0; ++ while (1) { ++ Elf64_auxv_t entry; ++ if (auxv_fetcher(proc)(fd, &entry) < 0) ++ goto fail; ++ ++ switch (entry.a_type) { ++ case AT_BASE: ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ at_bias = (target_address_t) ++ (uintptr_t)entry.a_un.a_val; ++ continue; + +- add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0); +- insert_breakpoint(proc, sym2addr(proc, library_symbols), +- library_symbols, 1); ++ case AT_ENTRY: ++ /* XXX The double cast should be removed when ++ * target_address_t becomes integral type. */ ++ at_entry = (target_address_t) ++ (uintptr_t)entry.a_un.a_val; ++ default: ++ continue; + +- crawl_linkmap(proc, rdbg, hook_libdl_cb, &data); ++ case AT_NULL: ++ break; ++ } ++ break; ++ } + +- free(rdbg); +- return 0; ++ *entryp = at_entry; ++ *interp_biasp = at_bias; ++ goto done; + } + + int +diff --git a/sysdeps/linux-gnu/s390/plt.c b/sysdeps/linux-gnu/s390/plt.c +index 85a1dd1..754d270 100644 +--- a/sysdeps/linux-gnu/s390/plt.c ++++ b/sysdeps/linux-gnu/s390/plt.c +@@ -1,4 +1,5 @@ + #include ++#include "proc.h" + #include "common.h" + + GElf_Addr +diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c +index 169893e..a45dd9b 100644 +--- a/sysdeps/linux-gnu/s390/regs.c ++++ b/sysdeps/linux-gnu/s390/regs.c +@@ -9,6 +9,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c +index 63935de..8c08f1f 100644 +--- a/sysdeps/linux-gnu/s390/trace.c ++++ b/sysdeps/linux-gnu/s390/trace.c +@@ -17,6 +17,7 @@ + #include + #include + ++#include "proc.h" + #include "common.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +diff --git a/sysdeps/linux-gnu/sparc/plt.c b/sysdeps/linux-gnu/sparc/plt.c +index f9e6d80..658e549 100644 +--- a/sysdeps/linux-gnu/sparc/plt.c ++++ b/sysdeps/linux-gnu/sparc/plt.c +@@ -1,4 +1,5 @@ + #include ++#include "proc.h" + #include "common.h" + + GElf_Addr +diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c +index 49d2729..d7ee114 100644 +--- a/sysdeps/linux-gnu/sparc/regs.c ++++ b/sysdeps/linux-gnu/sparc/regs.c +@@ -2,6 +2,7 @@ + + #include + #include "ptrace.h" ++#include "proc.h" + #include "common.h" + + void * +diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c +index 7f05b55..e05c4d3 100644 +--- a/sysdeps/linux-gnu/sparc/trace.c ++++ b/sysdeps/linux-gnu/sparc/trace.c +@@ -6,6 +6,7 @@ + #include + #include + #include "ptrace.h" ++#include "proc.h" + #include "common.h" + + void +diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c +index fe64a28..d5c5262 100644 +--- a/sysdeps/linux-gnu/trace.c ++++ b/sysdeps/linux-gnu/trace.c +@@ -17,6 +17,8 @@ + #include "common.h" + #include "config.h" + #include "breakpoint.h" ++#include "proc.h" ++#include "linux-gnu/trace.h" + + #include "config.h" + #ifdef HAVE_LIBSELINUX +@@ -107,26 +109,21 @@ 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 ++ * forks when we can't trace it yet. Here we wait for kernel to + * prepare the process. */ +-void ++int + 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); ++ /* man ptrace: PTRACE_ATTACH attaches to the process specified ++ in pid. The child is sent a SIGSTOP, but will not ++ necessarily have stopped by the completion of this call; ++ use wait() to wait for the child to stop. */ ++ if (waitpid(pid, NULL, __WALL) != pid) { ++ perror ("trace_pid: waitpid"); ++ return -1; + } + +- 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"); ++ return 0; + } + + int +@@ -139,23 +136,16 @@ trace_pid(pid_t pid) + if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) + return -1; + +- /* man ptrace: PTRACE_ATTACH attaches to the process specified +- in pid. The child is sent a SIGSTOP, but will not +- necessarily have stopped by the completion of this call; +- use wait() to wait for the child to stop. */ +- if (waitpid (pid, NULL, __WALL) != pid) { +- perror ("trace_pid: waitpid"); +- return -1; +- } +- +- return 0; ++ return wait_for_proc(pid); + } + + void +-trace_set_options(Process *proc, pid_t pid) { ++trace_set_options(struct Process *proc) ++{ + if (proc->tracesysgood & 0x80) + return; + ++ pid_t pid = proc->pid; + debug(DEBUG_PROCESS, "trace_set_options: pid=%d", pid); + + long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | +@@ -212,67 +202,6 @@ continue_process(pid_t pid) + "putting off the continue, events in que."); + } + +-/** +- * This is used for bookkeeping related to PIDs that the event +- * handlers work with. +- */ +-struct pid_task { +- pid_t pid; /* This may be 0 for tasks that exited +- * mid-handling. */ +- int sigstopped : 1; +- int got_event : 1; +- int delivered : 1; +- int vforked : 1; +- int sysret : 1; +-} * pids; +- +-struct pid_set { +- struct pid_task * tasks; +- size_t count; +- size_t alloc; +-}; +- +-/** +- * Breakpoint re-enablement. When we hit a breakpoint, we must +- * disable it, single-step, and re-enable it. That single-step can be +- * done only by one task in a task group, while others are stopped, +- * otherwise the processes would race for who sees the breakpoint +- * disabled and who doesn't. The following is to keep track of it +- * all. +- */ +-struct process_stopping_handler +-{ +- Event_Handler super; +- +- /* The task that is doing the re-enablement. */ +- Process * task_enabling_breakpoint; +- +- /* The pointer being re-enabled. */ +- struct breakpoint *breakpoint_being_enabled; +- +- /* Artificial atomic skip breakpoint, if any needed. */ +- void *atomic_skip_bp_addr; +- +- enum { +- /* We are waiting for everyone to land in t/T. */ +- psh_stopping = 0, +- +- /* We are doing the PTRACE_SINGLESTEP. */ +- psh_singlestep, +- +- /* We are waiting for all the SIGSTOPs to arrive so +- * that we can sink them. */ +- psh_sinking, +- +- /* This is for tracking the ugly workaround. */ +- psh_ugly_workaround, +- } state; +- +- int exiting; +- +- struct pid_set pids; +-}; +- + static struct pid_task * + get_task_info(struct pid_set * pids, pid_t pid) + { +@@ -303,8 +232,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) +@@ -318,48 +247,48 @@ 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(Event_Handler * super, Event * event); ++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 +-is_vfork_parent(Process * task) ++is_vfork_parent(struct Process *task) + { +- return each_task(task->leader, &task_vforked, NULL) != NULL; ++ return each_task(task->leader, NULL, &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; +@@ -372,24 +301,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; + } + +@@ -400,7 +329,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) { +@@ -410,7 +339,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 +@@ -426,7 +355,7 @@ ugly_workaround(Process * proc) + if (sbp != NULL) + enable_breakpoint(proc, sbp); + else +- insert_breakpoint(proc, ip, NULL, 1); ++ insert_breakpoint(proc, ip, NULL); + ptrace(PTRACE_CONT, proc->pid, 0, 0); + } + +@@ -444,9 +373,21 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) + continue_process(self->pids.tasks[i].pid); + continue_process(self->task_enabling_breakpoint->pid); + destroy_event_handler(leader); +- } else { ++ } ++ ++ if (self->exiting) { ++ ugly_workaround: + self->state = psh_ugly_workaround; + ugly_workaround(self->task_enabling_breakpoint); ++ } else { ++ switch ((self->ugly_workaround_p)(self)) { ++ case CBS_FAIL: ++ /* xxx handle me */ ++ case CBS_STOP: ++ break; ++ case CBS_CONT: ++ goto ugly_workaround; ++ } + } + } + +@@ -464,21 +405,28 @@ 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 enum callback_status ++retract_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) ++{ ++ breakpoint_on_retract(bp, proc); ++ return CBS_CONT; + } + + static void +@@ -486,6 +434,7 @@ detach_process(Process * leader) + { + each_qd_event(&undo_breakpoint, leader); + disable_all_breakpoints(leader); ++ proc_each_breakpoint(leader, NULL, retract_breakpoint_cb, NULL); + + /* Now untrace the process, if it was attached to by -p. */ + struct opt_p_t * it; +@@ -494,11 +443,11 @@ detach_process(Process * leader) + if (proc == NULL) + continue; + if (proc->leader == leader) { +- each_task(leader, &untrace_task, NULL); ++ each_task(leader, NULL, &untrace_task, NULL); + break; + } + } +- each_task(leader, &remove_task, leader); ++ each_task(leader, NULL, &remove_task, leader); + destroy_event_handler(leader); + remove_task(leader, NULL); + } +@@ -632,19 +581,56 @@ arch_atomic_singlestep(struct Process *proc, Breakpoint *sbp, + } + #endif + ++static Event *process_stopping_on_event(struct event_handler *super, ++ Event *event); ++ ++static void ++remove_atomic_breakpoints(struct Process *proc) ++{ ++ struct process_stopping_handler *self ++ = (void *)proc->leader->event_handler; ++ assert(self != NULL); ++ assert(self->super.on_event == process_stopping_on_event); ++ ++ int ct = sizeof(self->atomic_skip_bp_addrs) ++ / sizeof(*self->atomic_skip_bp_addrs); ++ int i; ++ for (i = 0; i < ct; ++i) ++ if (self->atomic_skip_bp_addrs[i] != 0) { ++ delete_breakpoint(proc, self->atomic_skip_bp_addrs[i]); ++ self->atomic_skip_bp_addrs[i] = 0; ++ } ++} ++ ++static void ++atomic_singlestep_bp_on_hit(struct breakpoint *bp, struct Process *proc) ++{ ++ remove_atomic_breakpoints(proc); ++} ++ + static int + atomic_singlestep_add_bp(void *addr, void *data) + { + struct process_stopping_handler *self = data; + struct Process *proc = self->task_enabling_breakpoint; + +- /* Only support single address as of now. */ +- assert(self->atomic_skip_bp_addr == NULL); +- +- self->atomic_skip_bp_addr = addr + 4; +- insert_breakpoint(proc->leader, self->atomic_skip_bp_addr, NULL, 1); ++ int ct = sizeof(self->atomic_skip_bp_addrs) ++ / sizeof(*self->atomic_skip_bp_addrs); ++ int i; ++ for (i = 0; i < ct; ++i) ++ if (self->atomic_skip_bp_addrs[i] == 0) { ++ self->atomic_skip_bp_addrs[i] = addr; ++ static struct bp_callbacks cbs = { ++ .on_hit = atomic_singlestep_bp_on_hit, ++ }; ++ struct breakpoint *bp ++ = insert_breakpoint(proc, addr, NULL); ++ breakpoint_set_callbacks(bp, &cbs); ++ return 0; ++ } + +- return 0; ++ assert(!"Too many atomic singlestep breakpoints!"); ++ abort(); + } + + static int +@@ -670,30 +656,68 @@ singlestep(struct process_stopping_handler *self) *** + } + + static void +-post_singlestep(struct process_stopping_handler *self, Event **eventp) ++post_singlestep(struct process_stopping_handler *self, ++ struct Event **eventp) + { + continue_for_sigstop_delivery(&self->pids); + +- if ((*eventp)->type == EVENT_BREAKPOINT) ++ if (*eventp != NULL && (*eventp)->type == EVENT_BREAKPOINT) + *eventp = NULL; // handled + +- if (self->atomic_skip_bp_addr != 0) +- delete_breakpoint(self->task_enabling_breakpoint->leader, +- self->atomic_skip_bp_addr); ++ struct Process *proc = self->task_enabling_breakpoint; + ++ remove_atomic_breakpoints(proc); + self->breakpoint_being_enabled = NULL; + } + + static void +-singlestep_error(struct process_stopping_handler *self, Event **eventp) ++singlestep_error(struct process_stopping_handler *self) + { + struct Process *teb = self->task_enabling_breakpoint; + struct breakpoint *sbp = self->breakpoint_being_enabled; +- fprintf(stderr, "%d couldn't singlestep over %s (%p)\n", ++ fprintf(stderr, "%d couldn't continue when handling %s (%p) at %p\n", + teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL, +- sbp->addr); ++ sbp->addr, get_instruction_pointer(teb)); + delete_breakpoint(teb->leader, sbp->addr); +- post_singlestep(self, eventp); ++} ++ ++static void ++pt_continue(struct process_stopping_handler *self) ++{ ++ struct Process *teb = self->task_enabling_breakpoint; ++ debug(1, "PTRACE_CONT"); ++ ptrace(PTRACE_CONT, teb->pid, 0, 0); ++} ++ ++static void ++pt_singlestep(struct process_stopping_handler *self) ++{ ++ if (singlestep(self) < 0) ++ singlestep_error(self); ++} ++ ++static void ++disable_and(struct process_stopping_handler *self, ++ void (*do_this)(struct process_stopping_handler *self)) ++{ ++ struct Process *teb = self->task_enabling_breakpoint; ++ debug(DEBUG_PROCESS, "all stopped, now singlestep/cont %d", teb->pid); ++ if (self->breakpoint_being_enabled->enabled) ++ disable_breakpoint(teb, self->breakpoint_being_enabled); ++ (do_this)(self); ++ self->state = psh_singlestep; ++} ++ ++void ++linux_ptrace_disable_and_singlestep(struct process_stopping_handler *self) ++{ ++ disable_and(self, &pt_singlestep); ++} ++ ++void ++linux_ptrace_disable_and_continue(struct process_stopping_handler *self) ++{ ++ disable_and(self, &pt_continue); + } + + /* This event handler is installed when we are in the process of +@@ -703,16 +727,15 @@ singlestep_error(struct process_stopping_handler *self, Event **eventp) + * happens, we let the re-enablement thread to PTRACE_SINGLESTEP, + * re-enable, and continue everyone. */ + static Event * +-process_stopping_on_event(Event_Handler * super, Event * event) ++process_stopping_on_event(struct event_handler *super, Event *event) + { + struct process_stopping_handler * self = (void *)super; + Process * task = event->proc; + Process * leader = task->leader; +- struct breakpoint *sbp = self->breakpoint_being_enabled; + Process * teb = self->task_enabling_breakpoint; + + debug(DEBUG_PROCESS, +- "pid %d; event type %d; state %d", ++ "process_stopping_on_event: pid %d; event type %d; state %d", + task->pid, event->type, self->state); + + struct pid_task * task_info = get_task_info(&self->pids, task->pid); +@@ -741,17 +764,10 @@ process_stopping_on_event(Event_Handler * super, Event * event) + switch (state) { + case psh_stopping: + /* If everyone is stopped, singlestep. */ +- if (each_task(leader, &task_blocked, &self->pids) == NULL) { +- debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d", +- teb->pid); +- if (sbp->enabled) +- disable_breakpoint(teb, sbp); +- if (singlestep(self) < 0) { +- singlestep_error(self, &event); +- goto psh_sinking; +- } +- +- self->state = state = psh_singlestep; ++ if (each_task(leader, NULL, &task_blocked, ++ &self->pids) == NULL) { ++ (self->on_all_stopped)(self); ++ state = self->state; + } + break; + +@@ -760,18 +776,47 @@ process_stopping_on_event(Event_Handler * super, Event * event) + * have now stepped, and can re-enable the breakpoint. */ + if (event != NULL && task == teb) { + +- /* This is not the singlestep that we are waiting for. */ ++ /* If this was caused by a real breakpoint, as ++ * opposed to a singlestep, assume that it's ++ * an artificial breakpoint installed for some ++ * reason for the re-enablement. In that case ++ * handle it. */ ++ if (event->type == EVENT_BREAKPOINT) { ++ target_address_t ip ++ = get_instruction_pointer(task); ++ struct breakpoint *other ++ = address2bpstruct(leader, ip); ++ if (other != NULL) ++ breakpoint_on_hit(other, task); ++ } ++ ++ /* If we got SIGNAL instead of BREAKPOINT, ++ * then this is not singlestep at all. */ + if (event->type == EVENT_SIGNAL) { ++ do_singlestep: + if (singlestep(self) < 0) { +- singlestep_error(self, &event); ++ singlestep_error(self); ++ post_singlestep(self, &event); + goto psh_sinking; + } + break; ++ } else { ++ switch ((self->keep_stepping_p)(self)) { ++ case CBS_FAIL: ++ /* XXX handle me */ ++ case CBS_STOP: ++ break; ++ case CBS_CONT: ++ /* Sink singlestep event. */ ++ if (event->type == EVENT_BREAKPOINT) ++ event = NULL; ++ goto do_singlestep; ++ } + } + +- /* Essentially we don't care what event caused +- * the thread to stop. We can do the +- * re-enablement now. */ ++ /* Re-enable the breakpoint that we are ++ * stepping over. */ ++ struct breakpoint *sbp = self->breakpoint_being_enabled; + if (sbp->enabled) + enable_breakpoint(teb, sbp); + +@@ -812,52 +857,89 @@ process_stopping_on_event(Event_Handler * super, Event * event) + } + + static void +-process_stopping_destroy(Event_Handler * super) ++process_stopping_destroy(struct event_handler *super) + { + struct process_stopping_handler * self = (void *)super; + free(self->pids.tasks); + } + ++static enum callback_status ++no(struct process_stopping_handler *self) ++{ ++ return CBS_STOP; ++} ++ ++int ++process_install_stopping_handler(struct Process *proc, struct breakpoint *sbp, ++ void (*as)(struct process_stopping_handler *), ++ enum callback_status (*ks) ++ (struct process_stopping_handler *), ++ enum callback_status (*uw) ++ (struct process_stopping_handler *)) ++{ ++ debug(DEBUG_FUNCTION, ++ "process_install_stopping_handler: pid=%d", proc->pid); ++ ++ struct process_stopping_handler *handler = calloc(sizeof(*handler), 1); ++ if (handler == NULL) ++ return -1; ++ ++ if (as == NULL) ++ as = &linux_ptrace_disable_and_singlestep; ++ if (ks == NULL) ++ ks = &no; ++ if (uw == NULL) ++ uw = &no; ++ ++ handler->super.on_event = process_stopping_on_event; ++ handler->super.destroy = process_stopping_destroy; ++ handler->task_enabling_breakpoint = proc; ++ handler->breakpoint_being_enabled = sbp; ++ handler->on_all_stopped = as; ++ handler->keep_stepping_p = ks; ++ handler->ugly_workaround_p = uw; ++ ++ install_event_handler(proc->leader, &handler->super); ++ ++ if (each_task(proc->leader, NULL, &send_sigstop, ++ &handler->pids) != NULL) { ++ destroy_event_handler(proc); ++ return -1; ++ } ++ ++ /* And deliver the first fake event, in case all the ++ * conditions are already fulfilled. */ ++ Event ev = { ++ .type = EVENT_NONE, ++ .proc = proc, ++ }; ++ process_stopping_on_event(&handler->super, &ev); ++ ++ return 0; ++} ++ + void + continue_after_breakpoint(Process *proc, struct breakpoint *sbp) + { ++ debug(DEBUG_PROCESS, ++ "continue_after_breakpoint: pid=%d, addr=%p", ++ proc->pid, sbp->addr); ++ + set_instruction_pointer(proc, sbp->addr); ++ + if (sbp->enabled == 0) { + continue_process(proc->pid); + } else { +- debug(DEBUG_PROCESS, +- "continue_after_breakpoint: pid=%d, addr=%p", +- proc->pid, sbp->addr); + #if defined __sparc__ || defined __ia64___ || defined __mips__ + /* we don't want to singlestep here */ + continue_process(proc->pid); + #else +- struct process_stopping_handler * handler +- = calloc(sizeof(*handler), 1); +- if (handler == NULL) { +- perror("malloc breakpoint disable handler"); +- fatal: ++ if (process_install_stopping_handler ++ (proc, sbp, NULL, NULL, NULL) < 0) { ++ perror("process_stopping_handler_create"); + /* Carry on not bothering to re-enable. */ + continue_process(proc->pid); +- return; + } +- +- handler->super.on_event = process_stopping_on_event; +- handler->super.destroy = process_stopping_destroy; +- handler->task_enabling_breakpoint = proc; +- handler->breakpoint_being_enabled = sbp; +- install_event_handler(proc->leader, &handler->super); +- +- if (each_task(proc->leader, &send_sigstop, +- &handler->pids) != NULL) +- goto fatal; +- +- /* And deliver the first fake event, in case all the +- * conditions are already fulfilled. */ +- Event ev; +- ev.type = EVENT_NONE; +- ev.proc = proc; +- process_stopping_on_event(&handler->super, &ev); + #endif + } + } +@@ -872,18 +954,20 @@ continue_after_breakpoint(Process *proc, struct breakpoint *sbp) + */ + struct ltrace_exiting_handler + { +- Event_Handler super; ++ struct event_handler super; + struct pid_set pids; + }; + + static Event * +-ltrace_exiting_on_event(Event_Handler * super, Event * event) ++ltrace_exiting_on_event(struct event_handler *super, Event *event) + { + struct ltrace_exiting_handler * self = (void *)super; + Process * task = event->proc; + Process * leader = task->leader; + +- debug(DEBUG_PROCESS, "pid %d; event type %d", task->pid, event->type); ++ debug(DEBUG_PROCESS, ++ "ltrace_exiting_on_event: pid %d; event type %d", ++ task->pid, event->type); + + struct pid_task * task_info = get_task_info(&self->pids, task->pid); + handle_stopping_event(task_info, &event); +@@ -904,7 +988,7 @@ ltrace_exiting_on_event(Event_Handler * super, Event * event) + } + + static void +-ltrace_exiting_destroy(Event_Handler * super) ++ltrace_exiting_destroy(struct event_handler *super) + { + struct ltrace_exiting_handler * self = (void *)super; + free(self->pids.tasks); +@@ -947,7 +1031,7 @@ ltrace_exiting_install_handler(Process * proc) + handler->super.destroy = ltrace_exiting_destroy; + install_event_handler(proc->leader, &handler->super); + +- if (each_task(proc->leader, &send_sigstop, ++ if (each_task(proc->leader, NULL, &send_sigstop, + &handler->pids) != NULL) + goto fatal; + +@@ -975,13 +1059,17 @@ ltrace_exiting_install_handler(Process * proc) + + struct process_vfork_handler + { +- Event_Handler super; ++ struct event_handler super; + void * bp_addr; + }; + + static Event * +-process_vfork_on_event(Event_Handler * super, Event * event) ++process_vfork_on_event(struct event_handler *super, Event *event) + { ++ debug(DEBUG_PROCESS, ++ "process_vfork_on_event: pid %d; event type %d", ++ event->proc->pid, event->type); ++ + struct process_vfork_handler * self = (void *)super; + struct breakpoint *sbp; + assert(self != NULL); +@@ -989,7 +1077,7 @@ process_vfork_on_event(Event_Handler * super, Event * event) + switch (event->type) { + case EVENT_BREAKPOINT: + /* Remember the vfork return breakpoint. */ +- if (self->bp_addr == NULL) ++ if (self->bp_addr == 0) + self->bp_addr = event->e_un.brk_addr; + break; + +@@ -998,13 +1086,15 @@ process_vfork_on_event(Event_Handler * super, Event * event) + case EVENT_EXEC: + /* Smuggle back in the vfork return breakpoint, so + * that our parent can trip over it once again. */ +- if (self->bp_addr != NULL) { ++ if (self->bp_addr != 0) { + sbp = dict_find_entry(event->proc->leader->breakpoints, + self->bp_addr); + if (sbp != NULL) +- insert_breakpoint(event->proc->parent, +- self->bp_addr, +- sbp->libsym, 1); ++ assert(sbp->libsym == NULL); ++ /* We don't mind failing that, it's not a big ++ * deal to not display one extra vfork return. */ ++ insert_breakpoint(event->proc->parent, ++ self->bp_addr, NULL); + } + + continue_process(event->proc->parent->pid); +diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h +new file mode 100644 +index 0000000..0f40709 +--- /dev/null ++++ b/sysdeps/linux-gnu/trace.h +@@ -0,0 +1,119 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * 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 _LTRACE_LINUX_TRACE_H_ ++#define _LTRACE_LINUX_TRACE_H_ ++ ++/* This publishes some Linux-specific data structures used for process ++ * handling. */ ++ ++/** ++ * This is used for bookkeeping related to PIDs that the event ++ * handlers work with. ++ */ ++struct pid_task { ++ pid_t pid; /* This may be 0 for tasks that exited ++ * mid-handling. */ ++ int sigstopped : 1; ++ int got_event : 1; ++ int delivered : 1; ++ int vforked : 1; ++ int sysret : 1; ++}; ++ ++struct pid_set { ++ struct pid_task *tasks; ++ size_t count; ++ size_t alloc; ++}; ++ ++/** ++ * Breakpoint re-enablement. When we hit a breakpoint, we must ++ * disable it, single-step, and re-enable it. That single-step can be ++ * done only by one task in a task group, while others are stopped, ++ * otherwise the processes would race for who sees the breakpoint ++ * disabled and who doesn't. The following is to keep track of it ++ * all. ++ */ ++struct process_stopping_handler ++{ ++ struct event_handler super; ++ ++ /* The task that is doing the re-enablement. */ ++ struct Process *task_enabling_breakpoint; ++ ++ /* The pointer being re-enabled. */ ++ struct breakpoint *breakpoint_being_enabled; ++ ++ /* Artificial atomic skip breakpoint, if any needed. */ ++ void *atomic_skip_bp_addrs[2]; ++ ++ /* When all tasks are stopped, this callback gets called. */ ++ void (*on_all_stopped)(struct process_stopping_handler *); ++ ++ /* When we get a singlestep event, this is called to decide ++ * whether to stop stepping, or whether to enable the ++ * brakpoint, sink remaining signals, and continue ++ * everyone. */ ++ enum callback_status (*keep_stepping_p) ++ (struct process_stopping_handler *); ++ ++ /* Whether we need to use ugly workaround to get around ++ * various problems with singlestepping. */ ++ enum callback_status (*ugly_workaround_p) ++ (struct process_stopping_handler *); ++ ++ enum { ++ /* We are waiting for everyone to land in t/T. */ ++ psh_stopping = 0, ++ ++ /* We are doing the PTRACE_SINGLESTEP. */ ++ psh_singlestep, ++ ++ /* We are waiting for all the SIGSTOPs to arrive so ++ * that we can sink them. */ ++ psh_sinking, ++ ++ /* This is for tracking the ugly workaround. */ ++ psh_ugly_workaround, ++ } state; ++ ++ int exiting; ++ ++ struct pid_set pids; ++}; ++ ++/* Allocate a process stopping handler, initialize it and install it. ++ * Return 0 on success or a negative value on failure. Pass NULL for ++ * each callback to use a default instead. The default for ++ * ON_ALL_STOPPED is LINUX_PTRACE_DISABLE_AND_SINGLESTEP, the default ++ * for KEEP_STEPPING_P and UGLY_WORKAROUND_P is "no". */ ++int process_install_stopping_handler ++ (struct Process *proc, struct breakpoint *sbp, ++ void (*on_all_stopped)(struct process_stopping_handler *), ++ enum callback_status (*keep_stepping_p) ++ (struct process_stopping_handler *), ++ enum callback_status (*ugly_workaround_p) ++ (struct process_stopping_handler *)); ++ ++void linux_ptrace_disable_and_singlestep(struct process_stopping_handler *self); ++void linux_ptrace_disable_and_continue(struct process_stopping_handler *self); ++ ++#endif /* _LTRACE_LINUX_TRACE_H_ */ +diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c +index b53ff44..bb1b2b1 100644 +--- a/sysdeps/linux-gnu/x86_64/plt.c ++++ b/sysdeps/linux-gnu/x86_64/plt.c +@@ -1,5 +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) { +diff --git a/sysdeps/linux-gnu/x86_64/regs.c b/sysdeps/linux-gnu/x86_64/regs.c +index ed1f118..0ff3281 100644 +--- a/sysdeps/linux-gnu/x86_64/regs.c ++++ b/sysdeps/linux-gnu/x86_64/regs.c +@@ -4,7 +4,7 @@ + #include + #include + +-#include "common.h" ++#include "proc.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR +diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c +index d0299d9..0d3f693 100644 +--- a/sysdeps/linux-gnu/x86_64/trace.c ++++ b/sysdeps/linux-gnu/x86_64/trace.c +@@ -12,6 +12,7 @@ + + #include "common.h" + #include "ptrace.h" ++#include "proc.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR +@@ -42,23 +43,35 @@ get_arch_dep(Process *proc) { + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ + int +-syscall_p(Process *proc, int status, int *sysnum) { ++syscall_p(struct Process *proc, int status, int *sysnum) ++{ + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { ++ struct callstack_element *elem = NULL; ++ if (proc->callstack_depth > 0) ++ elem = proc->callstack + proc->callstack_depth - 1; ++ + long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); +- if (ret == -1 && errno) +- return -1; ++ if (ret == -1) { ++ if (errno) ++ return -1; ++ /* Otherwise, ORIG_RAX == -1 means that the ++ * system call should not be restarted. In ++ * that case rely on what we have on ++ * stack. */ ++ if (elem != NULL && elem->is_syscall) ++ ret = elem->c_un.syscall; ++ } + + *sysnum = ret; +- if (proc->callstack_depth > 0 && +- proc->callstack[proc->callstack_depth - 1].is_syscall && +- proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { ++ debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret, ++ get_instruction_pointer(proc), errno); ++ if (elem != NULL && elem->is_syscall ++ && elem->c_un.syscall == *sysnum) + return 2; +- } + +- if (*sysnum >= 0) { ++ if (*sysnum >= 0) + return 1; +- } + } + return 0; + } +diff --git a/sysdeps/sysdep.h b/sysdeps/sysdep.h +index 1d19c6f..96b3857 100644 +--- a/sysdeps/sysdep.h ++++ b/sysdeps/sysdep.h +@@ -1 +1,31 @@ ++#ifndef LTRACE_SYSDEP_H ++#define LTRACE_SYSDEP_H ++ + #include ++ ++#ifndef ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++}; ++#endif ++ ++#ifndef ARCH_HAVE_BREAKPOINT_DATA ++struct arch_breakpoint_data { ++}; ++#endif ++ ++#ifndef ARCH_HAVE_LIBRARY_SYMBOL_DATA ++struct arch_library_symbol_data { ++}; ++#endif ++ ++#ifndef ARCH_HAVE_LIBRARY_DATA ++struct arch_library_data { ++}; ++#endif ++ ++#ifndef ARCH_HAVE_PROCESS_DATA ++struct arch_process_data { ++}; ++#endif ++ ++#endif /* LTRACE_SYSDEP_H */ +diff --git a/testsuite/ltrace.main/filt.c b/testsuite/ltrace.main/filt.c +new file mode 100644 +index 0000000..f31a30d +--- /dev/null ++++ b/testsuite/ltrace.main/filt.c +@@ -0,0 +1,8 @@ ++void func1(void); ++ ++int ++main(int argc, char *argv[]) ++{ ++ func1(); ++ return 0; ++} +diff --git a/testsuite/ltrace.main/filt1.c b/testsuite/ltrace.main/filt1.c +new file mode 100644 +index 0000000..a0eb906 +--- /dev/null ++++ b/testsuite/ltrace.main/filt1.c +@@ -0,0 +1,7 @@ ++void func2(void); ++ ++void ++func1(void) ++{ ++ func2(); ++} +diff --git a/testsuite/ltrace.main/filt2.c b/testsuite/ltrace.main/filt2.c +new file mode 100644 +index 0000000..24999a3 +--- /dev/null ++++ b/testsuite/ltrace.main/filt2.c +@@ -0,0 +1,5 @@ ++void ++func2(void) ++{ ++ puts("func2"); ++} +diff --git a/testsuite/ltrace.main/filters.exp b/testsuite/ltrace.main/filters.exp +new file mode 100644 +index 0000000..1a9a8f7 +--- /dev/null ++++ b/testsuite/ltrace.main/filters.exp +@@ -0,0 +1,79 @@ ++# Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ ++set testfile "filt" ++set srcfile0 $srcdir/$subdir/$testfile.c ++set binfile0 $objdir/$subdir/$testfile ++set base1 "filt1" ++set srcfile1 $srcdir/$subdir/$base1.c ++set binfile1 $objdir/$subdir/lib$base1.so ++set base2 "filt2" ++set srcfile2 $srcdir/$subdir/$base2.c ++set binfile2 $objdir/$subdir/lib$base2.so ++ ++if [get_compiler_info $binfile0] { ++ return -1 ++} ++ ++verbose "compiling source file now....." ++if { [ltrace_compile_shlib $srcfile2 $binfile2 debug ] != "" ++ || [ltrace_compile_shlib $srcfile1 $binfile1 debug ] != "" ++ || [ltrace_compile $srcfile0 $binfile0 executable [list debug shlib=$binfile1 shlib=$binfile2] ] != ""} { ++ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" ++} ++ ++ltrace_options "-e*" ++set exec_output [ltrace_runtest $objdir/$subdir $binfile0] ++ ++# Check the output of this program. ++verbose "ltrace runtest output: $exec_output\n" ++if [regexp {ELF from incompatible architecture} $exec_output] { ++ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" ++ return ++} elseif [ regexp {Couldn't get .hash data} $exec_output ] { ++ fail "Couldn't get .hash data!" ++ return ++} ++ ++ltrace_verify_output ${binfile0}.ltrace "filt->func1" 1 ++ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2" 1 ++ltrace_verify_output ${binfile0}.ltrace "libfilt2.so->puts" 1 ++ltrace_verify_output ${binfile0}.ltrace "func2 resumed" 1 ++ltrace_verify_output ${binfile0}.ltrace "func1 resumed" 1 ++ ++# I simply can't figure out how to pass an empty string to ++# ltrace_options without that getting interpreted as {}, so pass ++# something harmless instead. ++ltrace_options "-b" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "^func1(.*)" 1 ++ ++ltrace_options "-e@MAIN" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "filt->func1(.*)" 1 ++ ++ltrace_options "-e@libfilt1.so" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(.*)" 1 ++ ++ltrace_options "-e@libfilt2.so" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "libfilt2.so->puts(.*)" 1 ++ ++ltrace_options "-e@libfilt*" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(" 1 ++ltrace_verify_output ${binfile0}.ltrace "libfilt2.so->puts(.*)" 1 ++ltrace_verify_output ${binfile0}.ltrace "func2 resumed" 1 ++ ++ltrace_options "-efunc*" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "filt->func1(" 1 ++ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(.*)" 1 ++ltrace_verify_output ${binfile0}.ltrace "func1 resumed" 1 ++ ++# Check that we handle breakpoint on both PLT entry and entry point ++ltrace_options "-efunc1" "-xfunc1" ++ltrace_runtest $objdir/$subdir $binfile0 ++ltrace_verify_output ${binfile0}.ltrace "filt->func1(" 1 ++ltrace_verify_output ${binfile0}.ltrace "func1@libfilt1.so(.*)" 1 ++ltrace_verify_output ${binfile0}.ltrace "func1 resumed" 1 +diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c +index 154de84..fb46dfe 100644 +--- a/testsuite/ltrace.main/parameters.c ++++ b/testsuite/ltrace.main/parameters.c +@@ -36,10 +36,16 @@ typedef enum { + void func_enum(color_t); + void func_typedef(color_t); + +-void func_work (char *x); +-void func_call (char *x, char* y, void (*cb) (char *)); ++void func_work(char *x); ++void func_call(char *x, char *y, void (*cb)(char *)); + +-int ++void ++call_func_work (char *x) ++{ ++ func_work(x); ++} ++ ++int + main () + { + int x = 17; +@@ -124,7 +130,7 @@ main () + { + char x[10] = {}; + char y[10] = {}; +- func_call (x, y, func_work); ++ func_call(x, y, call_func_work); + } + + return 0; +diff --git a/testsuite/ltrace.minor/libdl-simple.exp b/testsuite/ltrace.minor/libdl-simple.exp +index 9957001..91af5bd 100644 +--- a/testsuite/ltrace.minor/libdl-simple.exp ++++ b/testsuite/ltrace.minor/libdl-simple.exp +@@ -28,25 +28,5 @@ if [regexp {ELF from incompatible architecture} $exec_output] { + return + } + +-# Verify the time for calling sleep. +-set fd [ open $objdir/$subdir/$binfile.ltrace r] +-set FOUND 0 +-while { [gets $fd line] >= 0 } { +- # match the line with sleep and extract the spent time in sleep and sleep argument. +- if [ regexp {(test_libdl)\(} $line match tester ] then { +- verbose "test_libdl = $tester" +- +- if { $tester == "test_libdl" } then { +- pass "Successfully traced libdl loaded function." +- } else { +- fail "Failed to trace libdl loaded function." +- } +- set FOUND 1 +- break +- } +-} +-close $fd +- +-if {$FOUND != 1} then { +- fail "Fail to trace libdl loaded function!" +-} ++set pattern "test_libdl@liblibdl-simple.so" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +diff -urp ltrace-0.6.0-orig/Makefile.in ltrace-0.6.0/Makefile.in +--- ltrace-0.6.0-orig/Makefile.in 2012-05-01 00:24:18.824433420 +0200 ++++ ltrace-0.6.0/Makefile.in 2012-05-01 00:25:01.285738780 +0200 +@@ -65,7 +65,7 @@ libltrace_la_DEPENDENCIES = $(am__DEPEND + am_libltrace_la_OBJECTS = breakpoints.lo debug.lo demangle.lo dict.lo \ + display_args.lo ltrace-elf.lo execute_program.lo \ + handle_event.lo libltrace.lo options.lo output.lo proc.lo \ +- read_config_file.lo summary.lo ++ read_config_file.lo summary.lo library.lo filter.lo glob.lo + libltrace_la_OBJECTS = $(am_libltrace_la_OBJECTS) + am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \ + "$(DESTDIR)$(docdir)" "$(DESTDIR)$(sysconfdir)" +@@ -311,7 +314,10 @@ libltrace_la_SOURCES = \ + output.c \ + proc.c \ + read_config_file.c \ +- summary.c ++ summary.c \ ++ library.c \ ++ filter.c \ ++ glob.c + + libltrace_la_LIBADD = \ + $(libelf_LIBS) \ +@@ -338,7 +344,10 @@ noinst_HEADERS = \ + ltrace.h \ + options.h \ + output.h \ +- read_config_file.h ++ read_config_file.h \ ++ library.h \ ++ filter.h \ ++ glob.h + + dist_man1_MANS = \ + ltrace.1 +diff -urp ltrace-0.6.0-orig/sysdeps/linux-gnu/Makefile.in ltrace-0.6.0/sysdeps/linux-gnu/Makefile.in +--- ltrace-0.6.0-orig/sysdeps/linux-gnu/Makefile.in 2012-05-01 00:24:18.864434649 +0200 ++++ ltrace-0.6.0/sysdeps/linux-gnu/Makefile.in 2012-05-01 00:24:59.835694202 +0200 +@@ -260,7 +263,8 @@ ___libos_la_LIBADD = \ + noinst_HEADERS = \ + arch_syscallent.h \ + signalent1.h \ +- syscallent1.h ++ syscallent1.h \ ++ trace.h + + EXTRA_DIST = \ + arch_mksyscallent \ diff --git a/ltrace-0.6.0-ppc-shift.patch b/ltrace-0.6.0-ppc-shift.patch deleted file mode 100644 index 27d8b95..0000000 --- a/ltrace-0.6.0-ppc-shift.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c -index 980d028..668f63d 100644 ---- a/sysdeps/linux-gnu/ppc/plt.c -+++ b/sysdeps/linux-gnu/ppc/plt.c -@@ -44,12 +44,16 @@ sym2addr(Process *proc, struct library_symbol *sym) { - - pt_ret = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); - -+#if SIZEOF_LONG == 8 - if (proc->mask_32bit) { - // Assume big-endian. - addr = (void *)((pt_ret >> 32) & 0xffffffff); - } else { - addr = (void *)pt_ret; - } -+#else -+ addr = (void *)pt_ret; -+#endif - - return addr; - } diff --git a/ltrace.spec b/ltrace.spec index 64838a5..2f9bc85 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -17,14 +17,12 @@ Source: %{name}-%{version}.tar.bz2 Patch1: ltrace-0.5-ia64-sigill.patch Patch2: ltrace-0.6.0-exec-stripped.patch -Patch3: ltrace-0.5-demangle.patch Patch4: ltrace-0.5-etc-memmove.patch Patch5: ltrace-0.6.0-return-string-n.patch Patch6: ltrace-0.6.0-threads.patch Patch7: ltrace-0.6.0-endian.patch Patch8: ltrace-0.6.0-clone-test.patch Patch9: ltrace-0.6.0-ppc-args.patch -Patch10: ltrace-0.6.0-ppc-shift.patch Patch11: ltrace-0.6.0-vfork.patch Patch12: ltrace-0.6.0-thread-races.patch Patch13: ltrace-0.6.0-process-start.patch @@ -32,6 +30,7 @@ Patch14: ltrace-0.6.0-selinux.patch Patch15: ltrace-0.6.0-detach-sleeping.patch Patch16: ltrace-0.6.0-tail-return.patch Patch17: ltrace-0.6.0-ppc-lwarx.patch +Patch18: ltrace-0.6.0-libs.patch %description Ltrace is a debugging program which runs a specified command until the @@ -47,14 +46,12 @@ execution of processes. %setup -q %patch1 -p1 %patch2 -p1 -#%patch3 -p1 %patch4 -p1 %patch5 -p1 %patch6 -p1 %patch7 -p1 %patch8 -p1 %patch9 -p1 -%patch10 -p1 %patch11 -p1 %patch12 -p1 %patch13 -p1 @@ -62,6 +59,7 @@ execution of processes. %patch15 -p1 %patch16 -p1 %patch17 -p1 +%patch18 -p1 sed -i -e 's/-o root -g root//' Makefile.in %build @@ -90,6 +88,18 @@ echo ====================TESTING END===================== %config(noreplace) %{_sysconfdir}/ltrace.conf %changelog +* Mon Apr 30 2012 Petr Machata - 0.6.0-11 +- Fix detach from sleeping process +- Add limited support for return from tail call +- Fix singlestep over atomic instruction sequence on PPC +- Add extensive upstream patch that implements + - tracing calls done from DSOs + - better tools for filtering symbol tables + - support for tracing PLT calls on PPC64 (not entry points read from .plt) + - support for PPC32 old-style (BSS) PLT table +- Drop ppc-shift patch that was superseded by the above +- Drop demangle patch that hasn't been applied for some time now + * Wed Apr 11 2012 Peter Robinson - 0.6.0-10 - Drop ExclusiveArch as all current Primary/Secondary Arches are supported