Add support for tracing functions in libraries loaded at runtime (via libdl).
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index 86ad301..d11b61b 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -68,3 +68,212 @@
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(<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 @ %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(<e);
+ }
+}
+
+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;
+}
+
+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) {
+ 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;
+}