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 : <es[0]; debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename); + memset(<es, 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(<e[i + 1], library[i]); + do_init_elf(<es[0], proc->filename); + + memcpy(&main_lte, <es[0], sizeof(struct ltelf)); + + if (opt_p && opt_p->pid > 0) { + linkmap_init(proc, <es[0]); + libdl_hooked = 1; + } + + proc->e_machine = ltes[0].ehdr.e_machine; + for (i = 0; i < library_num; ++i) { + do_init_elf(<es[i + 1], library[i]); + if (options.libcalls + && strcmp(library[i], options.libcalls) == 0) { + lte = <es[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(<e[i]); + do_close_elf(<es[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(<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; + struct library_symbol *lsym; + + 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; +} + +#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
