aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorPetr Machata <pmachata@redhat.com>2012-02-18 11:17:29 +0100
committerPetr Machata <pmachata@redhat.com>2012-04-19 00:57:35 +0200
commit2b46cfc1127d390eddd9593fe5ce5399c1f68130 (patch)
treebe4ce983280d64681bc3eaa295fc0837eac0b833 /sysdeps
parenta7db59c355cef464073496221ad27519a48466f9 (diff)
downloadltrace-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.h5
-rw-r--r--sysdeps/linux-gnu/arm/breakpoint.c11
-rw-r--r--sysdeps/linux-gnu/arm/regs.c6
-rw-r--r--sysdeps/linux-gnu/breakpoint.c1
-rw-r--r--sysdeps/linux-gnu/events.c12
-rw-r--r--sysdeps/linux-gnu/ppc/plt.c3
-rw-r--r--sysdeps/linux-gnu/proc.c206
-rw-r--r--sysdeps/linux-gnu/trace.c52
-rw-r--r--sysdeps/linux-gnu/x86_64/plt.c1
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(&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;
-
- if (in_load_libraries(xptr->name, &lte, 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(&lte);
- }
-}
-
-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) {