commit ee71d18ef66d8243798d3513e3ef2107f6f15cb1
Author: Petr Machata <pmachata(a)redhat.com>
Date: Tue May 1 00:46:48 2012 +0200
Add code from upstream libs branch
ltrace-0.5-demangle.patch | 58 -
ltrace-0.6.0-libs.patch |10190 ++++++++++++++++++++++++++++++++++++++++++
ltrace-0.6.0-ppc-shift.patch | 21 -
ltrace.spec | 18 +-
4 files changed, 10204 insertions(+), 83 deletions(-)
---
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 <stdlib.h>
+ #include <string.h>
+ #include <assert.h>
++#include <errno.h>
+
+ #ifdef __powerpc__
+ #include <sys/ptrace.h>
+@@ -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 <config.h>
+-#if defined(HAVE_LIBUNWIND)
+-#include <libunwind.h>
+-#endif /* defined(HAVE_LIBUNWIND) */
+
+ #include <sys/types.h>
+ #include <sys/time.h>
+@@ -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 <limits.h>
+
+ #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 <stdlib.h>
++#include <assert.h>
++#include <stdio.h>
++#include <string.h>
++
++#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 <sys/types.h>
++#include <regex.h>
++
++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 <sys/types.h>
++#include <regex.h>
++#include <string.h>
++#include <stdlib.h>
++
++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 <assert.h>
++#include <stdio.h>
++
++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 <sys/types.h>
++#include <regex.h>
++
++/* 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 <assert.h>
++#include <errno.h>
++#include <signal.h>
+ #include <stdio.h>
+-#include <string.h>
+ #include <stdlib.h>
+-#include <signal.h>
+-#include <assert.h>
++#include <string.h>
+ #include <sys/time.h>
+-#include <errno.h>
+-
+-#ifdef __powerpc__
+-#include <sys/ptrace.h>
+-#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 <sys/param.h>
++#include <sys/wait.h>
++#include <errno.h>
++#include <signal.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+-#include <unistd.h>
+ #include <string.h>
+-#include <errno.h>
+-#include <sys/param.h>
+-#include <signal.h>
+-#include <sys/wait.h>
++#include <unistd.h>
+
+ #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 <stdlib.h>
++#include <string.h>
++#include <assert.h>
++
++#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 <stdint.h>
++#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 <assert.h>
+ #include <endian.h>
+ #include <errno.h>
+-#include <error.h>
+ #include <fcntl.h>
+ #include <gelf.h>
+ #include <inttypes.h>
++#include <search.h>
+ #include <stdint.h>
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+-#include <assert.h>
+
+ #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; i<lte->dynsym_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 <gelf.h>
+ #include <stdlib.h>
++#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 <cespedes(a)debian.org>
+ .\" 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-(a)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(a)libc.*\fR is really \fB*(a)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 <cespedes(a)debian.org>
++.br
++Petr Machata <pmachata(a)redhat.com>
+
+ .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 <string.h>
+-#include <stdlib.h>
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <errno.h>
+-#include <limits.h>
+ #include <sys/ioctl.h>
+-
++#include <assert.h>
++#include <errno.h>
++#include <fcntl.h>
+ #include <getopt.h>
++#include <limits.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
+
+ #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 <stdio.h>
+ #include <sys/types.h>
+
++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 <unistd.h>
+
+ #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 <errno.h>
+ #include <stdlib.h>
+ #include <assert.h>
+-#include <error.h>
+
+ #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 <libunwind.h>
++#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 <gelf.h>
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <gelf.h>
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <sys/ptrace.h>
++#include <errno.h>
+ #include <string.h>
++#include <stdio.h>
+
+ #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 <gelf.h>
+-#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
+-#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 <stdlib.h>
++#include <sys/ptrace.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+-#include <signal.h>
+-#include <sys/ptrace.h>
+ #include <asm/ptrace.h>
++#include <errno.h>
++#include <signal.h>
++#include <stdlib.h>
+
++#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 <gelf.h>
++#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 <asm/rse.h>
+
+ #include <stddef.h>
++#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 <asm/rse.h>
+ #include <errno.h>
+
++#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 <gelf.h>
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <stddef.h>
++
+ #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 <gelf.h>
+ #include <sys/ptrace.h>
++#include <error.h>
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+ #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 <gelf.h>
++
+ #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 <gelf.h>
+ #include <sys/ptrace.h>
++#include <errno.h>
++#include <error.h>
++#include <inttypes.h>
++#include <assert.h>
++#include <string.h>
++
++#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.<callee>
++ * (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 <sys/types.h>
+ #include <sys/ptrace.h>
+ #include <asm/ptrace.h>
++#include <errno.h>
++#include <error.h>
+
++#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 <errno.h>
+ #include <string.h>
+
++#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 <sys/types.h>
+ #include <sys/stat.h>
++#include <sys/syscall.h>
++#include <sys/types.h>
++#include <ctype.h>
++#include <dirent.h>
++#include <errno.h>
+ #include <fcntl.h>
+ #include <inttypes.h>
+ #include <link.h>
++#include <signal.h>
+ #include <stdio.h>
+ #include <string.h>
+-#include <signal.h>
+ #include <unistd.h>
+-#include <dirent.h>
+-#include <ctype.h>
+-#include <errno.h>
+-#include <sys/syscall.h>
+-#include <error.h>
+
+ #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 <gelf.h>
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <sys/ptrace.h>
+ #include <asm/ptrace.h>
+
++#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 <gelf.h>
++#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 <sys/types.h>
+ #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 <signal.h>
+ #include <string.h>
+ #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 <gelf.h>
++#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 <sys/ptrace.h>
+ #include <sys/reg.h>
+
+-#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 <arch.h>
++
++#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(a)libfilt1.so"
++ltrace_runtest $objdir/$subdir $binfile0
++ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(.*)" 1
++
++ltrace_options "-e(a)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(a)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(a)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.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 <pmachata(a)redhat.com> - 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 <pbrobinson(a)fedoraproject.org> - 0.6.0-10
- Drop ExclusiveArch as all current Primary/Secondary Arches are supported