aboutsummaryrefslogtreecommitdiff
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
parenta7db59c355cef464073496221ad27519a48466f9 (diff)
downloadltrace-2b46cfc1127d390eddd9593fe5ce5399c1f68130.tar.gz
The first crude version of tracing across libraries
- the patch will be sliced later
-rw-r--r--Makefile.am6
-rw-r--r--breakpoint.h21
-rw-r--r--breakpoints.c179
-rw-r--r--common.h34
-rw-r--r--defs.h2
-rw-r--r--handle_event.c104
-rw-r--r--libltrace.c6
-rw-r--r--library.c207
-rw-r--r--library.h149
-rw-r--r--ltrace-elf.c543
-rw-r--r--ltrace-elf.h34
-rw-r--r--options.c12
-rw-r--r--output.h9
-rw-r--r--proc.c297
-rw-r--r--proc.h93
-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
24 files changed, 1117 insertions, 876 deletions
diff --git a/Makefile.am b/Makefile.am
index 6c299d8..7c9faf6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,7 +24,8 @@ libltrace_la_SOURCES = \
output.c \
proc.c \
read_config_file.c \
- summary.c
+ summary.c \
+ library.c
libltrace_la_LIBADD = \
$(libelf_LIBS) \
@@ -56,7 +57,8 @@ noinst_HEADERS = \
ltrace.h \
options.h \
output.h \
- read_config_file.h
+ read_config_file.h \
+ library.h
dist_man1_MANS = \
ltrace.1
diff --git a/breakpoint.h b/breakpoint.h
index ce6f501..53cdbf2 100644
--- a/breakpoint.h
+++ b/breakpoint.h
@@ -57,6 +57,7 @@
*/
#include "arch.h"
+#include "library.h"
struct Process;
struct breakpoint;
@@ -66,15 +67,18 @@ struct bp_callbacks {
void (*on_destroy) (struct breakpoint *bp);
};
+#ifndef ARCH_HAVE_BREAKPOINT_DATA
+struct arch_breakpoint_data {
+};
+#endif
+
struct breakpoint {
struct bp_callbacks *cbs;
void *addr;
unsigned char orig_value[BREAKPOINT_LENGTH];
int enabled;
struct library_symbol *libsym;
-#ifdef __arm__
- int thumb_mode;
-#endif
+ struct arch_breakpoint_data arch;
};
/* Call on-hit handler of BP, if any is set. */
@@ -83,6 +87,14 @@ void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc);
/* Call on-destroy handler of BP, if any is set. */
void breakpoint_on_destroy(struct breakpoint *bp);
+/* Initialize a breakpoint structure. That doesn't actually realize
+ * the breakpoint. The breakpoint is initially assumed to be
+ * disabled. orig_value has to be set separately. CBS may be
+ * NULL. */
+int breakpoint_init(struct breakpoint *bp, struct Process *proc,
+ target_address_t addr, struct library_symbol *libsym,
+ struct bp_callbacks *cbs);
+
/* This is actually three functions rolled in one:
* - breakpoint_init
* - proc_insert_breakpoint
@@ -100,7 +112,4 @@ void enable_all_breakpoints(struct Process *proc);
void disable_all_breakpoints(struct Process *proc);
int breakpoints_init(struct Process *proc, int enable);
-void reinitialize_breakpoints(struct Process *proc);
-
-
#endif /* BREAKPOINT_H */
diff --git a/breakpoints.c b/breakpoints.c
index 5a473a9..f0f8db4 100644
--- a/breakpoints.c
+++ b/breakpoints.c
@@ -3,6 +3,8 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <error.h>
+#include <errno.h>
#ifdef __powerpc__
#include <sys/ptrace.h>
@@ -11,6 +13,7 @@
#include "breakpoint.h"
#include "common.h"
#include "proc.h"
+#include "library.h"
void
breakpoint_on_hit(struct breakpoint *bp, struct Process *proc)
@@ -40,12 +43,33 @@ address2bpstruct(Process *proc, void *addr)
return dict_find_entry(proc->breakpoints, addr);
}
+#ifdef ARCH_HAVE_BREAKPOINT_DATA
+int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
+#else
+int
+arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp)
+{
+ return 0;
+}
+#endif
+
+int
+breakpoint_init(struct breakpoint *bp, struct Process *proc,
+ target_address_t addr, struct library_symbol *libsym,
+ struct bp_callbacks *cbs)
+{
+ bp->cbs = cbs;
+ bp->addr = addr;
+ memset(bp->orig_value, 0, sizeof(bp->orig_value));
+ bp->enabled = 0;
+ bp->libsym = libsym;
+ return arch_breakpoint_init(proc, bp);
+}
+
struct breakpoint *
insert_breakpoint(Process *proc, void *addr,
struct library_symbol *libsym, int enable)
{
- struct breakpoint *sbp;
-
Process * leader = proc->leader;
/* Only the group leader should be getting the breakpoints and
@@ -53,12 +77,6 @@ insert_breakpoint(Process *proc, void *addr,
assert(leader != NULL);
assert(leader->breakpoints != NULL);
-#ifdef __arm__
- int thumb_mode = (int)addr & 1;
- if (thumb_mode)
- addr = (void *)((int)addr & ~1);
-#endif
-
debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL");
debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
@@ -68,20 +86,17 @@ insert_breakpoint(Process *proc, void *addr,
if (libsym)
libsym->needs_init = 0;
- sbp = dict_find_entry(leader->breakpoints, addr);
+ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
if (sbp == NULL) {
- sbp = calloc(1, sizeof(*sbp));
- if (sbp == NULL) {
- return NULL; /* TODO FIXME XXX: error_mem */
+ sbp = malloc(sizeof(*sbp));
+ if (sbp == NULL
+ || breakpoint_init(sbp, proc, addr, libsym, NULL) < 0
+ || dict_enter(leader->breakpoints, addr, sbp) < 0) {
+ free(sbp);
+ return NULL;
}
- dict_enter(leader->breakpoints, addr, sbp);
- sbp->addr = addr;
- sbp->libsym = libsym;
}
-#ifdef __arm__
- sbp->thumb_mode = thumb_mode | proc->thumb_mode;
- proc->thumb_mode = 0;
-#endif
+
sbp->enabled++;
if (sbp->enabled == 1 && enable) {
assert(proc->pid != 0);
@@ -177,108 +192,80 @@ disable_all_breakpoints(Process *proc) {
dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
}
-static void
-free_bp_cb(void *addr, void *sbp, void *data) {
- debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp);
- assert(sbp);
- free(sbp);
+static enum callback_status
+reinitialize_breakpoints(struct Process *proc, struct library *library,
+ void *data)
+{
+ debug(DEBUG_FUNCTION, "reinitialize_breakpoints_in(pid=%d, %s)",
+ proc->pid, library->name);
+
+ struct library_symbol *sym;
+ for (sym = library->symbols; sym != NULL; sym = sym->next)
+ if (sym->needs_init) {
+ target_address_t addr = sym2addr(proc, sym);
+ if (insert_breakpoint(proc, addr, sym, 1) == NULL
+ || (sym->needs_init && !sym->is_weak))
+ fprintf(stderr,
+ "could not re-initialize breakpoint "
+ "for \"%s\" in file \"%s\"\n",
+ sym->name, proc->filename);
+ }
+
+ return CBS_CONT;
}
static void
entry_callback_hit(struct breakpoint *bp, struct Process *proc)
{
+ fprintf(stderr, "entry_callback_hit\n");
if (proc == NULL || proc->leader == NULL)
return;
delete_breakpoint(proc, bp->addr); // xxx
- reinitialize_breakpoints(proc->leader);
+
+ linkmap_init(proc);
+ proc_each_library(proc->leader, NULL, reinitialize_breakpoints, NULL);
}
int
breakpoints_init(Process *proc, int enable)
{
+ fprintf(stderr, "breakpoints_init %d enable=%d\n", proc->pid, enable);
debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid);
- if (proc->breakpoints) { /* let's remove that struct */
- dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL);
- dict_clear(proc->breakpoints);
- proc->breakpoints = NULL;
- }
- /* Only the thread group leader should hold the breakpoints.
- * (N.B. PID may be set to 0 temporarily when called by
- * handle_exec). */
- assert(proc->leader == proc);
-
- proc->breakpoints = dict_init(dict_key2hash_int,
- dict_key_cmp_int);
+ /* XXX breakpoint dictionary should be initialized
+ * outside. Here we just put in breakpoints. */
+ assert(proc->breakpoints != NULL);
- destroy_library_symbol_chain(proc->list_of_symbols);
- proc->list_of_symbols = NULL;
+ /* Only the thread group leader should hold the breakpoints. */
+ assert(proc->leader == proc);
- GElf_Addr entry;
if (options.libcalls && proc->filename) {
- proc->list_of_symbols = read_elf(proc, &entry);
- if (proc->list_of_symbols == NULL) {
+ struct library *lib = ltelf_read_main_binary(proc, proc->filename);
+ switch (lib != NULL) {
fail:
- /* XXX leak breakpoints */
+ proc_remove_library(proc, lib);
+ library_destroy(lib);
+ case 0:
return -1;
}
-
- if (opt_e) {
- struct library_symbol **tmp1 = &proc->list_of_symbols;
- while (*tmp1) {
- struct opt_e_t *tmp2 = opt_e;
- int keep = !opt_e_enable;
-
- while (tmp2) {
- if (!strcmp((*tmp1)->name,
- tmp2->name)) {
- keep = opt_e_enable;
- }
- tmp2 = tmp2->next;
- }
- if (!keep) {
- *tmp1 = (*tmp1)->next;
- } else {
- tmp1 = &((*tmp1)->next);
- }
- }
+ proc_add_library(proc, lib);
+ fprintf(stderr, "note: symbols in %s were not filtered.\n",
+ lib->name);
+
+ struct breakpoint *entry_bp
+ = insert_breakpoint(proc, lib->entry, NULL, 1);
+ if (entry_bp == NULL) {
+ error(0, errno, "couldn't insert entry breakpoint");
+ goto fail;
}
- }
- struct breakpoint *entry_bp
- = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1);
- if (entry_bp == NULL) {
- fprintf(stderr, "fail!\n");
- goto fail;
+ fprintf(stderr, "setting entry_callbacks by hand, fix it\n");
+ static struct bp_callbacks entry_callbacks = {
+ .on_hit = entry_callback_hit,
+ };
+ entry_bp->cbs = &entry_callbacks;
}
- static struct bp_callbacks entry_callbacks = {
- .on_hit = entry_callback_hit,
- };
- entry_bp->cbs = &entry_callbacks;
-
proc->callstack_depth = 0;
return 0;
}
-
-void
-reinitialize_breakpoints(Process *proc) {
- struct library_symbol *sym;
-
- debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid);
-
- sym = proc->list_of_symbols;
-
- while (sym) {
- if (sym->needs_init) {
- insert_breakpoint(proc, sym2addr(proc, sym), sym, 1);
- if (sym->needs_init && !sym->is_weak) {
- fprintf(stderr,
- "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
- sym->name, proc->filename);
- exit(1);
- }
- }
- sym = sym->next;
- }
-}
diff --git a/common.h b/common.h
index 0d56a2b..087b2bc 100644
--- a/common.h
+++ b/common.h
@@ -14,6 +14,7 @@
#include "debug.h"
#include "ltrace-elf.h"
#include "read_config_file.h"
+#include "proc.h"
#if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__
# define USE_DEMANGLE
@@ -113,24 +114,9 @@ struct Function {
Function * next;
};
-enum toplt {
- LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */
- LS_TOPLT_EXEC, /* PLT for this symbol is executable. */
- LS_TOPLT_POINT /* PLT for this symbol is a non-executable. */
-};
-
extern Function * list_of_functions;
extern char *PLTs_initialized_by_here;
-struct library_symbol {
- const char *name;
- void * enter_addr;
- char needs_init;
- enum toplt plt_type;
- char is_weak;
- struct library_symbol * next;
-};
-
struct opt_c_struct {
int count;
struct timeval tv;
@@ -174,22 +160,8 @@ extern void disable_all_breakpoints(Process * proc);
extern void show_summary(void);
extern arg_type_info * lookup_prototype(enum arg_type at);
-extern int 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 library_symbol_init(struct library_symbol *libsym,
- GElf_Addr addr, const char *name,
- enum toplt type_of_plt, int is_weak);
-extern void add_library_symbol(GElf_Addr addr, const char *name,
- struct library_symbol **library_symbolspp,
- enum toplt type_of_plt, int is_weak);
-
-extern struct library_symbol * clone_library_symbol(struct library_symbol * s);
-extern void destroy_library_symbol(struct library_symbol * s);
-extern void destroy_library_symbol_chain(struct library_symbol * chain);
-
struct breakpoint;
+struct library_symbol;
/* Arch-dependent stuff: */
extern char * pid2name(pid_t pid);
@@ -223,7 +195,7 @@ extern int umovelong (Process * proc, void * addr, long * result, arg_type_info
extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count);
extern int ffcheck(void * maddr);
extern void * sym2addr(Process *, struct library_symbol *);
-extern int linkmap_init(Process *, struct ltelf *);
+extern int linkmap_init(struct Process *);
extern void arch_check_dbg(Process *proc);
extern int task_kill (pid_t pid, int sig);
diff --git a/defs.h b/defs.h
index dea000b..1eadb47 100644
--- a/defs.h
+++ b/defs.h
@@ -14,5 +14,3 @@
#ifndef DEFAULT_ARRAYLEN
#define DEFAULT_ARRAYLEN 4 /* default maximum # array elements */
#endif /* (-A switch) */
-
-#define MAX_LIBRARIES 200
diff --git a/handle_event.c b/handle_event.c
index afabd96..bdd9af9 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -16,6 +16,7 @@
#include "common.h"
#include "breakpoint.h"
#include "proc.h"
+#include "library.h"
static void handle_signal(Event *event);
static void handle_exit(Event *event);
@@ -149,37 +150,6 @@ handle_event(Event *event)
}
}
-/* TODO */
-static void *
-address_clone(void * addr, void * data)
-{
- debug(DEBUG_FUNCTION, "address_clone(%p)", addr);
- return addr;
-}
-
-static void *
-breakpoint_clone(void *bp, void *data)
-{
- Dict *map = data;
- debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp);
- struct breakpoint *b = malloc(sizeof(*b));
- if (!b) {
- perror("malloc()");
- exit(1);
- }
- memcpy(b, bp, sizeof(*b));
- if (b->libsym != NULL) {
- struct library_symbol *sym = dict_find_entry(map, b->libsym);
- if (b->libsym == NULL) {
- fprintf(stderr, "Can't find cloned symbol %s.\n",
- b->libsym->name);
- return NULL;
- }
- b->libsym = sym;
- }
- return b;
-}
-
typedef struct Pending_New Pending_New;
struct Pending_New {
pid_t pid;
@@ -241,6 +211,7 @@ pending_new_remove(pid_t pid) {
}
}
+#if 0
static int
clone_breakpoints(Process * proc, Process * orig_proc)
{
@@ -274,50 +245,48 @@ clone_breakpoints(Process * proc, Process * orig_proc)
dict_clear(map);
return 0;
}
+#endif
static void
-handle_clone(Event * event) {
- Process *p;
-
+handle_clone(Event *event)
+{
debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid);
- p = malloc(sizeof(Process));
- if (!p) {
+ struct Process *proc = malloc(sizeof(*proc));
+ if (proc == NULL) {
+ fail:
+ free(proc);
+ /* XXX proper error handling here, please. */
perror("malloc()");
exit(1);
}
- memcpy(p, event->proc, sizeof(Process));
- p->pid = event->e_un.newpid;
- p->parent = event->proc;
+
+ if (process_clone(proc, event->proc, event->e_un.newpid) < 0)
+ goto fail;
+ proc->parent = event->proc;
/* We save register values to the arch pointer, and these need
to be per-thread. */
- p->arch_ptr = NULL;
-
- if (pending_new(p->pid)) {
- pending_new_remove(p->pid);
- if (p->event_handler != NULL)
- destroy_event_handler(p);
- if (event->proc->state == STATE_ATTACHED && options.follow) {
- p->state = STATE_ATTACHED;
- } else {
- p->state = STATE_IGNORED;
- }
- continue_process(p->pid);
- add_process(p);
+ proc->arch_ptr = NULL;
+
+ if (pending_new(proc->pid)) {
+ pending_new_remove(proc->pid);
+ /* XXX this used to be destroy_event_handler call, but
+ * I don't think we want to call that on a shared
+ * state. */
+ proc->event_handler = NULL;
+ if (event->proc->state == STATE_ATTACHED && options.follow)
+ proc->state = STATE_ATTACHED;
+ else
+ proc->state = STATE_IGNORED;
+ continue_process(proc->pid);
} else {
- p->state = STATE_BEING_CREATED;
- add_process(p);
+ proc->state = STATE_BEING_CREATED;
}
-
- if (p->leader == p)
- clone_breakpoints(p, event->proc->leader);
- else
- /* Thread groups share breakpoints. */
- p->breakpoints = NULL;
+ add_process(proc);
if (event->type == EVENT_VFORK)
- continue_after_vfork(p);
+ continue_after_vfork(proc);
else
continue_process(event->proc->pid);
}
@@ -676,17 +645,8 @@ handle_breakpoint(Event *event)
if ((sbp = address2bpstruct(leader, brk_addr))) {
breakpoint_on_hit(sbp, event->proc);
- if (sbp->libsym == NULL) {
- continue_after_breakpoint(event->proc, sbp);
- return;
- }
-
- if (strcmp(sbp->libsym->name, "") == 0) {
- debug(DEBUG_PROCESS, "Hit _dl_debug_state breakpoint!\n");
- arch_check_dbg(leader);
- }
-
- if (event->proc->state != STATE_IGNORED) {
+ if (event->proc->state != STATE_IGNORED
+ && sbp->libsym != NULL) {
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/libltrace.c b/libltrace.c
index dcd5537..2295901 100644
--- a/libltrace.c
+++ b/libltrace.c
@@ -16,8 +16,8 @@ char *command = NULL;
int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */
-static enum pcb_status
-stop_non_p_processes (Process * proc, void * data)
+static enum callback_status
+stop_non_p_processes(Process *proc, void *data)
{
int stop = 1;
@@ -39,7 +39,7 @@ stop_non_p_processes (Process * proc, void * data)
kill(proc->pid, SIGSTOP);
}
- return pcb_cont;
+ return CBS_CONT;
}
static void
diff --git a/library.c b/library.c
new file mode 100644
index 0000000..56a47c4
--- /dev/null
+++ b/library.c
@@ -0,0 +1,207 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2001,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "library.h"
+#include "proc.h" // for enum callback_status
+#include "debug.h"
+
+/* If the other symbol owns the name, we need to make the copy, so
+ * that the life-times of the two symbols are not dependent on each
+ * other. */
+static int
+strdup_if_owned(const char **retp, const char *str, int owned)
+{
+ if (!owned || str == NULL) {
+ *retp = str;
+ return 0;
+ } else {
+ *retp = strdup(str);
+ return *retp != NULL ? 0 : -1;
+ }
+}
+
+void
+library_symbol_init(struct library_symbol *libsym,
+ GElf_Addr addr, const char *name, int own_name,
+ enum toplt type_of_plt, int is_weak)
+{
+ libsym->needs_init = 0;
+ libsym->is_weak = is_weak;
+ libsym->plt_type = type_of_plt;
+ libsym->name = name;
+ libsym->own_name = own_name;
+ libsym->enter_addr = (void *)(uintptr_t)addr;
+ libsym->next = NULL;
+}
+
+void
+library_symbol_destroy(struct library_symbol *libsym)
+{
+ if (libsym != NULL && libsym->own_name)
+ free((char *)libsym->name);
+}
+
+int
+library_symbol_clone(struct library_symbol *retp, struct library_symbol *libsym)
+{
+ const char *name;
+ if (strdup_if_owned(&name, libsym->name, libsym->own_name) < 0)
+ return -1;
+
+ library_symbol_init(retp, (GElf_Addr)libsym->enter_addr,
+ name, libsym->own_name, libsym->plt_type,
+ libsym->is_weak);
+ retp->needs_init = libsym->needs_init;
+ return 0;
+}
+
+int
+library_symbol_cmp(struct library_symbol *a, struct library_symbol *b)
+{
+ if (a->enter_addr < b->enter_addr)
+ return -1;
+ if (a->enter_addr > b->enter_addr)
+ return 1;
+ if (a->name != NULL && b->name != NULL)
+ return strcmp(a->name, b->name);
+ if (a->name == NULL) {
+ if (b->name == NULL)
+ return 0;
+ return -1;
+ }
+ return 1;
+}
+
+enum callback_status
+library_symbol_equal_cb(struct library_symbol *libsym, void *u)
+{
+ struct library_symbol *standard = u;
+ return library_symbol_cmp(libsym, standard) == 0 ? CBS_STOP : CBS_CONT;
+}
+
+void
+library_init(struct library *lib, const char *name, int own_name)
+{
+ lib->next = NULL;
+ lib->name = name;
+ lib->own_name = own_name;
+ lib->symbols = NULL;
+}
+
+int
+library_clone(struct library *retp, struct library *lib)
+{
+ const char *name;
+ if (strdup_if_owned(&name, lib->name, lib->own_name) < 0)
+ return -1;
+
+ library_init(retp, lib->name, lib->own_name);
+
+ struct library_symbol *it;
+ struct library_symbol **nsymp = &retp->symbols;
+ for (it = lib->symbols; it != NULL; it = it->next) {
+ *nsymp = malloc(sizeof(**nsymp));
+ if (*nsymp == NULL
+ || library_symbol_clone(*nsymp, it) < 0) {
+ /* Release what we managed to allocate. */
+ library_destroy(retp);
+ return -1;
+ }
+
+ nsymp = &(*nsymp)->next;
+ }
+ return 0;
+}
+
+void
+library_destroy(struct library *lib)
+{
+ if (lib == NULL)
+ return;
+ library_set_name(lib, NULL, 0);
+
+ struct library_symbol *sym;
+ for (sym = lib->symbols; sym != NULL; ) {
+ struct library_symbol *next = sym->next;
+ library_symbol_destroy(sym);
+ free(sym);
+ sym = next;
+ }
+}
+
+void
+library_set_name(struct library *lib, const char *new_name, int own_name)
+{
+ if (lib->own_name)
+ free((char *)lib->name);
+ lib->name = new_name;
+ lib->own_name = own_name;
+}
+
+struct library_symbol *
+library_each_symbol(struct library *lib, struct library_symbol *it,
+ enum callback_status (*cb)(struct library_symbol *, void *),
+ void *data)
+{
+ if (it == NULL)
+ it = lib->symbols;
+
+ while (it != NULL) {
+ struct library_symbol *next = it->next;
+
+ switch ((*cb)(it, data)) {
+ case CBS_STOP:
+ return it;
+ case CBS_CONT:
+ break;
+ }
+
+ it = next;
+ }
+
+ return NULL;
+}
+
+void
+library_add_symbol(struct library *lib, struct library_symbol *sym)
+{
+ sym->next = lib->symbols;
+ lib->symbols = sym;
+}
+
+enum callback_status
+library_named_cb(struct Process *proc, struct library *lib, void *name)
+{
+ if (name == lib->name
+ || strcmp(lib->name, (char *)name) == 0)
+ return CBS_STOP;
+ else
+ return CBS_CONT;
+}
+
+enum callback_status
+library_with_base_cb(struct Process *proc, struct library *lib, void *basep)
+{
+ return lib->base == *(target_address_t *)basep ? CBS_STOP : CBS_CONT;
+}
diff --git a/library.h b/library.h
new file mode 100644
index 0000000..88da6c5
--- /dev/null
+++ b/library.h
@@ -0,0 +1,149 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2006 Paul Gilliam
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef _LIBRARY_H_
+#define _LIBRARY_H_
+
+#include <stdint.h>
+#include <gelf.h> // XXX
+
+struct Process;
+struct library;
+
+enum toplt {
+ LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */
+ LS_TOPLT_EXEC, /* PLT for this symbol is executable. */
+ LS_TOPLT_POINT /* PLT for this symbol is a non-executable. */
+};
+
+/* We should in general be able to trace 64-bit processes with 32-bit
+ * ltrace. (At least PPC has several PTRACE requests related to
+ * tracing 64-on-32, so presumably it should be possible.) But ltrace
+ * is currently hopelessly infested with using void* for host address.
+ * So keep with it, for now. */
+typedef void *target_address_t;
+
+struct library_symbol {
+ struct library_symbol *next;
+ const char *name;
+ target_address_t enter_addr;
+ enum toplt plt_type;
+ char needs_init;
+ char is_weak;
+ char own_name;
+};
+
+/* Init LIBSYM. NAME will be freed when LIBSYM is destroyed if
+ * OWN_NAME. */
+/* XXX note that we shouldn't use GElf_Addr for ADDR either. The fact
+ * that Elf is used is a back-end detail. At least ltrace pretends
+ * that it would like to be cross-platform like that one day. */
+void library_symbol_init(struct library_symbol *libsym,
+ GElf_Addr addr, const char *name, int own_name,
+ enum toplt type_of_plt, int is_weak);
+
+/* Copy library symbol SYM into the area pointed-to by RETP. Return 0
+ * on success or a negative value on failure. */
+int library_symbol_clone(struct library_symbol *retp,
+ struct library_symbol *sym);
+
+/* Destroy library symbol. This essentially just frees name if it's
+ * owned. It doesn't free the memory associated with SYM pointer
+ * itself. Returns 0 on success or a negative value in case of an
+ * error (which would be an out of memory condition). */
+void library_symbol_destroy(struct library_symbol *sym);
+
+/* Compare two library symbols. Returns a negative value, 0, or a
+ * positive value, much like strcmp. The function compares symbol
+ * addresses, and if those are equal, it compares symbol names. If
+ * those are equal, too, the symbols are considered equal. */
+int library_symbol_cmp(struct library_symbol *a, struct library_symbol *b);
+
+/* A function that can be used as library_each_symbol callback. Looks
+ * for a symbol SYM for which library_symbol_cmp(SYM, STANDARD)
+ * returns 0. */
+enum callback_status library_symbol_equal_cb(struct library_symbol *libsym,
+ void *standard);
+
+/* XXX we might consider sharing libraries across processes. Things
+ * like libc will be opened by every single process, no point cloning
+ * these everywhere. But for now, keep the ownership structure
+ * simple. */
+struct library {
+ struct library *next;
+
+ /* Address where the library is mapped. Two library objects
+ * are considered equal, if they have the same base. */
+ target_address_t base;
+
+ /* Absolute address of the entry point. Useful for main
+ * binary, though I the value might be useful for the dynamic
+ * linker, too (in case we ever want to do early process
+ * tracing). */
+ target_address_t entry;
+
+ /* Symbols associated with the library. */
+ struct library_symbol *symbols;
+ const char *name;
+ char own_name;
+};
+
+/* Init LIB. NAME will be freed when LIB is destroyed if
+ * OWN_NAME. */
+void library_init(struct library *lib, const char *name, int own_name);
+
+/* Initialize RETP to a library identical to LIB. Symbols are not
+ * shared, but copied over. Returns 0 on success and a negative value
+ * in case of failure. */
+int library_clone(struct library *retp, struct library *lib);
+
+/* Destroy library. Doesn't free LIB itself. */
+void library_destroy(struct library *lib);
+
+/* Set library name. Frees the old name if necessary. */
+void library_set_name(struct library *lib, const char *new_name, int own_name);
+
+/* Iterate through list of symbols of library LIB. Restarts are
+ * supported via START (see each_process for details of iteration
+ * interface). */
+struct library_symbol *library_each_symbol
+ (struct library *lib, struct library_symbol *start,
+ enum callback_status (*cb)(struct library_symbol *, void *),
+ void *data);
+
+/* Add a new symbol SYM to LIB. SYM is assumed owned, we need to
+ * overwrite SYM->next. */
+void library_add_symbol(struct library *lib, struct library_symbol *sym);
+
+/* A function that can be used as proc_each_library callback. Looks
+ * for a library with the name passed in DATA. PROC is ignored. */
+enum callback_status library_named_cb(struct Process *proc,
+ struct library *lib, void *name);
+
+/* A function that can be used as proc_each_library callback. Looks
+ * for a library with given base.
+ *
+ * NOTE: The base is passed as a POINTER to target_address_t (that
+ * because in general, target_address_t doesn't fit in void*). */
+enum callback_status library_with_base_cb(struct Process *proc,
+ struct library *lib, void *basep);
+
+#endif /* _LIBRARY_H_ */
diff --git a/ltrace-elf.c b/ltrace-elf.c
index d3d67d5..7d2ba5b 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -14,16 +14,7 @@
#include "common.h"
#include "proc.h"
-
-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);
-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 = NULL;
-struct ltelf main_lte;
+#include "library.h"
#ifdef PLT_REINITALISATION_BP
extern char *PLTs_initialized_by_here;
@@ -54,7 +45,7 @@ static int maybe_pick_section(GElf_Addr addr,
Elf_Scn *in_sec, GElf_Shdr *in_shdr,
Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
{
- if (inside (addr, in_shdr)) {
+ if (inside(addr, in_shdr)) {
*tgt_sec = in_sec;
*tgt_shdr = *in_shdr;
return 1;
@@ -180,10 +171,13 @@ open_elf(struct ltelf *lte, const char *filename)
return 0;
}
+/* XXX temporarily non-static */
int
-do_init_elf(struct ltelf *lte, const char *filename) {
+do_init_elf(struct ltelf *lte, const char *filename)
+{
int i;
GElf_Addr relplt_addr = 0;
+ GElf_Addr soname_offset = 0;
size_t relplt_size = 0;
debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
@@ -195,6 +189,21 @@ do_init_elf(struct ltelf *lte, const char *filename) {
Elf_Data *plt_data = NULL;
GElf_Addr ppcgot = 0;
+ /* For non-DSO objects, find out the base address. */
+ if (lte->ehdr.e_type == ET_EXEC) {
+ GElf_Phdr phdr;
+ for (i = 0; gelf_getphdr (lte->elf, i, &phdr) != NULL; ++i) {
+ if (phdr.p_type == PT_LOAD) {
+ fprintf(stderr, " + loadable segment at %#lx\n",
+ phdr.p_vaddr);
+ lte->base_addr = phdr.p_vaddr;
+ break;
+ }
+ }
+ }
+
+ lte->entry_addr = lte->ehdr.e_entry;
+
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
Elf_Scn *scn;
GElf_Shdr shdr;
@@ -268,6 +277,9 @@ do_init_elf(struct ltelf *lte, const char *filename) {
size_t j;
lte->dyn_addr = shdr.sh_addr;
+ fprintf(stderr, "dyn_addr = %#lx\n", lte->dyn_addr);
+ extern void *dyn_addr;
+ dyn_addr = (void *)lte->dyn_addr;
lte->dyn_sz = shdr.sh_size;
data = elf_getdata(scn, NULL);
@@ -317,75 +329,10 @@ do_init_elf(struct ltelf *lte, const char *filename) {
else if (dyn.d_tag == DT_PPC_GOT) {
ppcgot = dyn.d_un.d_val;
debug(1, "ppcgot %#" PRIx64, ppcgot);
+ } else if (dyn.d_tag == DT_SONAME) {
+ soname_offset = dyn.d_un.d_val;
}
}
- } else if (shdr.sh_type == SHT_HASH) {
- Elf_Data *data;
- size_t j;
-
- lte->hash_type = SHT_HASH;
-
- data = elf_getdata(scn, NULL);
- if (data == NULL || elf_getdata(scn, data) != NULL
- || data->d_off || data->d_size != shdr.sh_size)
- error(EXIT_FAILURE, 0,
- "Couldn't get .hash data from \"%s\"",
- filename);
-
- if (shdr.sh_entsize == 4) {
- /* Standard conforming ELF. */
- if (data->d_type != ELF_T_WORD)
- error(EXIT_FAILURE, 0,
- "Couldn't get .hash data from \"%s\"",
- filename);
- lte->hash = (Elf32_Word *) data->d_buf;
- } else if (shdr.sh_entsize == 8) {
- /* Alpha or s390x. */
- Elf32_Word *dst, *src;
- size_t hash_count = data->d_size / 8;
-
- lte->hash = (Elf32_Word *)
- malloc(hash_count * sizeof(Elf32_Word));
- if (lte->hash == NULL)
- error(EXIT_FAILURE, 0,
- "Couldn't convert .hash section from \"%s\"",
- filename);
- lte->lte_flags |= LTE_HASH_MALLOCED;
- dst = lte->hash;
- src = (Elf32_Word *) data->d_buf;
- if ((data->d_type == ELF_T_WORD
- && __BYTE_ORDER == __BIG_ENDIAN)
- || (data->d_type == ELF_T_XWORD
- && lte->ehdr.e_ident[EI_DATA] ==
- ELFDATA2MSB))
- ++src;
- for (j = 0; j < hash_count; ++j, src += 2)
- *dst++ = *src;
- } else
- error(EXIT_FAILURE, 0,
- "Unknown .hash sh_entsize in \"%s\"",
- filename);
- } else if (shdr.sh_type == SHT_GNU_HASH
- && lte->hash == NULL) {
- Elf_Data *data;
-
- lte->hash_type = SHT_GNU_HASH;
-
- if (shdr.sh_entsize != 0
- && shdr.sh_entsize != 4) {
- error(EXIT_FAILURE, 0,
- ".gnu.hash sh_entsize in \"%s\" "
- "should be 4, but is %#" PRIx64,
- filename, shdr.sh_entsize);
- }
-
- data = loaddata(scn, &shdr);
- if (data == NULL)
- error(EXIT_FAILURE, 0,
- "Couldn't get .gnu.hash data from \"%s\"",
- filename);
-
- lte->hash = (Elf32_Word *) data->d_buf;
} else if (shdr.sh_type == SHT_PROGBITS
|| shdr.sh_type == SHT_NOBITS) {
if (strcmp(name, ".plt") == 0) {
@@ -465,193 +412,24 @@ do_init_elf(struct ltelf *lte, const char *filename) {
debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
}
+
+ if (soname_offset != 0)
+ lte->soname = lte->dynstr + soname_offset;
+
return 0;
}
+/* XXX temporarily non-static */
void
do_close_elf(struct ltelf *lte) {
debug(DEBUG_FUNCTION, "do_close_elf()");
- if (lte->lte_flags & LTE_HASH_MALLOCED)
- free((char *)lte->hash);
elf_end(lte->elf);
close(lte->fd);
}
-void
-library_symbol_init(struct library_symbol *libsym,
- GElf_Addr addr, const char *name,
- enum toplt type_of_plt, int is_weak)
-{
- libsym->needs_init = 0;
- libsym->is_weak = is_weak;
- libsym->plt_type = type_of_plt;
- libsym->name = name;
- libsym->enter_addr = (void *)(uintptr_t)addr;
- libsym->next = NULL;
-}
-
-static struct library_symbol *
-create_library_symbol(const char *name, GElf_Addr addr,
- enum toplt type_of_plt, int is_weak)
-{
- size_t namel = strlen(name) + 1;
- struct library_symbol * libsym = calloc(sizeof(*libsym) + namel, 1);
- if (libsym == NULL) {
- perror("create_library_symbol");
- return NULL;
- }
- memcpy(libsym + 1, name, namel);
- library_symbol_init(libsym, addr,
- (char *)(libsym + 1), type_of_plt, is_weak);
-
- return libsym;
-}
-
-void
-add_library_symbol(GElf_Addr addr, const char *name,
- struct library_symbol **library_symbolspp,
- enum toplt type_of_plt, int is_weak)
-{
- debug(DEBUG_FUNCTION, "add_library_symbol()");
-
- struct library_symbol *s = create_library_symbol(name, addr,
- type_of_plt, is_weak);
- if (s == NULL)
- error(EXIT_FAILURE, errno, "add_library_symbol failed");
-
- s->needs_init = 1;
- s->next = *library_symbolspp;
- *library_symbolspp = s;
-
- debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name);
-}
-
-struct library_symbol *
-clone_library_symbol(struct library_symbol *libsym)
-{
- GElf_Addr addr = (uintptr_t)libsym->enter_addr;
- struct library_symbol *copy
- = create_library_symbol(libsym->name, addr,
- libsym->plt_type, libsym->is_weak);
- if (copy != NULL)
- copy->needs_init = libsym->needs_init;
-
- return copy;
-}
-
-void
-destroy_library_symbol(struct library_symbol * sym)
-{
- free(sym);
-}
-
-void
-destroy_library_symbol_chain(struct library_symbol * sym)
-{
- while (sym != NULL) {
- struct library_symbol * next = sym->next;
- destroy_library_symbol(sym);
- sym = next;
- }
-}
-
-/* stolen from elfutils-0.123 */
-static unsigned long
-private_elf_gnu_hash(const char *name) {
- unsigned long h = 5381;
- const unsigned char *string = (const unsigned char *)name;
- unsigned char c;
- for (c = *string; c; c = *++string)
- h = h * 33 + c;
- return h & 0xffffffff;
-}
-
-static int
-symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym,
- size_t symidx, const char *name)
-{
- GElf_Sym tmp_sym;
- GElf_Sym *tmp;
-
- tmp = (sym) ? (sym) : (&tmp_sym);
-
- if (gelf_getsym(lte[lte_i].dynsym, symidx, tmp) == NULL)
- error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym");
- else {
- tmp->st_value += lte[lte_i].base_addr;
- debug(2, "symbol found: %s, %zd, %#" PRIx64,
- name, lte_i, tmp->st_value);
- }
- return tmp->st_value != 0
- && tmp->st_shndx != SHN_UNDEF
- && strcmp(name, lte[lte_i].dynstr + tmp->st_name) == 0;
-}
-
-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 (!count)
- return 1;
-
-#ifdef ELF_HASH_TAKES_CHARP
- hash = elf_hash(name);
-#else
- hash = elf_hash((const unsigned char *)name);
-#endif
- gnu_hash = private_elf_gnu_hash(name);
-
- for (i = 0; i < count; ++i) {
- if (lte[i].hash == NULL)
- continue;
-
- if (lte[i].hash_type == SHT_GNU_HASH) {
- Elf32_Word * hashbase = lte[i].hash;
- Elf32_Word nbuckets = *hashbase++;
- Elf32_Word symbias = *hashbase++;
- Elf32_Word bitmask_nwords = *hashbase++;
- Elf32_Word * buckets;
- Elf32_Word * chain_zero;
- Elf32_Word bucket;
-
- // +1 for skipped `shift'
- hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1;
- buckets = hashbase;
- hashbase += nbuckets;
- chain_zero = hashbase - symbias;
- bucket = buckets[gnu_hash % nbuckets];
-
- if (bucket != 0) {
- const Elf32_Word *hasharr = &chain_zero[bucket];
- do
- if ((*hasharr & ~1u) == (gnu_hash & ~1u)) {
- int symidx = hasharr - chain_zero;
- if (symbol_matches(lte, i,
- sym, symidx,
- name))
- return 1;
- }
- while ((*hasharr++ & 1u) == 0);
- }
- } else {
- Elf32_Word nbuckets, symndx;
- Elf32_Word *buckets, *chain;
- nbuckets = lte[i].hash[0];
- buckets = &lte[i].hash[2];
- chain = &lte[i].hash[2 + nbuckets];
-
- for (symndx = buckets[hash % nbuckets];
- symndx != STN_UNDEF; symndx = chain[symndx])
- if (symbol_matches(lte, i, sym, symndx, name))
- return 1;
- }
- }
- return 0;
-}
-
-static GElf_Addr
+/* XXX non-static for now, as it's not called anywhere. But we want
+ * this code around. */
+GElf_Addr
opd2addr(struct ltelf *lte, GElf_Addr addr) {
#ifdef ARCH_SUPPORTS_OPD
unsigned long base, offset;
@@ -670,198 +448,101 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) {
#endif
}
-struct library_symbol *
-read_elf(Process *proc, GElf_Addr *entryp)
+struct library *
+ltelf_read_library(const char *filename, GElf_Addr base)
{
- struct ltelf lte[MAX_LIBRARIES + 1];
- size_t i;
- struct opt_x_t *xptr;
- struct opt_x_t *opt_x_loc = opt_x;
- struct library_symbol **lib_tail = NULL;
- int exit_out = 0;
- int count = 0;
-
- debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename);
-
- memset(lte, 0, sizeof(lte));
- library_symbols = NULL;
- library_num = 0;
- proc->libdl_hooked = 0;
-
- if (do_init_elf(lte, proc->filename))
+ // XXX we leak LTE contents
+ struct ltelf lte = {
+ .base_addr = base,
+ };
+ if (do_init_elf(&lte, filename) < 0)
return NULL;
- memcpy(&main_lte, lte, sizeof(struct ltelf));
-
- if (opt_p && opt_p->pid > 0) {
- linkmap_init(proc, lte);
- proc->libdl_hooked = 1;
+ struct library *lib = malloc(sizeof(*lib));
+ char *soname = NULL;
+ if (lib == NULL) {
+ fail:
+ free(soname);
+ library_destroy(lib);
+ free(lib);
+ lib = NULL;
+ goto done;
}
- proc->e_machine = lte->ehdr.e_machine;
-
- for (i = 0; i < library_num; ++i) {
- if (do_init_elf(&lte[i + 1], library[i]))
- error(EXIT_FAILURE, errno, "Can't open \"%s\"",
- library[i]);
+ if (lte.soname != NULL) {
+ soname = strdup(lte.soname);
+ if (soname == NULL)
+ goto fail;
}
- if (!options.no_plt) {
-#ifdef __mips__
- // MIPS doesn't use the PLT and the GOT entries get changed
- // on startup.
- 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);
- if (gelf_getsym(lte->dynsym, i, &sym) == NULL){
- error(EXIT_FAILURE, 0,
- "Couldn't get relocation from \"%s\"",
- proc->filename);
- }
- name=lte->dynstr+sym.st_name;
- if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){
- debug(2,"sym %s not a function",name);
- continue;
- }
- add_library_symbol(addr, name, &library_symbols, 0,
- ELF64_ST_BIND(sym.st_info) != 0);
- if (!lib_tail)
- lib_tail = &(library_symbols->next);
- }
-#else
- for (i = 0; i < lte->relplt_count; ++i) {
- GElf_Rel rel;
- GElf_Rela rela;
- GElf_Sym sym;
- GElf_Addr addr;
- void *ret;
- const char *name;
-
- if (lte->relplt->d_type == ELF_T_REL) {
- ret = gelf_getrel(lte->relplt, i, &rel);
- rela.r_offset = rel.r_offset;
- rela.r_info = rel.r_info;
- rela.r_addend = 0;
- } else
- ret = gelf_getrela(lte->relplt, i, &rela);
-
- if (ret == NULL
- || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
- || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
- &sym) == NULL)
- error(EXIT_FAILURE, 0,
- "Couldn't get relocation from \"%s\"",
- proc->filename);
-
- name = lte->dynstr + sym.st_name;
- count = library_num ? library_num+1 : 0;
+ library_init(lib, soname, soname != NULL);
+ lib->entry = (target_address_t)lte.entry_addr;
+ lib->base = (target_address_t)lte.base_addr;
- if (in_load_libraries(name, lte, count, NULL)) {
- enum toplt pltt;
- if (sym.st_value == 0 && lte->plt_stub_vma != 0) {
- pltt = LS_TOPLT_EXEC;
- addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i;
- }
- else {
- pltt = PLTS_ARE_EXECUTABLE(lte)
- ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
- addr = arch_plt_sym_val(lte, i, &rela);
- }
-
- add_library_symbol(addr, name, &library_symbols, pltt,
- ELF64_ST_BIND(sym.st_info) == STB_WEAK);
- if (!lib_tail)
- lib_tail = &(library_symbols->next);
- }
- }
-#endif // !__mips__
- } else {
- lib_tail = &library_symbols;
- }
-
- for (i = 0; i < lte->symtab_count; ++i) {
+ size_t i;
+ for (i = 0; i < lte.relplt_count; ++i) {
+ GElf_Rel rel;
+ GElf_Rela rela;
GElf_Sym sym;
GElf_Addr addr;
- const char *name;
+ void *ret;
- if (gelf_getsym(lte->symtab, i, &sym) == NULL)
- error(EXIT_FAILURE, 0,
- "Couldn't get symbol from \"%s\"",
- proc->filename);
-
- name = lte->strtab + sym.st_name;
- addr = sym.st_value;
- if (!addr)
- continue;
-
- for (xptr = opt_x_loc; xptr; xptr = xptr->next)
- if (xptr->name && strcmp(xptr->name, name) == 0) {
- /* FIXME: Should be able to use &library_symbols as above. But
- when you do, none of the real library symbols cause breaks. */
- add_library_symbol(opd2addr(lte, addr),
- name, lib_tail, LS_TOPLT_NONE, 0);
- xptr->found = 1;
- break;
- }
- }
-
- unsigned found_count = 0;
+ if (lte.relplt->d_type == ELF_T_REL) {
+ ret = gelf_getrel(lte.relplt, i, &rel);
+ rela.r_offset = rel.r_offset;
+ rela.r_info = rel.r_info;
+ rela.r_addend = 0;
+ } else {
+ ret = gelf_getrela(lte.relplt, i, &rela);
+ }
- for (xptr = opt_x_loc; xptr; xptr = xptr->next) {
- if (xptr->found)
- continue;
+ if (ret == NULL
+ || ELF64_R_SYM(rela.r_info) >= lte.dynsym_count
+ || gelf_getsym(lte.dynsym, ELF64_R_SYM(rela.r_info),
+ &sym) == NULL)
+ error(EXIT_FAILURE, 0,
+ "Couldn't get relocation from \"%s\"",
+ filename);
- GElf_Sym sym;
- GElf_Addr addr;
- if (in_load_libraries(xptr->name, lte, library_num+1, &sym)) {
- debug(2, "found symbol %s @ %#" PRIx64 ", adding it.",
- xptr->name, sym.st_value);
- addr = sym.st_value;
- if (ELF32_ST_TYPE (sym.st_info) == STT_FUNC) {
- 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;
+ /* We will destroy the ELF object at the end of the
+ * scope. We need to copy the name for our purposes.
+ * XXX consider just keeping the ELF around. */
+ char *name = strdup(lte.dynstr + sym.st_name);
+ if (name == NULL) {
+ fail2:
+ free(name);
+ goto fail;
}
- }
-
- if (lte->ehdr.e_entry != 0) {
- *entryp = opd2addr(lte, lte->ehdr.e_entry);
- } else {
- }
- for (xptr = opt_x_loc; xptr; xptr = xptr->next)
- if ( ! xptr->found) {
- char *badthing = "WARNING";
-#ifdef PLT_REINITALISATION_BP
- if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) {
- if (lte->ehdr.e_entry) {
- fprintf (stderr, "WARNING: Using e_ent"
- "ry from elf header (%p) for "
- "address of \"%s\"\n", (void*)
- (long) lte->ehdr.e_entry,
- PLTs_initialized_by_here);
- continue;
- }
- badthing = "ERROR";
- exit_out = 1;
- }
-#endif
- fprintf (stderr,
- "%s: Couldn't find symbol \"%s\" in file \"%s\" assuming it will be loaded by libdl!"
- "\n", badthing, xptr->name, proc->filename);
+ enum toplt pltt;
+ if (sym.st_value == 0 && lte.plt_stub_vma != 0) {
+ pltt = LS_TOPLT_EXEC;
+ addr = lte.plt_stub_vma + PPC_PLT_STUB_SIZE * i;
+ } else {
+ pltt = PLTS_ARE_EXECUTABLE(&lte)
+ ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
+ addr = arch_plt_sym_val(&lte, i, &rela);
}
- if (exit_out) {
- exit (1);
+
+ struct library_symbol *libsym = malloc(sizeof(*libsym));
+ if (libsym == NULL)
+ goto fail2;
+ library_symbol_init(libsym, addr, name, 1, pltt,
+ ELF64_ST_BIND(sym.st_info) == STB_WEAK);
+ library_add_symbol(lib, libsym);
}
- for (i = 0; i < library_num + 1; ++i)
- do_close_elf(&lte[i]);
+done:
+ do_close_elf(&lte);
+ return lib;
+}
- return library_symbols;
+struct library *
+ltelf_read_main_binary(struct Process *proc, const char *path)
+{
+ fprintf(stderr, "ltelf_read_main_binary %d %s\n", proc->pid, path);
+ char *fname = pid2name(proc->pid);
+ struct library *lib = ltelf_read_library(fname, 0);
+ library_set_name(lib, path, 0);
+ return lib;
}
diff --git a/ltrace-elf.h b/ltrace-elf.h
index 507a466..7616814 100644
--- a/ltrace-elf.h
+++ b/ltrace-elf.h
@@ -3,8 +3,16 @@
#include <gelf.h>
#include <stdlib.h>
-#include "proc.h"
+struct Process;
+struct library;
+
+/* XXX Ok, the original idea was to separate the low-level ELF data
+ * from the abstract "struct library" object, but we use some of the
+ * following extensively in the back end. Not all though. So what we
+ * use should be move to struct library, and the rest of this
+ * structure maybe could be safely hidden in .c. How to integrate the
+ * arch-specific bits into struct library is unclear as of now. */
struct ltelf {
int fd;
Elf *elf;
@@ -18,16 +26,16 @@ struct ltelf {
size_t relplt_count;
Elf_Data *symtab;
const char *strtab;
+ const char *soname;
size_t symtab_count;
Elf_Data *opd;
GElf_Addr *opd_addr;
size_t opd_size;
- Elf32_Word *hash;
- int hash_type;
int lte_flags;
GElf_Addr dyn_addr;
size_t dyn_sz;
GElf_Addr base_addr;
+ GElf_Addr entry_addr;
#ifdef __mips__
size_t pltgot_addr;
size_t mips_local_gotno;
@@ -37,22 +45,22 @@ struct ltelf {
};
#define ELF_MAX_SEGMENTS 50
-#define LTE_HASH_MALLOCED 1
#define LTE_PLT_EXECUTABLE 2
-#define PLTS_ARE_EXECUTABLE(lte) ((lte->lte_flags & LTE_PLT_EXECUTABLE) != 0)
+#define PLTS_ARE_EXECUTABLE(lte) (((lte)->lte_flags & LTE_PLT_EXECUTABLE) != 0)
-extern size_t library_num;
-extern char *library[MAX_LIBRARIES];
+int open_elf(struct ltelf *lte, const char *filename);
-extern int open_elf(struct ltelf *lte, const char *filename);
-extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp);
+/* XXX is it possible to put breakpoints in VDSO and VSYSCALL
+ * pseudo-libraries? For now we assume that all libraries can be
+ * opened via a filesystem. BASE is ignored for ET_EXEC files. */
+struct library *ltelf_read_library(const char *filename, GElf_Addr base);
-extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *);
+/* Create a library object representing the main binary. The entry
+ * point address is stored to *ENTRYP. */
+struct library *ltelf_read_main_binary(struct Process *proc, const char *path);
-#ifndef SHT_GNU_HASH
-#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
-#endif
+GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *);
#if __WORDSIZE == 32
#define PRI_ELF_ADDR PRIx32
diff --git a/options.c b/options.c
index 74c28bd..eae797e 100644
--- a/options.c
+++ b/options.c
@@ -9,6 +9,7 @@
#include <sys/ioctl.h>
#include <getopt.h>
+#include <assert.h>
#include "common.h"
@@ -36,8 +37,6 @@ struct options_t options = {
.follow = 0, /* trace child processes */
};
-char *library[MAX_LIBRARIES];
-size_t library_num = 0;
static char *progname; /* Program name (`ltrace') */
int opt_i = 0; /* instruction pointer */
int opt_r = 0; /* print relative timestamp */
@@ -316,13 +315,8 @@ process_options(int argc, char **argv) {
opt_i++;
break;
case 'l':
- if (library_num == MAX_LIBRARIES) {
- fprintf(stderr,
- "Too many libraries. Maximum is %i.\n",
- MAX_LIBRARIES);
- exit(1);
- }
- library[library_num++] = optarg;
+ assert(!"-l support not yet implemented");
+ abort();
break;
case 'L':
options.libcalls = 0;
diff --git a/output.h b/output.h
index 10a144f..8e4fa46 100644
--- a/output.h
+++ b/output.h
@@ -1,3 +1,6 @@
-void output_line(Process *proc, char *fmt, ...);
-void output_left(enum tof type, Process *proc, const char *function_name);
-void output_right(enum tof type, Process *proc, const char *function_name);
+struct Process;
+void output_line(struct Process *proc, char *fmt, ...);
+void output_left(enum tof type, struct Process *proc,
+ const char *function_name);
+void output_right(enum tof type, struct Process *proc,
+ const char *function_name);
diff --git a/proc.c b/proc.c
index 5360b0b..1984d12 100644
--- a/proc.c
+++ b/proc.c
@@ -17,40 +17,189 @@
#include "breakpoint.h"
#include "proc.h"
-Process *
-open_program(char *filename, pid_t pid, int enable) {
- Process *proc;
- assert(pid != 0);
- proc = calloc(sizeof(Process), 1);
- if (!proc) {
- perror("malloc");
- exit(1);
- }
+static int
+process_bare_init(struct Process *proc, const char *filename, pid_t pid)
+{
+ fprintf(stderr, "process_bare_init %s %d\n", filename, pid);
+ memset(proc, 0, sizeof(*proc));
proc->filename = strdup(filename);
+ if (proc->filename == NULL) {
+ fail:
+ free(proc->filename);
+ if (proc->breakpoints != NULL)
+ dict_clear(proc->breakpoints);
+ return -1;
+ }
+
+ /* Add process so that we know who the leader is. */
proc->pid = pid;
+ add_process(proc);
+ if (proc->leader == NULL)
+ goto fail;
+
+ if (proc->leader == proc) {
+ proc->breakpoints = dict_init(dict_key2hash_int,
+ dict_key_cmp_int);
+ if (proc->breakpoints == NULL)
+ goto fail;
+ } else {
+ proc->breakpoints = NULL;
+ }
+
#if defined(HAVE_LIBUNWIND)
proc->unwind_priv = _UPT_create(pid);
proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0);
#endif /* defined(HAVE_LIBUNWIND) */
- add_process(proc);
- if (proc->leader == NULL) {
+ return 0;
+}
+
+static void
+process_bare_destroy(struct Process *proc)
+{
+ free(proc->filename);
+ dict_clear(proc->breakpoints);
+ remove_process(proc);
+}
+
+int
+process_init(struct Process *proc, const char *filename, pid_t pid, int enable)
+{
+ fprintf(stderr, "process_init %s %d enable=%d\n", filename, pid, enable);
+ if (process_bare_init(proc, filename, pid) < 0) {
+ error(0, errno, "init process %d", pid);
+ return -1;
+ }
+
+ if (proc->leader == proc && breakpoints_init(proc, enable) < 0) {
+ fprintf(stderr, "failed to init breakpoints %d\n",
+ proc->pid);
+ process_bare_destroy(proc);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct Process *
+open_program(const char *filename, pid_t pid, int enable)
+{
+ fprintf(stderr, "open_program %s %d enable=%d\n",
+ filename, pid, enable);
+ assert(pid != 0);
+ struct Process *proc = malloc(sizeof(*proc));
+ if (proc == NULL
+ || process_init(proc, filename, pid, enable) < 0) {
free(proc);
return NULL;
}
+ return proc;
+}
- if (proc->leader == proc) {
- trace_set_options(proc, proc->pid);
- if (breakpoints_init(proc, enable)) {
- fprintf(stderr, "failed to init breakpoints %d\n",
- proc->pid);
- remove_process(proc);
- return NULL;
+struct clone_single_bp_data {
+ struct Process *old_proc;
+ struct Process *new_proc;
+ int error;
+};
+
+struct find_symbol_data {
+ struct library_symbol *old_libsym;
+ struct library_symbol *found_libsym;
+};
+
+static enum callback_status
+find_sym_in_lib(struct Process *proc, struct library *lib, void *u)
+{
+ struct find_symbol_data *fs = u;
+ fs->found_libsym
+ = library_each_symbol(lib, NULL, library_symbol_equal_cb,
+ fs->old_libsym);
+ return fs->found_libsym != NULL ? CBS_STOP : CBS_CONT;
+}
+
+static void
+clone_single_bp(void *key, void *value, void *u)
+{
+ target_address_t addr = (target_address_t)key;
+ struct breakpoint *bp = value;
+ struct clone_single_bp_data *data = u;
+
+ /* Find library and symbol that this symbol was linked to. */
+ struct library_symbol *libsym = bp->libsym;
+ struct library *lib = NULL;
+ if (libsym != NULL) {
+ struct find_symbol_data f_data = {
+ .old_libsym = libsym,
+ };
+ lib = proc_each_library(data->old_proc, NULL,
+ find_sym_in_lib, &f_data);
+ assert(lib != NULL);
+ libsym = f_data.found_libsym;
+ }
+
+ /* LIB and LIBSYM now hold the new library and symbol that
+ * correspond to the original breakpoint. Now we can do the
+ * clone itself. */
+ struct breakpoint *clone = malloc(sizeof(*clone));
+ if (clone == NULL
+ || breakpoint_init(clone, data->new_proc, addr,
+ libsym, bp->cbs) < 0) {
+ data->error = -1;
+ return;
+ }
+}
+
+int
+process_clone(struct Process *retp, struct Process *proc, pid_t pid)
+{
+ if (process_bare_init(retp, proc->filename, pid) < 0) {
+ fail:
+ error(0, errno, "clone process %d->%d", proc->pid, pid);
+ return -1;
+ }
+
+ /* For non-leader processes, that's all we need to do. */
+ if (proc->leader != proc)
+ return 0;
+
+ /* Clone symbols first so that we can clone and relink
+ * breakpoints. */
+ struct library *lib;
+ struct library **nlibp = &retp->libraries;
+ for (lib = proc->libraries; lib != NULL; lib = lib->next) {
+ *nlibp = malloc(sizeof(**nlibp));
+ if (*nlibp == NULL
+ || library_clone(*nlibp, lib) < 0) {
+ fail2:
+ process_bare_destroy(retp);
+
+ /* Error when cloning. Unroll what was done. */
+ for (lib = retp->libraries; lib != NULL; ) {
+ struct library *next = lib->next;
+ library_destroy(lib);
+ free(lib);
+ lib = next;
+ }
+ goto fail;
}
+
+ nlibp = &(*nlibp)->next;
}
- return proc;
+ /* Now clone breakpoints. Symbol relinking is done in
+ * clone_single_bp. */
+ struct clone_single_bp_data data = {
+ .old_proc = proc,
+ .new_proc = retp,
+ .error = 0,
+ };
+ dict_apply_to_all(proc->breakpoints, &clone_single_bp, &data);
+
+ if (data.error < 0)
+ goto fail2;
+
+ return 0;
}
static int
@@ -76,11 +225,11 @@ open_one_pid(pid_t pid)
return 0;
}
-static enum pcb_status
+static enum callback_status
start_one_pid(Process * proc, void * data)
{
continue_process(proc->pid);
- return pcb_cont;
+ return CBS_CONT;
}
void
@@ -144,11 +293,11 @@ open_pid(pid_t pid)
each_task(pid2proc(pid)->leader, start_one_pid, NULL);
}
-static enum pcb_status
+static enum callback_status
find_proc(Process * proc, void * data)
{
pid_t pid = (pid_t)(uintptr_t)data;
- return proc->pid == pid ? pcb_stop : pcb_cont;
+ return proc->pid == pid ? CBS_STOP : CBS_CONT;
}
Process *
@@ -180,33 +329,43 @@ unlist_process(Process * proc)
}
}
-Process *
-each_process(Process * proc,
- enum pcb_status (* cb)(Process * proc, void * data),
- void * data)
+struct Process *
+each_process(struct Process *it,
+ enum callback_status(*cb)(struct Process *proc, void *data),
+ void *data)
{
- Process * it = proc ?: list_of_processes;
+ if (it == NULL)
+ it = list_of_processes;
for (; it != NULL; ) {
/* Callback might call remove_process. */
Process * next = it->next;
- if ((*cb) (it, data) == pcb_stop)
+ switch ((*cb)(it, data)) {
+ case CBS_STOP:
return it;
+ case CBS_CONT:
+ break;
+ }
it = next;
}
return NULL;
}
Process *
-each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data),
- void * data)
+each_task(struct Process *it,
+ enum callback_status(*cb)(struct Process *proc, void *data),
+ void *data)
{
if (it != NULL) {
Process * leader = it->leader;
for (; it != NULL && it->leader == leader; ) {
/* Callback might call remove_process. */
Process * next = it->next;
- if ((*cb) (it, data) == pcb_stop)
+ switch ((*cb)(it, data)) {
+ case CBS_STOP:
return it;
+ case CBS_CONT:
+ break;
+ }
it = next;
}
}
@@ -216,9 +375,11 @@ each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data),
void
add_process(Process * proc)
{
+ fprintf(stderr, "add_process %d\n", proc->pid);
Process ** leaderp = &list_of_processes;
if (proc->pid) {
pid_t tgid = process_leader(proc->pid);
+ fprintf(stderr, " + leader is %d\n", tgid);
if (tgid == 0)
/* Must have been terminated before we managed
* to fully attach. */
@@ -253,13 +414,13 @@ change_process_leader(Process * proc, Process * leader)
*leaderp = proc;
}
-static enum pcb_status
-clear_leader(Process * proc, void * data)
+static enum callback_status
+clear_leader(struct Process *proc, void *data)
{
debug(DEBUG_FUNCTION, "detach_task %d from leader %d",
proc->pid, proc->leader->pid);
proc->leader = NULL;
- return pcb_cont;
+ return CBS_CONT;
}
static enum ecb_status
@@ -311,3 +472,69 @@ destroy_event_handler(Process * proc)
free(handler);
proc->event_handler = NULL;
}
+
+static enum callback_status
+breakpoint_for_symbol(struct library_symbol *libsym, void *data)
+{
+ struct Process *proc = data;
+ fprintf(stderr, " %s@%p\n", libsym->name, libsym->enter_addr);
+
+ if (insert_breakpoint(proc, libsym->enter_addr, libsym, 1) == NULL)
+ return CBS_STOP;
+
+ return CBS_CONT;
+}
+
+void
+proc_add_library(struct Process *proc, struct library *lib)
+{
+ assert(lib->next == NULL);
+ lib->next = proc->libraries;
+ proc->libraries = lib;
+ fprintf(stderr, "=== Added library %s@%p to %d:\n",
+ lib->name, lib->base, proc->pid);
+
+ struct library_symbol *libsym = NULL;
+ while ((libsym = library_each_symbol(lib, libsym, breakpoint_for_symbol,
+ proc)) != NULL) {
+ error(0, errno, "insert breakpoint for %s", libsym->name);
+ libsym = libsym->next;
+ }
+}
+
+int
+proc_remove_library(struct Process *proc, struct library *lib)
+{
+ struct library **libp;
+ for (libp = &proc->libraries; *libp != NULL; libp = &(*libp)->next)
+ if (*libp == lib) {
+ *libp = lib->next;
+ return 0;
+ }
+ return -1;
+}
+
+struct library *
+proc_each_library(struct Process *proc, struct library *it,
+ enum callback_status (*cb)(struct Process *proc,
+ struct library *lib, void *data),
+ void *data)
+{
+ if (it == NULL)
+ it = proc->libraries;
+
+ while (it != NULL) {
+ struct library *next = it->next;
+
+ switch (cb(proc, it, data)) {
+ case CBS_STOP:
+ return it;
+ case CBS_CONT:
+ break;
+ }
+
+ it = next;
+ }
+
+ return NULL;
+}
diff --git a/proc.h b/proc.h
index 9b80556..0f670e5 100644
--- a/proc.h
+++ b/proc.h
@@ -1,3 +1,25 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1998,2001,2008,2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#ifndef _PROC_H_
#define _PROC_H_
@@ -8,6 +30,16 @@
#include "ltrace.h"
#include "dict.h"
+struct library;
+
+/* XXX Move this somewhere where it makes sense. When the mess in
+ * common.h is disentangled, that would actually be a good place for
+ * this. */
+enum callback_status {
+ CBS_STOP, /* The iteration should stop. */
+ CBS_CONT, /* The iteration should continue. */
+};
+
struct event_handler {
/* Event handler that overrides the default one. Should
* return NULL if the event was handled, otherwise the
@@ -24,11 +56,6 @@ enum process_state {
STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */
};
-enum pcb_status {
- pcb_stop, /* The iteration should stop. */
- pcb_cont, /* The iteration should continue. */
-};
-
struct callstack_element {
union {
int syscall;
@@ -65,9 +92,8 @@ struct Process {
int callstack_depth;
struct callstack_element callstack[MAX_CALLDEPTH];
- struct library_symbol * list_of_symbols;
+ struct library *libraries;
- int libdl_hooked;
/* Arch-dependent: */
void * debug; /* arch-dep process debug struct */
long debug_state; /* arch-dep debug state */
@@ -76,7 +102,6 @@ struct Process {
void * return_addr;
void * arch_ptr;
short e_machine;
- short need_to_reinitialize_breakpoints;
#ifdef __arm__
int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */
#endif
@@ -104,19 +129,65 @@ struct Process {
Process * leader;
};
-Process * open_program(char *filename, pid_t pid, int init_breakpoints);
+int process_init(struct Process *proc,
+ const char *filename, pid_t pid, int enable_breakpoints);
+
+Process * open_program(const char *filename, pid_t pid, int enable_breakpoints);
void open_pid(pid_t pid);
Process * pid2proc(pid_t pid);
+
+/* Clone the contents of PROC into the memory referenced by RETP.
+ * Returns 0 on success or a negative value on failure. */
+int process_clone(struct Process *retp, struct Process *proc, pid_t pid);
+
+/* Iterate through the processes that ltrace currently traces. CB is
+ * called for each process. Tasks are considered to be processes for
+ * the purpose of this iterator.
+ *
+ * Notes on this iteration interface: DATA is passed verbatim to CB.
+ * If CB returns CBS_STOP, the iteration stops and the current
+ * iterator is returned. That iterator can then be used to restart
+ * the iteration. If you don't want CB to see the same process more
+ * than once, restart with IT->next instead of just IT. NULL is
+ * returned when iteration ends.
+ *
+ * There's no provision for returning error states. Errors need to be
+ * signaled to the caller via DATA, together with any other data that
+ * the callback needs. */
Process *each_process(Process *start,
- enum pcb_status (* cb)(Process *proc, void *data),
+ enum callback_status (*cb)(struct Process *proc,
+ void *data),
void *data);
+
+/* Iterate through list of tasks of given process START. Normally you
+ * start the iteration by calling this on PROC->leader, the iterator
+ * doesn't do this for you (so as to support restarts). See above for
+ * details on the iteration interface. */
Process *each_task(Process *start,
- enum pcb_status (* cb)(Process *proc, void *data),
+ enum callback_status (*cb)(struct Process *proc,
+ void *data),
void *data);
+
void add_process(Process *proc);
void change_process_leader(Process *proc, Process *leader);
void remove_process(Process *proc);
void install_event_handler(Process *proc, struct event_handler *handler);
void destroy_event_handler(Process *proc);
+/* Add a library LIB to the list of PROC's libraries. */
+void proc_add_library(struct Process *proc, struct library *lib);
+
+/* Remove LIB from list of PROC's libraries. Returns 0 if the library
+ * was found and unlinked, otherwise returns a negative value. */
+int proc_remove_library(struct Process *proc, struct library *lib);
+
+/* Iterate through the libraries of PROC. See each_process for
+ * detailed description of the iteration interface. */
+struct library *proc_each_library(struct Process *proc, struct library *start,
+ enum callback_status (*cb)(struct Process *p,
+ struct library *l,
+ void *data),
+ void *data);
+
+
#endif /* _PROC_H_ */
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) {