On Tue, Mar 9, 2010 at 2:13 PM, Rodrigo Dominguez <roddomi at hotmail.com>wrote:

> How can I get ltrace to trace dependent libraries (libraries called from
> within libraries)?

Attached is a patch that I used to trace calls from one particular
shared library to other libraries.

The bulk of it comes from an old version of Joe Damato's libdl
support (thank you!):
http://lists.alioth.debian.org/pipermail/ltrace-devel/2009-October/000269.html

diff --git a/README b/README
index ab1a2e5..06c375c 100644
--- a/README
+++ b/README
@@ -37,6 +37,7 @@ people have contributed significantly to this project:
 * Ian Wienand <[email protected]> (IA64 port)
 * Eric Vaitl <[email protected]> (mipsel port)
 * Petr Machata <[email protected]> (misc fixes)
+* Joe Damato <[email protected]> (libdl support)
 
 1. Introduction
 ---------------
diff --git a/breakpoints.c b/breakpoints.c
index ba3b060..175021b 100644
--- a/breakpoints.c
+++ b/breakpoints.c
@@ -89,7 +89,8 @@ enable_all_breakpoints(Process *proc) {
 		 * If the dynamic linker hasn't populated the PLT then
 		 * dont enable the breakpoints
 		 */
-		if (options.libcalls) {
+		if (!options.libcalls || strcmp(options.libcalls, "none") != 0
+		    || opt_x) {
 			a = ptrace(PTRACE_PEEKTEXT, proc->pid,
 				   sym2addr(proc, proc->list_of_symbols),
 				   0);
@@ -173,7 +174,9 @@ breakpoints_init(Process *proc) {
 	}
 	proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int);
 
-	if (options.libcalls && proc->filename) {
+	if ((!options.libcalls || strcmp(options.libcalls, "none") != 0
+	     || opt_x)
+	    && proc->filename) {
 		/* FIXME: memory leak when called by exec(): */
 		proc->list_of_symbols = read_elf(proc);
 		if (opt_e) {
diff --git a/common.h b/common.h
index c672913..821d7a9 100644
--- a/common.h
+++ b/common.h
@@ -172,6 +172,8 @@ struct Process {
 	struct library_symbol * list_of_symbols;
 
 	/* 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;
@@ -222,6 +224,14 @@ extern void open_pid(pid_t pid);
 extern void show_summary(void);
 extern arg_type_info * lookup_prototype(enum arg_type at);
 
+extern void 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);
+
 /* Arch-dependent stuff: */
 extern char * pid2name(pid_t pid);
 extern void trace_set_options(Process * proc, pid_t pid);
@@ -245,9 +255,11 @@ extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info
 extern void save_register_args(enum tof type, Process * proc);
 extern int umovestr(Process * proc, void * addr, int len, void * laddr);
 extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info);
+extern size_t umovebytes (Process *proc, void * src, void * dest, size_t count);
 extern int ffcheck(void * maddr);
 extern void * sym2addr(Process *, struct library_symbol *);
+extern int linkmap_init(Process *, struct ltelf *);
+extern void arch_check_dbg(Process *proc);
 
-#if 0				/* not yet */
-extern int umoven(Process * proc, void * addr, int len, void * laddr);
-#endif
+extern int libdl_hooked;
+extern struct ltelf main_lte;
diff --git a/defs.h b/defs.h
index b694099..a741f8d 100644
--- a/defs.h
+++ b/defs.h
@@ -15,4 +15,4 @@
 #define DEFAULT_ARRAYLEN  4	/* default maximum # array elements */
 #endif				/* (-A switch) */
 
-#define MAX_LIBRARIES 30
+#define MAX_LIBRARIES (200)
diff --git a/elf.c b/elf.c
index aeff211..5222d8d 100644
--- a/elf.c
+++ b/elf.c
@@ -1,4 +1,4 @@
-# include "config.h"
+#include "config.h"
 
 #include <endian.h>
 #include <errno.h>
@@ -12,19 +12,24 @@
 
 #include "common.h"
 
-static void do_init_elf(struct ltelf *lte, const char *filename);
-static void do_close_elf(struct ltelf *lte);
-static void add_library_symbol(GElf_Addr addr, const char *name,
+void do_init_elf(struct ltelf *lte, const char *filename);
+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);
-static int in_load_libraries(const char *name, struct ltelf *lte);
+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;
+struct ltelf main_lte;
+
 #ifdef PLT_REINITALISATION_BP
 extern char *PLTs_initialized_by_here;
 #endif
 
-static void
+int libdl_hooked = 0;
+
+void
 do_init_elf(struct ltelf *lte, const char *filename) {
 	int i;
 	GElf_Addr relplt_addr = 0;
@@ -33,7 +38,6 @@ do_init_elf(struct ltelf *lte, const char *filename) {
 	debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
 	debug(1, "Reading ELF from %s...", filename);
 
-	memset(lte, 0, sizeof(*lte));
 	lte->fd = open(filename, O_RDONLY);
 	if (lte->fd == -1)
 		error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename);
@@ -141,6 +145,9 @@ do_init_elf(struct ltelf *lte, const char *filename) {
 		} else if (shdr.sh_type == SHT_DYNAMIC) {
 			Elf_Data *data;
 			size_t j;
+      
+			lte->dyn_addr = shdr.sh_addr;
+			lte->dyn_sz = shdr.sh_size;
 
 			data = elf_getdata(scn, NULL);
 			if (data == NULL || elf_getdata(scn, data) != NULL)
@@ -314,7 +321,7 @@ do_init_elf(struct ltelf *lte, const char *filename) {
 	}
 }
 
-static void
+void
 do_close_elf(struct ltelf *lte) {
 	debug(DEBUG_FUNCTION, "do_close_elf()");
 	if (lte->lte_flags & LTE_HASH_MALLOCED)
@@ -323,7 +330,7 @@ do_close_elf(struct ltelf *lte) {
 	close(lte->fd);
 }
 
-static void
+void
 add_library_symbol(GElf_Addr addr, const char *name,
 		   struct library_symbol **library_symbolspp,
 		   enum toplt type_of_plt, int is_weak) {
@@ -358,18 +365,18 @@ private_elf_gnu_hash(const char *name) {
 	return h & 0xffffffff;
 }
 
-static int
-in_load_libraries(const char *name, struct ltelf *lte) {
+int
+in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym) {
 	size_t i;
 	unsigned long hash;
 	unsigned long gnu_hash;
 
-	if (!library_num)
+	if (!count)
 		return 1;
 
-	hash = elf_hash((const unsigned char *)name);
+	hash = elf_hash(name);
 	gnu_hash = private_elf_gnu_hash(name);
-	for (i = 1; i <= library_num; ++i) {
+	for (i = 0; i < count; ++i) {
 		if (lte[i].hash == NULL)
 			continue;
 
@@ -394,15 +401,20 @@ in_load_libraries(const char *name, struct ltelf *lte) {
 				do
 					if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
 						int symidx = hasharr - chain_zero;
-						GElf_Sym sym;
-
-						if (gelf_getsym(lte[i].dynsym, symidx, &sym) == NULL)
-							error(EXIT_FAILURE, 0,
-							      "Couldn't get symbol from .dynsym");
-
-						if (sym.st_value != 0
-						    && sym.st_shndx != SHN_UNDEF
-						    && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
+						GElf_Sym tmp_sym;
+						GElf_Sym *tmp;
+
+						tmp = (sym) ? (sym) : (&tmp_sym);
+
+						if (gelf_getsym(lte[i].dynsym, symidx, tmp) == NULL)
+							error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym");
+						else {
+							tmp->st_value += lte[i].base_addr;
+							debug(35, "symbol found: %s, %zd, %lx", name, i, tmp->st_value);
+						}
+						if (tmp->st_value != 0
+						    && tmp->st_shndx != SHN_UNDEF
+						    && strcmp(name, lte[i].dynstr + tmp->st_name) == 0)
 							return 1;
 					}
 				while ((*hasharr++ & 1u) == 0);
@@ -416,15 +428,22 @@ in_load_libraries(const char *name, struct ltelf *lte) {
 
 			for (symndx = buckets[hash % nbuckets];
 			     symndx != STN_UNDEF; symndx = chain[symndx]) {
-				GElf_Sym sym;
+				GElf_Sym tmp_sym;
+				GElf_Sym *tmp;
 
-				if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
+				tmp = (sym) ? (sym) : (&tmp_sym);
+
+				if (gelf_getsym(lte[i].dynsym, symndx, tmp) == NULL)
 					error(EXIT_FAILURE, 0,
 					      "Couldn't get symbol from .dynsym");
+				else {
+					tmp->st_value += lte[i].base_addr;
+					debug(35, "symbol found: %s, %zd, %lx", name, i, tmp->st_value);
+				}
 
-				if (sym.st_value != 0
-				    && sym.st_shndx != SHN_UNDEF
-				    && strcmp(name, lte[i].dynstr + sym.st_name) == 0)
+				if (tmp->st_value != 0
+				    && tmp->st_shndx != SHN_UNDEF
+				    && strcmp(name, lte[i].dynstr + tmp->st_name) == 0)
 					return 1;
 			}
 		}
@@ -438,36 +457,62 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) {
 	unsigned long base, offset;
 
 	if (!lte->opd)
-		return addr;
+		return lte->base_addr + addr;
 
-	base = (unsigned long)lte->opd->d_buf;
+	base = (unsigned long)lte->base_addr + (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;
+	return lte->base_addr + addr;
 #endif
 }
 
 struct library_symbol *
 read_elf(Process *proc) {
-	struct library_symbol *library_symbols = NULL;
-	struct ltelf lte[MAX_LIBRARIES + 1];
+	struct ltelf ltes[MAX_LIBRARIES + 1];
 	size_t i;
 	struct opt_x_t *xptr;
-	struct library_symbol **lib_tail = NULL;
+	struct library_symbol **lib_tail = &library_symbols;
 	int exit_out = 0;
+	int count  = 0;
+	char *plt_filename =
+		options.libcalls ? options.libcalls : proc->filename;
+	struct ltelf *lte = options.libcalls ? NULL : &ltes[0];
 
 	debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename);
 
+	memset(&ltes, 0, sizeof(ltes));
+
 	elf_version(EV_CURRENT);
 
-	do_init_elf(lte, proc->filename);
-	proc->e_machine = lte->ehdr.e_machine;
-	for (i = 0; i < library_num; ++i)
-		do_init_elf(&lte[i + 1], library[i]);
+	do_init_elf(&ltes[0], proc->filename);
+
+	memcpy(&main_lte, &ltes[0], sizeof(struct ltelf));
+
+	if (opt_p && opt_p->pid > 0) {
+		linkmap_init(proc, &ltes[0]);
+		libdl_hooked = 1;
+	}
+
+	proc->e_machine = ltes[0].ehdr.e_machine;
+	for (i = 0; i < library_num; ++i) {
+		do_init_elf(&ltes[i + 1], library[i]);
+		if (options.libcalls
+		    && strcmp(library[i], options.libcalls) == 0) {
+			lte = &ltes[i + 1];
+		}
+	}
+
+	if (!lte
+	    && (!options.libcalls || strcmp(options.libcalls, "none") != 0)) {
+		error(EXIT_FAILURE, 0,
+		      "Didn't find library \"%s\"", options.libcalls);
+	}
+
+	if (lte) {
 #ifdef __mips__
 	// MIPS doesn't use the PLT and the GOT entries get changed
 	// on startup.
@@ -475,11 +520,11 @@ read_elf(Process *proc) {
 	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);
+		GElf_Addr addr = lte->base_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);
+					plt_filename);
 		}
 		name=lte->dynstr+sym.st_name;
 		if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
@@ -514,7 +559,7 @@ read_elf(Process *proc) {
 				   &sym) == NULL)
 			error(EXIT_FAILURE, 0,
 			      "Couldn't get relocation from \"%s\"",
-			      proc->filename);
+			      plt_filename);
 
 #ifdef PLT_REINITALISATION_BP
 		if (!sym.st_value && PLTs_initialized_by_here)
@@ -522,11 +567,12 @@ read_elf(Process *proc) {
 #endif
 
 		name = lte->dynstr + sym.st_name;
-		if (in_load_libraries(name, lte)) {
-			addr = arch_plt_sym_val(lte, i, &rela);
+		count = library_num ? library_num+1 : 0;
+		if (in_load_libraries(name, ltes, count, NULL)) {
+			addr = lte->base_addr + arch_plt_sym_val(lte, i, &rela);
 			add_library_symbol(addr, name, &library_symbols,
 					   (PLTS_ARE_EXECUTABLE(lte)
-					   ?  LS_TOPLT_EXEC : LS_TOPLT_POINT),
+					    ?	LS_TOPLT_EXEC : LS_TOPLT_POINT),
 					   ELF64_ST_BIND(sym.st_info) == STB_WEAK);
 			if (!lib_tail)
 				lib_tail = &(library_symbols->next);
@@ -557,7 +603,6 @@ read_elf(Process *proc) {
 			opt_x = main_cheat;
 	}
 #endif
-
 	for (i = 0; i < lte->symtab_count; ++i) {
 		GElf_Sym sym;
 		GElf_Addr addr;
@@ -566,7 +611,7 @@ read_elf(Process *proc) {
 		if (gelf_getsym(lte->symtab, i, &sym) == NULL)
 			error(EXIT_FAILURE, 0,
 			      "Couldn't get symbol from \"%s\"",
-			      proc->filename);
+			      plt_filename);
 
 		name = lte->strtab + sym.st_name;
 		addr = sym.st_value;
@@ -583,6 +628,29 @@ read_elf(Process *proc) {
 				break;
 			}
 	}
+	}
+
+	int found_count = 0;
+
+	for (xptr = opt_x; xptr; xptr = xptr->next) {
+		if (xptr->found)
+			continue;
+
+		GElf_Sym sym;
+		GElf_Addr addr;
+		if (in_load_libraries(xptr->name, ltes, library_num+1, &sym)) {
+			debug(2, "found symbol %s @ %lx, adding it.", xptr->name, sym.st_value);
+			addr = sym.st_value;
+			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;
+		}
+	}
+
 	for (xptr = opt_x; xptr; xptr = xptr->next)
 		if ( ! xptr->found) {
 			char *badthing = "WARNING";
@@ -592,7 +660,7 @@ read_elf(Process *proc) {
 					add_library_symbol (
 						opd2addr (lte, lte->ehdr.e_entry),
 						PLTs_initialized_by_here,
-						lib_tail, 1, 0);
+						lib_tail, LS_TOPLT_EXEC, 0);
 					fprintf (stderr, "WARNING: Using e_ent"
 						 "ry from elf header (%p) for "
 						 "address of \"%s\"\n", (void*)
@@ -605,15 +673,15 @@ read_elf(Process *proc) {
 			}
 #endif
 			fprintf (stderr,
-				 "%s: Couldn't find symbol \"%s\" in file \"%s"
-			         "\"\n", badthing, xptr->name, proc->filename);
+				 "%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);
 	}
 
 	for (i = 0; i < library_num + 1; ++i)
-		do_close_elf(&lte[i]);
+		do_close_elf(&ltes[i]);
 
 	return library_symbols;
 }
diff --git a/elf.h b/elf.h
index 426f7b8..60c4a7c 100644
--- a/elf.h
+++ b/elf.h
@@ -24,6 +24,10 @@ struct ltelf {
 	Elf32_Word *hash;
 	int hash_type;
 	int lte_flags;
+  GElf_Addr dyn_addr;
+  size_t dyn_sz;
+  size_t debug_offset;
+  GElf_Addr base_addr;
 #ifdef __mips__
 	size_t pltgot_addr;
 	size_t mips_local_gotno;
@@ -31,6 +35,8 @@ struct ltelf {
 #endif // __mips__
 };
 
+#define ELF_MAX_SEGMENTS  (50)
+#define ELF_SYMTAB_MAX    (5000)
 #define LTE_HASH_MALLOCED 1
 #define LTE_PLT_EXECUTABLE 2
 
diff --git a/handle_event.c b/handle_event.c
index 1dfb82b..408d29c 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -607,7 +607,10 @@ handle_breakpoint(Event *event) {
 	}
 
 	if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) {
-		if (event->proc->state != STATE_IGNORED) {
+		if (strcmp(sbp->libsym->name, "") == 0) {
+			debug(2, "Hit _dl_debug_state breakpoint!\n");
+			arch_check_dbg(event->proc);
+		} else if (event->proc->state != STATE_IGNORED) {
 			event->proc->stack_pointer = get_stack_pointer(event->proc);
 			event->proc->return_addr =
 				get_return_addr(event->proc, event->proc->stack_pointer);
diff --git a/ltrace.1 b/ltrace.1
index 358d6aa..1140c65 100644
--- a/ltrace.1
+++ b/ltrace.1
@@ -100,8 +100,9 @@ Display only the symbols included in the library
 Up to 30 library names can be specified with several instances
 of this option.
 .TP
-.I \-L
-DON'T display library calls (use it with the
+.I \-L, \-\-from\-library filename
+Display library calls from this library instead of from the executable.
+Use "none" to display no library calls (with the
 .I \-S
 option).
 .TP
diff --git a/options.c b/options.c
index aef73b1..a1013ae 100644
--- a/options.c
+++ b/options.c
@@ -23,7 +23,7 @@ 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 */
+	.libcalls = NULL,                /* display library calls from this library */
 #ifdef USE_DEMANGLE
 	.demangle = 0,                /* Demangle low-level symbol names */
 #endif
@@ -53,6 +53,7 @@ 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) */
@@ -86,8 +87,8 @@ usage(void) {
 		"  -F, --config=FILE   load alternate configuration file (may be repeated).\n"
 		"  -h, --help          display this help and exit.\n"
 		"  -i                  print instruction pointer at time of library call.\n"
-		"  -l, --library=FILE  print library calls from this library only.\n"
-		"  -L                  do NOT display library calls.\n"
+		"  -l, --library=FILE  print library calls into these libraries only.\n"
+		"  -L, --from-library=FILE print library calls from this library (instead of the executable).\n"
 		"  -n, --indent=NR     indent output by NR spaces for each call level nesting.\n"
 		"  -o, --output=FILE   write the trace output to that file.\n"
 		"  -p PID              attach to the process with the process ID pid.\n"
@@ -196,15 +197,16 @@ process_options(int argc, char **argv) {
 			{"indent", 1, 0, 'n'},
 			{"help", 0, 0, 'h'},
 			{"library", 1, 0, 'l'},
+			{"from-library", 1, 0, 'L'},
 			{"output", 1, 0, 'o'},
 			{"version", 0, 0, 'V'},
 			{0, 0, 0, 0}
 		};
-		c = getopt_long(argc, argv, "+cfhiLrStTV"
+		c = getopt_long(argc, argv, "+cfhirStTV"
 # ifdef USE_DEMANGLE
 				"C"
 # endif
-				"a:A:D:e:F:l:n:o:p:s:u:x:X:", long_options,
+				"a:A:D:e:F:l:L:n:o:p:s:u:x:X:", long_options,
 				&option_index);
 		if (c == -1) {
 			break;
@@ -299,7 +301,7 @@ process_options(int argc, char **argv) {
 			library[library_num++] = optarg;
 			break;
 		case 'L':
-			options.libcalls = 0;
+			options.libcalls = optarg;
 			break;
 		case 'n':
 			options.indent = atoi(optarg);
@@ -378,9 +380,11 @@ process_options(int argc, char **argv) {
 					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;
 			}
diff --git a/options.h b/options.h
index db253c5..4f3db0d 100644
--- a/options.h
+++ b/options.h
@@ -5,7 +5,7 @@ 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 */
+	char *libcalls; /* -L: display library calls from this library */
 	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 */
@@ -40,6 +40,7 @@ struct opt_F_t {
 struct opt_x_t {
 	char *name;
 	int found;
+	unsigned long hash;
 	struct opt_x_t *next;
 };
 
@@ -51,5 +52,6 @@ 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/proc.c b/proc.c
index bfc6e41..81e6ef7 100644
--- a/proc.c
+++ b/proc.c
@@ -46,6 +46,7 @@ open_pid(pid_t pid) {
 	}
 
 	proc = open_program(filename, pid);
+	trace_set_options(proc, pid);
 	continue_process(pid);
 	proc->breakpoints_enabled = 1;
 }
diff --git a/sysdeps/README b/sysdeps/README
index ce033ef..0a37909 100644
--- a/sysdeps/README
+++ b/sysdeps/README
@@ -30,3 +30,4 @@ char * pid2name(pid_t pid);
 void trace_me(void);
 int trace_pid(pid_t pid);
 void untrace_pid(pid_t pid);
+void linkmap_init(Process *, struct ltelf *);
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
index a1e2a14..8cd2066 100644
--- a/sysdeps/linux-gnu/events.c
+++ b/sysdeps/linux-gnu/events.c
@@ -19,7 +19,7 @@ next_event(void) {
 	int status;
 	int tmp;
 	int stop_signal;
-
+	struct ltelf lte;
 	debug(DEBUG_FUNCTION, "next_event()");
 	if (!list_of_processes) {
 		debug(DEBUG_EVENT, "event: No more traced programs: exiting");
@@ -49,13 +49,19 @@ next_event(void) {
 	event.proc->instruction_pointer = NULL;
 	debug(3, "event from pid %u", pid);
 	if (event.proc->breakpoints_enabled == -1) {
-		enable_all_breakpoints(event.proc);
 		event.type = EVENT_NONE;
 		trace_set_options(event.proc, event.proc->pid);
+		enable_all_breakpoints(event.proc);
 		continue_process(event.proc->pid);
 		debug(DEBUG_EVENT, "event: NONE: pid=%d (enabling breakpoints)", pid);
 		return &event;
+	} else if (!libdl_hooked) {
+		/* debug struct may not have been written yet.. */
+		if (linkmap_init(event.proc, &main_lte) == 0) {
+			libdl_hooked = 1;
+		}
 	}
+
 	if (opt_i) {
 		event.proc->instruction_pointer =
 			get_instruction_pointer(event.proc);
diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c
index 76f1105..634b5d3 100644
--- a/sysdeps/linux-gnu/i386/trace.c
+++ b/sysdeps/linux-gnu/i386/trace.c
@@ -1,5 +1,6 @@
 #include "config.h"
 
+#include <gelf.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/wait.h>
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index 4251c1d..476d121 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -1,6 +1,8 @@
 #include "config.h"
+#include "common.h"
 
 #include <sys/types.h>
+#include <link.h>
 #include <stdio.h>
 #include <string.h>
 #include <signal.h>
@@ -34,3 +36,283 @@ pid2name(pid_t pid) {
 	}
 	return NULL;
 }
+
+static int
+find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) {
+	int i = 0, done = 0;
+	ElfW(Dyn) entry;
+
+	debug(DEBUG_FUNCTION, "find_dynamic_entry()");
+
+	if (addr ==	NULL || pvAddr == NULL || 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)) {
+		if (entry.d_tag == d_tag) {
+			done = 1;
+			*addr = (void *)entry.d_un.d_val;
+		}
+		pvAddr += sizeof(entry);
+		i++;
+	}
+
+	if (done) {
+		debug(2, "found address: 0x%p in dtag %d\n", *addr, d_tag);
+		return 0;
+	}
+	else {
+		debug(2, "Couldn't address for dtag!\n");
+		return -1;
+	}
+}
+
+struct cb_data {
+	const char *lib_name;
+	struct ltelf *lte;
+	ElfW(Addr) addr;
+	Process *proc;
+};
+
+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;
+
+	debug (DEBUG_FUNCTION, "crawl_linkmap()");
+
+	if (!dbg || !dbg->r_map) {
+		debug(2, "Debug structure or it's linkmap are NULL!");
+		return;
+	}
+
+	lm = dbg->r_map;
+
+	while (lm) {
+		if (umovebytes(proc, lm, &rlm, sizeof(rlm)) != sizeof(rlm)) {
+			debug(2, "Unable to read link map\n");
+			return;
+		}
+
+		lm = rlm.l_next;
+		if (rlm.l_name == NULL) {
+			debug(2, "Invalid library name referenced in dynamic linker map\n");
+			return;
+		}
+
+		umovebytes(proc, rlm.l_name, lib_name, sizeof(lib_name));
+
+		if (lib_name[0] == '\0') {
+			debug(2, "Library name is an empty string");
+			continue;
+		}
+
+		if (callback) {
+			debug(2, "Dispatching callback for: %s, Loaded at 0x%x\n", lib_name, rlm.l_addr);
+			data->addr = rlm.l_addr;
+			data->lib_name = lib_name;
+			callback(data);
+		}
+	}
+	return;
+}
+
+static struct r_debug *
+load_debug_struct(Process *proc) {
+	struct r_debug *rdbg = NULL;
+
+	debug(DEBUG_FUNCTION, "load_debug_struct");
+
+	rdbg = malloc(sizeof(*rdbg));
+	if (!rdbg) {
+		return NULL;
+	}
+
+	if (umovebytes(proc, proc->debug, rdbg, sizeof(*rdbg)) != sizeof(*rdbg)) {
+		debug(2, "This process does not have a debug structure!\n");
+		free(rdbg);
+		return NULL;
+	}
+
+	return rdbg;
+}
+
+static void
+linkmap_add_cb(void *data) { //const char *lib_name, ElfW(Addr) addr) {
+	int 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;
+			struct library_symbol *lsym;
+
+			if (in_load_libraries(xptr->name, &lte, 1, &sym)) {
+				debug(2, "found symbol %s @ %lx, 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);
+			}
+		}
+		do_close_elf(&lte);
+	}
+}
+
+void
+arch_check_dbg(Process *proc) {
+	struct r_debug *dbg = NULL;
+	struct cb_data data;
+
+	debug(DEBUG_FUNCTION, "arch_check_dbg");
+
+	if (!(dbg = load_debug_struct(proc))) {
+		debug(2, "Unable to load debug structure!");
+		return;
+	}
+
+	if (dbg->r_state == RT_CONSISTENT) {
+		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);
+		} else if (proc->debug_state == RT_DELETE) {
+			debug(2, "Removing DSO from linkmap");
+		} else {
+			debug(2, "Unexpected debug state!");
+		}
+	}
+
+	proc->debug_state = dbg->r_state;
+
+	return;
+}
+
+#if 0
+
+/*(XXX TODO)
+
+	 support removal of libraries loaded with dlsym:
+		- should be fairly easy, but some refactoring needed
+		- create a mapping between libraries and their breakpoints
+		- once a library "disappears" form the linkmap, remove each breakpoint
+*/
+
+static void
+check_still_there(void *data) {
+	debug(DEBUG_FUNCTION, "check_still_there");
+
+	/* check if lib found in link map matches the library[i] we are searching
+		 for */
+}
+
+static void
+linkmap_remove() {
+	int i = 0;
+	debug(DEBUG_FUNCTION, "linkmap_remove");
+	/*
+		 XXX
+		 for each library[i], see if its still in the linkmap. 
+	 */
+
+	data.lib_name2find = lib_name;
+
+	/* store some search state in data, too */
+
+	for(;i < library_num;i++) {
+		crawl_linkmap(proc, lte, debug struct, check_still_there, data); 
+	}
+}
+
+#endif
+
+static void
+hook_libdl_cb(void *data) { //const char *lib_name, ElfW(Addr) addr) {
+	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) {
+		library[library_num++] = strdup(lib_name);
+		lte[library_num].base_addr = addr;
+	}
+	else {
+		fprintf (stderr, "MAX LIBS REACHED\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+int
+linkmap_init(Process *proc, struct ltelf *lte) {
+	void *dbg_addr = NULL;
+	struct r_debug *rdbg = NULL;
+	struct cb_data data;
+
+	debug(DEBUG_FUNCTION, "linkmap_init()");
+
+	if (find_dynamic_entry_addr(proc, (void *)lte->dyn_addr, DT_DEBUG, &dbg_addr) == -1) {
+		debug(2, "Couldn't find debug structure!");
+		return -1;
+	}
+
+	proc->debug = dbg_addr;
+
+	if (!(rdbg = load_debug_struct(proc))) {
+		debug(2, "No debug structure or no memory to allocate one!");
+		return -1;
+	}
+
+	data.lte = lte;
+
+	add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0);
+	insert_breakpoint(proc, sym2addr(proc, library_symbols), library_symbols);
+
+	crawl_linkmap(proc, rdbg, hook_libdl_cb, &data);
+
+	free(rdbg);
+	return 0;
+}
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index df5b090..5801d24 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -163,6 +163,30 @@ continue_after_breakpoint(Process *proc, Breakpoint *sbp) {
 	}
 }
 
+size_t
+umovebytes(Process *proc, void *src, void *dest, size_t count) {
+  size_t bytes_read = 0;
+  long word = 0;
+  void *src_iter = src, *dest_iter = dest;
+  int min = 0, max = 0;
+
+  while (bytes_read < count) {
+    word = ptrace(PTRACE_PEEKDATA, proc->pid, src_iter, NULL);
+    if (word == -1) {
+      return bytes_read; 
+    }
+
+    min = ((src_iter < src) ? (src - src_iter) : 0);
+    max = ((count - bytes_read) > sizeof(word) ? sizeof(word) : (count - bytes_read));
+    memcpy(dest_iter, (void *) ((unsigned long) &word + min), max - min);
+    bytes_read += max - min;
+    src_iter += sizeof(word);
+    dest_iter += max - min;
+
+  }
+  return bytes_read;
+}
+
 /* Read a series of bytes starting at the process's memory address
    'addr' and continuing until a NUL ('\0') is seen or 'len' bytes
    have been read.
_______________________________________________
Ltrace-devel mailing list
[email protected]
http://lists.alioth.debian.org/mailman/listinfo/ltrace-devel

Reply via email to