diff --git a/common.h b/common.h
index c672913..39d5f86 100644
--- a/common.h
+++ b/common.h
@@ -217,6 +217,8 @@ extern void enable_all_breakpoints(Process * proc);
 extern void disable_all_breakpoints(Process * proc);
 extern void reinitialize_breakpoints(Process *);
 
+extern void linkmap_init(Process * proc, struct ltelf *lte);
+
 extern Process * open_program(char * filename, pid_t pid);
 extern void open_pid(pid_t pid);
 extern void show_summary(void);
@@ -245,6 +247,7 @@ 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 *);
 
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..8b8597a 100644
--- a/elf.c
+++ b/elf.c
@@ -5,6 +5,7 @@
 #include <error.h>
 #include <fcntl.h>
 #include <gelf.h>
+#include <link.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -17,9 +18,11 @@ static void do_close_elf(struct ltelf *lte);
 static 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);
+static int in_load_libraries(const char *name, struct ltelf *lte, GElf_Sym *sym);
 static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr);
 
+static struct library_symbol *library_symbols = NULL;
+
 #ifdef PLT_REINITALISATION_BP
 extern char *PLTs_initialized_by_here;
 #endif
@@ -33,7 +36,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 +143,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)
@@ -186,6 +191,10 @@ do_init_elf(struct ltelf *lte, const char *filename) {
 					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_DEBUG) {
+          lte->debug_offset = sizeof(GElf_Dyn) * j;
+          debug(2, "vma of dynamic: %lx:%lx debug offset: %zd bytes: %zd\n", shdr.sh_addr, shdr.sh_size, j, lte->debug_offset);
+        }
 			}
 		} else if (shdr.sh_type == SHT_HASH) {
 			Elf_Data *data;
@@ -280,7 +289,7 @@ do_init_elf(struct ltelf *lte, const char *filename) {
 	if (!relplt_addr || !lte->plt_addr) {
 		debug(1, "%s has no PLT relocations", filename);
 		lte->relplt = NULL;
-		lte->relplt_count = 0;
+    lte->relplt_count = 0;
 	} else {
 		for (i = 1; i < lte->ehdr.e_shnum; ++i) {
 			Elf_Scn *scn;
@@ -302,7 +311,7 @@ do_init_elf(struct ltelf *lte, const char *filename) {
 					      "Couldn't get .rel*.plt data from \"%s\"",
 					      filename);
 				break;
-			}
+      }
 		}
 
 		if (i == lte->ehdr.e_shnum)
@@ -359,7 +368,7 @@ private_elf_gnu_hash(const char *name) {
 }
 
 static int
-in_load_libraries(const char *name, struct ltelf *lte) {
+in_load_libraries(const char *name, struct ltelf *lte, GElf_Sym *sym) {
 	size_t i;
 	unsigned long hash;
 	unsigned long gnu_hash;
@@ -394,15 +403,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(2, "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 +430,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;
+
+				tmp = (sym) ? (sym) : (&tmp_sym);
 
-				if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL)
+				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(2, "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;
 			}
 		}
@@ -451,9 +472,108 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) {
 #endif
 }
 
+
+static int
+find_dynamic_entry(Process *proc, void *pvAddr, Elf64_Sxword d_tag, GElf_Dyn *entry) {
+	int i = 0, done = 0;
+
+	debug(DEBUG_FUNCTION, "find_dynamic_entry()");
+
+	if (entry == 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;
+		pvAddr += sizeof(*entry);
+		i++;
+	}
+
+	if (done) {
+		debug(2, "found address: 0x%lx in dtag %ld\n", entry->d_un.d_val, d_tag);
+		return(0);
+	}
+	else {
+		debug(2, "Couldn't address for dtag!\n");
+		return(-1);
+	}
+}
+
+static void
+crawl_linkmap(Process *proc, struct ltelf *lte, struct link_map *lm) {
+  struct link_map rlm;
+  char lib_name[BUFSIZ];
+
+  debug (DEBUG_FUNCTION, "crawl_linkmap()");
+  
+  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;
+    }
+
+    debug(2, "Object %s, Loaded at 0x%lx\n", lib_name, rlm.l_addr);
+
+    if (library_num < MAX_LIBRARIES) {
+      library[library_num++] = strdup(lib_name);
+      lte[library_num].base_addr = rlm.l_addr;
+    }
+    else {
+      fprintf (stderr, "MAX LIBS REACHED\n");
+      exit(EXIT_FAILURE);
+    }
+  }
+  return;
+}
+
+
+void
+linkmap_init(Process *proc, struct ltelf *lte) {
+  GElf_Dyn tmp;
+  struct r_debug rdbg, *tdbg = NULL;
+  struct link_map *tlink_map = NULL;
+
+  debug(DEBUG_FUNCTION, "linkmap_init()");
+
+  if (find_dynamic_entry(proc, (void *)lte->dyn_addr, DT_DEBUG, &tmp) == -1) {
+    debug(2, "Couldn't find debug structure!");
+    return;
+  }
+  
+  tdbg = (struct r_debug *) tmp.d_un.d_val;
+  /* XXX
+     this isn't actually a failure case - we need to set breakpoints on
+     dl_debug_state and then re-try this function.
+   */
+  if (umovebytes(proc, tdbg, &rdbg, sizeof(rdbg)) != sizeof(rdbg)) {
+    debug(2, "This process does not have a debug structure!\n");
+    return;
+  }
+
+  tlink_map = rdbg.r_map;
+  crawl_linkmap(proc, lte, tlink_map);
+
+  return;
+}
+
 struct library_symbol *
 read_elf(Process *proc) {
-	struct library_symbol *library_symbols = NULL;
 	struct ltelf lte[MAX_LIBRARIES + 1];
 	size_t i;
 	struct opt_x_t *xptr;
@@ -465,9 +585,15 @@ read_elf(Process *proc) {
 	elf_version(EV_CURRENT);
 
 	do_init_elf(lte, proc->filename);
+
+	linkmap_init(proc, lte);
+
 	proc->e_machine = lte->ehdr.e_machine;
-	for (i = 0; i < library_num; ++i)
+
+	for (i = 0; i < library_num; ++i) {
 		do_init_elf(&lte[i + 1], library[i]);
+	}
+
 #ifdef __mips__
 	// MIPS doesn't use the PLT and the GOT entries get changed
 	// on startup.
@@ -522,7 +648,7 @@ read_elf(Process *proc) {
 #endif
 
 		name = lte->dynstr + sym.st_name;
-		if (in_load_libraries(name, lte)) {
+		if (in_load_libraries(name, lte, NULL)) {
 			addr = arch_plt_sym_val(lte, i, &rela);
 			add_library_symbol(addr, name, &library_symbols,
 					   (PLTS_ARE_EXECUTABLE(lte)
@@ -583,6 +709,28 @@ 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, lte, &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-1){
+			debug(2, "done, found everything: %d\n", found_count);
+			break;
+		}
+	}
+
 	for (xptr = opt_x; xptr; xptr = xptr->next)
 		if ( ! xptr->found) {
 			char *badthing = "WARNING";
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/options.c b/options.c
index aef73b1..4a54fa3 100644
--- a/options.c
+++ b/options.c
@@ -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) */
@@ -378,9 +379,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..9500add 100644
--- a/options.h
+++ b/options.h
@@ -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/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index df5b090..1ce8c4e 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -84,6 +84,8 @@ trace_pid(pid_t pid) {
 		exit (1);
 	}
 
+  
+
 	return 0;
 }
 
@@ -163,6 +165,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 (errno != 0) {
+      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.
