diff options
author | Petr Machata <pmachata@redhat.com> | 2012-02-18 11:17:29 +0100 |
---|---|---|
committer | Petr Machata <pmachata@redhat.com> | 2012-04-19 00:57:35 +0200 |
commit | 2b46cfc1127d390eddd9593fe5ce5399c1f68130 (patch) | |
tree | be4ce983280d64681bc3eaa295fc0837eac0b833 /sysdeps | |
parent | a7db59c355cef464073496221ad27519a48466f9 (diff) | |
download | ltrace-2b46cfc1127d390eddd9593fe5ce5399c1f68130.tar.gz |
The first crude version of tracing across libraries
- the patch will be sliced later
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/linux-gnu/arm/arch.h | 5 | ||||
-rw-r--r-- | sysdeps/linux-gnu/arm/breakpoint.c | 11 | ||||
-rw-r--r-- | sysdeps/linux-gnu/arm/regs.c | 6 | ||||
-rw-r--r-- | sysdeps/linux-gnu/breakpoint.c | 1 | ||||
-rw-r--r-- | sysdeps/linux-gnu/events.c | 12 | ||||
-rw-r--r-- | sysdeps/linux-gnu/ppc/plt.c | 3 | ||||
-rw-r--r-- | sysdeps/linux-gnu/proc.c | 206 | ||||
-rw-r--r-- | sysdeps/linux-gnu/trace.c | 52 | ||||
-rw-r--r-- | sysdeps/linux-gnu/x86_64/plt.c | 1 |
9 files changed, 135 insertions, 162 deletions
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 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) 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 @@ void * 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 event; 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 @@ next_event(void) 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 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { 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 @@ sym2addr(Process *proc, struct library_symbol *sym) { 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 @@ find_line_starting(FILE * file, const char * prefix, size_t len) } 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 @@ process_leader(pid_t pid) 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 @@ process_stopped(pid_t pid) 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 @@ process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) 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 @@ find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { 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 @@ find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { } } -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 @@ crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), stru 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 @@ load_debug_struct(Process *proc) { } 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(<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 @ %#" 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(<e); - } -} - -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 @@ arch_check_dbg(Process *proc) { 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 @@ arch_check_dbg(Process *proc) { } 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 @@ linkmap_init(Process *proc, struct ltelf *lte) { 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 @@ add_task_info(struct pid_set * pids, pid_t pid) 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 @@ task_stopped(Process * task, void * data) 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 @@ is_vfork_parent(Process * task) 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 @@ send_sigstop(Process * task, void * data) 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 @@ send_sigstop(Process * task, void * data) 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 @@ send_sigstop(Process * task, void * data) 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 @@ undo_breakpoint(Event * event, void * data) 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) { |