The first crude version of tracing across libraries

- the patch will be sliced later
diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h
index 8f2dfb3..d50e439 100644
--- a/sysdeps/linux-gnu/arm/arch.h
+++ b/sysdeps/linux-gnu/arm/arch.h
@@ -9,3 +9,8 @@
 
 #define LT_ELFCLASS	ELFCLASS32
 #define LT_ELF_MACHINE	EM_ARM
+
+#define ARCH_HAVE_BREAKPOINT_DATA
+struct arch_breakpoint_data {
+	int thumb_mode;
+};
diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c
index 493f973..b94e471 100644
--- a/sysdeps/linux-gnu/arm/breakpoint.c
+++ b/sysdeps/linux-gnu/arm/breakpoint.c
@@ -82,3 +82,14 @@
 		ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), current.l);
 	}
 }
+
+int
+arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp)
+{
+	int thumb_mode = (int)addr & 1;
+	if (thumb_mode)
+		addr = (void *)((int)addr & ~1);
+	sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode;
+	proc->thumb_mode = 0;
+	return 0;
+}
diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c
index ea4d3a6..22bc4bf 100644
--- a/sysdeps/linux-gnu/arm/regs.c
+++ b/sysdeps/linux-gnu/arm/regs.c
@@ -40,9 +40,15 @@
 get_return_addr(Process *proc, void *stack_pointer) {
 	long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0);
 
+	/* Remember & unset the thumb mode bit.  XXX This is really a
+	 * bit of a hack, as we assume that the following
+	 * insert_breakpoint call will be related to this address.
+	 * This interface should really be get_return_breakpoint, or
+	 * maybe install_return_breakpoint.  */
 	proc->thumb_mode = addr & 1;
 	if (proc->thumb_mode)
 		addr &= ~1;
+
 	return (void *)addr;
 }
 
diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c
index 58eac8d..1012447 100644
--- a/sysdeps/linux-gnu/breakpoint.c
+++ b/sysdeps/linux-gnu/breakpoint.c
@@ -7,6 +7,7 @@
 #include "arch.h"
 #include "breakpoint.h"
 #include "proc.h"
+#include "library.h"
 
 #ifdef ARCH_HAVE_ENABLE_BREAKPOINT
 extern void arch_enable_breakpoint(pid_t, struct breakpoint *);
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
index 9c376f3..91d873e 100644
--- a/sysdeps/linux-gnu/events.c
+++ b/sysdeps/linux-gnu/events.c
@@ -22,10 +22,10 @@
 static Event * delayed_events = NULL;
 static Event * end_delayed_events = NULL;
 
-static enum pcb_status
+static enum callback_status
 first (Process * proc, void * data)
 {
-	return pcb_stop;
+	return CBS_STOP;
 }
 
 void
@@ -175,14 +175,6 @@
 	get_arch_dep(event.proc);
 	debug(3, "event from pid %u", pid);
 	Process *leader = event.proc->leader;
-	if (leader == event.proc) {
-		if (!event.proc->libdl_hooked) {
-			/* debug struct may not have been written yet.. */
-			if (linkmap_init(event.proc, &main_lte) == 0) {
-				event.proc->libdl_hooked = 1;
-			}
-		}
-	}
 
 	/* The process should be stopped after the waitpid call.  But
 	 * when the whole thread group is terminated, we see
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
index 70bc19b..707d9d9 100644
--- a/sysdeps/linux-gnu/ppc/plt.c
+++ b/sysdeps/linux-gnu/ppc/plt.c
@@ -8,6 +8,7 @@
 	return rela->r_offset;
 }
 
+/* XXX Apparently PPC64 doesn't support PLT breakpoints.  */
 void *
 sym2addr(Process *proc, struct library_symbol *sym) {
 	void *addr = sym->enter_addr;
@@ -53,6 +54,8 @@
 		addr = (void *)pt_ret;
 	}
 #else
+	/* XXX Um, so where exactly are we dealing with the non-secure
+	   PLT thing?  */
 	addr = (void *)pt_ret;
 #endif
 
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index eba030f..4592924 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -19,6 +19,7 @@
 #include "common.h"
 #include "breakpoint.h"
 #include "proc.h"
+#include "library.h"
 
 /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace'
  * couldn't open it to find the executable.  So it may be necessary to
@@ -78,27 +79,28 @@
 }
 
 static void
-each_line_starting(FILE * file, const char *prefix,
-		   enum pcb_status (*cb)(const char * line, const char * prefix,
-					 void * data),
-		   void * data)
+each_line_starting(FILE *file, const char *prefix,
+		   enum callback_status (*cb)(const char *line,
+					      const char *prefix,
+					      void *data),
+		   void *data)
 {
 	size_t len = strlen(prefix);
 	char * line;
 	while ((line = find_line_starting(file, prefix, len)) != NULL) {
-		enum pcb_status st = (*cb)(line, prefix, data);
+		enum callback_status st = (*cb)(line, prefix, data);
 		free (line);
-		if (st == pcb_stop)
+		if (st == CBS_STOP)
 			return;
 	}
 }
 
-static enum pcb_status
-process_leader_cb(const char * line, const char * prefix, void * data)
+static enum callback_status
+process_leader_cb(const char *line, const char *prefix, void *data)
 {
 	pid_t * pidp = data;
 	*pidp = atoi(line + strlen(prefix));
-	return pcb_stop;
+	return CBS_STOP;
 }
 
 pid_t
@@ -114,13 +116,13 @@
 	return tgid;
 }
 
-static enum pcb_status
-process_stopped_cb(const char * line, const char * prefix, void * data)
+static enum callback_status
+process_stopped_cb(const char *line, const char *prefix, void *data)
 {
 	char c = line[strlen(prefix)];
 	// t:tracing stop, T:job control stop
 	*(int *)data = (c == 't' || c == 'T');
-	return pcb_stop;
+	return CBS_STOP;
 }
 
 int
@@ -136,15 +138,15 @@
 	return is_stopped;
 }
 
-static enum pcb_status
-process_status_cb(const char * line, const char * prefix, void * data)
+static enum callback_status
+process_status_cb(const char *line, const char *prefix, void *data)
 {
 	const char * status = line + strlen(prefix);
 	const char c = *status;
 
 #define RETURN(C) do {					\
 		*(enum process_status *)data = C;	\
-		return pcb_stop;			\
+		return CBS_STOP;			\
 	} while (0)
 
 	switch (c) {
@@ -245,6 +247,8 @@
 
 static int
 find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) {
+	fprintf(stderr, "find_dynamic_entry_addr %d %p %d\n",
+		proc->pid, pvAddr, d_tag);
 	int i = 0, done = 0;
 	ElfW(Dyn) entry;
 
@@ -257,7 +261,9 @@
 	while ((!done) && (i < ELF_MAX_SEGMENTS) &&
 		(sizeof(entry) == umovebytes(proc, pvAddr, &entry, sizeof(entry))) &&
 		(entry.d_tag != DT_NULL)) {
+		fprintf(stderr, " entry %ld %#lx\n", entry.d_tag, entry.d_un.d_val);
 		if (entry.d_tag == d_tag) {
+			fprintf(stderr, "   hit\n");
 			done = 1;
 			*addr = (void *)entry.d_un.d_val;
 		}
@@ -275,15 +281,16 @@
 	}
 }
 
-struct cb_data {
-	const char *lib_name;
-	struct ltelf *lte;
-	ElfW(Addr) addr;
-	Process *proc;
-};
+enum callback_status
+find_library_addr(struct Process *proc, struct library *lib, void *data)
+{
+	target_address_t addr = (target_address_t)*(GElf_Addr *)data;
+	return lib->base == addr ? CBS_STOP : CBS_CONT;
+}
 
 static void
-crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), struct cb_data *data) {
+crawl_linkmap(Process *proc, struct r_debug *dbg)
+{
 	struct link_map rlm;
 	char lib_name[BUFSIZ];
 	struct link_map *lm = NULL;
@@ -311,19 +318,33 @@
 
 		umovebytes(proc, rlm.l_name, lib_name, sizeof(lib_name));
 
-		if (lib_name[0] == '\0') {
-			debug(2, "Library name is an empty string");
+		debug(2, "Dispatching callback for: %s, "
+		      "Loaded at 0x%" PRI_ELF_ADDR "\n",
+		      lib_name, rlm.l_addr);
+		fprintf(stderr, "DSO addr=%#lx, name='%s'\n", rlm.l_addr, lib_name);
+
+		/* Do we have that library already?  */
+		struct library *lib
+			= proc_each_library(proc, NULL, find_library_addr,
+					    &rlm.l_addr);
+		if (lib != NULL)
+			continue;
+
+		if (*lib_name == '\0') {
+			/* VDSO.  No associated file, XXX but we might
+			 * load it from the address space of the
+			 * process.  */
 			continue;
 		}
 
-		if (callback) {
-			debug(2, "Dispatching callback for: %s, "
-					"Loaded at 0x%" PRI_ELF_ADDR "\n",
-					lib_name, rlm.l_addr);
-			data->addr = rlm.l_addr;
-			data->lib_name = lib_name;
-			callback(data);
+		lib = ltelf_read_library(lib_name, rlm.l_addr);
+		if (lib == NULL) {
+			error(0, errno, "Couldn't load ELF object %s\n",
+			      lib_name);
+			continue;
 		}
+
+		proc_add_library(proc, lib);
 	}
 	return;
 }
@@ -349,63 +370,11 @@
 }
 
 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(&lte, 0, sizeof(struct ltelf));
-		lte.base_addr = lm_add->addr;
-		do_init_elf(&lte, 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, &lte, 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(&lte);
-	}
-}
-
-void
-arch_check_dbg(Process *proc) {
+rdebug_callback_hit(struct breakpoint *bp, struct Process *proc)
+{
+	fprintf(stderr, "======= HIT\n");
 	struct r_debug *dbg = NULL;
-	struct cb_data data;
+	//struct cb_data data;
 
 	debug(DEBUG_FUNCTION, "arch_check_dbg");
 
@@ -418,8 +387,9 @@
 		debug(2, "Linkmap is now consistent");
 		if (proc->debug_state == RT_ADD) {
 			debug(2, "Adding DSO to linkmap");
-			data.proc = proc;
-			crawl_linkmap(proc, dbg, linkmap_add_cb, &data);
+			//data.proc = proc;
+			crawl_linkmap(proc, dbg);
+			//&data);
 		} else if (proc->debug_state == RT_DELETE) {
 			debug(2, "Removing DSO from linkmap");
 		} else {
@@ -428,45 +398,19 @@
 	}
 
 	proc->debug_state = dbg->r_state;
-
 	return;
 }
 
-static void
-hook_libdl_cb(void *data) {
-	struct cb_data *hook_data = data;
-	const char *lib_name = NULL;
-	ElfW(Addr) addr;
-	struct ltelf *lte = NULL;
-
-	debug(DEBUG_FUNCTION, "add_library_cb");
-
-	if (!data) {
-		debug(2, "No callback data");
-		return;
-	}
-
-	lib_name = hook_data->lib_name;
-	addr = hook_data->addr;
-	lte = hook_data->lte;
-
-	if (library_num < MAX_LIBRARIES) {
-		lte[library_num].base_addr = addr;
-		library[library_num++] = strdup(lib_name);
-	}
-	else {
-		fprintf (stderr, "MAX LIBS REACHED\n");
-		exit(EXIT_FAILURE);
-	}
-}
-
+void *dyn_addr;
 int
-linkmap_init(Process *proc, struct ltelf *lte) {
-	void *dbg_addr = NULL, *dyn_addr = GELF_ADDR_CAST(lte->dyn_addr);
+linkmap_init(struct Process *proc)
+{
+	void *dbg_addr = NULL;
 	struct r_debug *rdbg = NULL;
-	struct cb_data data;
+	//struct cb_data data;
 
 	debug(DEBUG_FUNCTION, "linkmap_init()");
+	fprintf(stderr, "linkmap_init dyn_addr=%p\n", dyn_addr);
 
 	if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, &dbg_addr) == -1) {
 		debug(2, "Couldn't find debug structure!");
@@ -480,13 +424,23 @@
 		return -1;
 	}
 
-	data.lte = lte;
+	//data.lte = lte;
 
-	add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0);
-	insert_breakpoint(proc, sym2addr(proc, library_symbols),
-			  library_symbols, 1);
+	void *addr;
+	{
+		struct library_symbol libsym;
+		library_symbol_init(&libsym, rdbg->r_brk, NULL, 0,
+				    LS_TOPLT_NONE, 0);
+		addr = sym2addr(proc, &libsym);
+		library_symbol_destroy(&libsym);
+	}
+	struct breakpoint *rdebug_bp = insert_breakpoint(proc, addr, NULL, 1);
+	static struct bp_callbacks rdebug_callbacks = {
+		.on_hit = rdebug_callback_hit,
+	};
+	rdebug_bp->cbs = &rdebug_callbacks;
 
-	crawl_linkmap(proc, rdbg, hook_libdl_cb, &data);
+	crawl_linkmap(proc, rdbg);
 
 	free(rdbg);
 	return 0;
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 6c6e814..9613271 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -304,8 +304,8 @@
 	return task_info;
 }
 
-static enum pcb_status
-task_stopped(Process * task, void * data)
+static enum callback_status
+task_stopped(struct Process *task, void *data)
 {
 	enum process_status st = process_status(task->pid);
 	if (data != NULL)
@@ -319,38 +319,38 @@
 	case ps_invalid:
 	case ps_tracing_stop:
 	case ps_zombie:
-		return pcb_cont;
+		return CBS_CONT;
 	case ps_sleeping:
 	case ps_stop:
 	case ps_other:
-		return pcb_stop;
+		return CBS_STOP;
 	}
 
 	abort ();
 }
 
 /* Task is blocked if it's stopped, or if it's a vfork parent.  */
-static enum pcb_status
-task_blocked(Process * task, void * data)
+static enum callback_status
+task_blocked(struct Process *task, void *data)
 {
 	struct pid_set * pids = data;
 	struct pid_task * task_info = get_task_info(pids, task->pid);
 	if (task_info != NULL
 	    && task_info->vforked)
-		return pcb_cont;
+		return CBS_CONT;
 
 	return task_stopped(task, NULL);
 }
 
 static Event *process_vfork_on_event(struct event_handler *super, Event *event);
 
-static enum pcb_status
-task_vforked(Process * task, void * data)
+static enum callback_status
+task_vforked(struct Process *task, void *data)
 {
 	if (task->event_handler != NULL
 	    && task->event_handler->on_event == &process_vfork_on_event)
-		return pcb_stop;
-	return pcb_cont;
+		return CBS_STOP;
+	return CBS_CONT;
 }
 
 static int
@@ -359,8 +359,8 @@
 	return each_task(task->leader, &task_vforked, NULL) != NULL;
 }
 
-static enum pcb_status
-send_sigstop(Process * task, void * data)
+static enum callback_status
+send_sigstop(struct Process *task, void *data)
 {
 	Process * leader = task->leader;
 	struct pid_set * pids = data;
@@ -373,24 +373,24 @@
 		perror("send_sigstop: add_task_info");
 		destroy_event_handler(leader);
 		/* Signal failure upwards.  */
-		return pcb_stop;
+		return CBS_STOP;
 	}
 
 	/* This task still has not been attached to.  It should be
 	   stopped by the kernel.  */
 	if (task->state == STATE_BEING_CREATED)
-		return pcb_cont;
+		return CBS_CONT;
 
 	/* Don't bother sending SIGSTOP if we are already stopped, or
 	 * if we sent the SIGSTOP already, which happens when we are
 	 * handling "onexit" and inherited the handler from breakpoint
 	 * re-enablement.  */
 	enum process_status st;
-	if (task_stopped(task, &st) == pcb_cont)
-		return pcb_cont;
+	if (task_stopped(task, &st) == CBS_CONT)
+		return CBS_CONT;
 	if (task_info->sigstopped) {
 		if (!task_info->delivered)
-			return pcb_cont;
+			return CBS_CONT;
 		task_info->delivered = 0;
 	}
 
@@ -401,7 +401,7 @@
 	if (st == ps_sleeping
 	    && is_vfork_parent (task)) {
 		task_info->vforked = 1;
-		return pcb_cont;
+		return CBS_CONT;
 	}
 
 	if (task_kill(task->pid, SIGSTOP) >= 0) {
@@ -411,7 +411,7 @@
 		fprintf(stderr,
 			"Warning: couldn't send SIGSTOP to %d\n", task->pid);
 
-	return pcb_cont;
+	return CBS_CONT;
 }
 
 /* On certain kernels, detaching right after a singlestep causes the
@@ -465,21 +465,21 @@
 	return ecb_cont;
 }
 
-static enum pcb_status
-untrace_task(Process * task, void * data)
+static enum callback_status
+untrace_task(struct Process *task, void *data)
 {
 	if (task != data)
 		untrace_pid(task->pid);
-	return pcb_cont;
+	return CBS_CONT;
 }
 
-static enum pcb_status
-remove_task(Process * task, void * data)
+static enum callback_status
+remove_task(struct Process *task, void *data)
 {
 	/* Don't untrace leader just yet.  */
 	if (task != data)
 		remove_process(task);
-	return pcb_cont;
+	return CBS_CONT;
 }
 
 static void
diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c
index 8b0fc46..bb1b2b1 100644
--- a/sysdeps/linux-gnu/x86_64/plt.c
+++ b/sysdeps/linux-gnu/x86_64/plt.c
@@ -1,6 +1,7 @@
 #include <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) {