1054 lines
32 KiB
Diff
1054 lines
32 KiB
Diff
diff --git a/breakpoint.h b/breakpoint.h
|
|
new file mode 100644
|
|
index 0000000..ce6f501
|
|
--- /dev/null
|
|
+++ b/breakpoint.h
|
|
@@ -0,0 +1,106 @@
|
|
+/*
|
|
+ * This file is part of ltrace.
|
|
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
|
|
+ * Copyright (C) 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 BREAKPOINT_H
|
|
+#define BREAKPOINT_H
|
|
+
|
|
+/* XXX This is currently a very weak abstraction. We would like to
|
|
+ * much expand this to allow things like breakpoints on SDT probes and
|
|
+ * such.
|
|
+ *
|
|
+ * In particular, we would like to add a tracepoint abstraction.
|
|
+ * Tracepoint is a traceable feature--e.g. an exact address, a DWARF
|
|
+ * symbol, an ELF symbol, a PLT entry, or an SDT probe. Tracepoints
|
|
+ * are named and the user can configure which of them he wants to
|
|
+ * enable. Realized tracepoints enable breakpoints, which are a
|
|
+ * low-level realization of high-level tracepoint.
|
|
+ *
|
|
+ * Tracepoints are provided by the main binary as well as by any
|
|
+ * opened libraries: every time an ELF file is mapped into the address
|
|
+ * space, a new set of tracepoints is extracted, and filtered
|
|
+ * according to user settings. Those tracepoints that are left are
|
|
+ * then realized, and the tracing starts.
|
|
+ *
|
|
+ * A scheme like this would take care of gradually introducing
|
|
+ * breakpoints when the library is mapped, and therefore ready, and
|
|
+ * would avoid certain hacks. For example on PPC64, we don't actually
|
|
+ * add breakpoints to PLT. Instead, we read the PLT (which contains
|
|
+ * addresses, not code), to figure out where to put the breakpoints.
|
|
+ * In prelinked code, that address is non-zero, and points to an
|
|
+ * address that's not yet mapped. ptrace then fails when we try to
|
|
+ * add the breakpoint.
|
|
+ *
|
|
+ * Ideally, return breakpoints would be just a special kind of
|
|
+ * tracepoint that has attached some magic. Or a feature of a
|
|
+ * tracepoint. Service breakpoints like the handling of dlopen would
|
|
+ * be a low-level breakpoint, likely without tracepoint attached.
|
|
+ *
|
|
+ * So that's for sometimes.
|
|
+ */
|
|
+
|
|
+#include "arch.h"
|
|
+
|
|
+struct Process;
|
|
+struct breakpoint;
|
|
+
|
|
+struct bp_callbacks {
|
|
+ void (*on_hit) (struct breakpoint *bp, struct Process *proc);
|
|
+ void (*on_destroy) (struct breakpoint *bp);
|
|
+};
|
|
+
|
|
+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
|
|
+};
|
|
+
|
|
+/* Call on-hit handler of BP, if any is set. */
|
|
+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);
|
|
+
|
|
+/* This is actually three functions rolled in one:
|
|
+ * - breakpoint_init
|
|
+ * - proc_insert_breakpoint
|
|
+ * - breakpoint_enable
|
|
+ * XXX I think it should be broken up somehow. */
|
|
+struct breakpoint *insert_breakpoint(struct Process *proc, void *addr,
|
|
+ struct library_symbol *libsym, int enable);
|
|
+
|
|
+/* */
|
|
+void delete_breakpoint(struct Process *proc, void *addr);
|
|
+
|
|
+/* XXX some of the following belongs to proc.h/proc.c. */
|
|
+struct breakpoint *address2bpstruct(struct Process *proc, void *addr);
|
|
+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 387b2a5..5713fe4 100644
|
|
--- a/breakpoints.c
|
|
+++ b/breakpoints.c
|
|
@@ -8,12 +8,30 @@
|
|
#include <sys/ptrace.h>
|
|
#endif
|
|
|
|
+#include "breakpoint.h"
|
|
#include "common.h"
|
|
|
|
+void
|
|
+breakpoint_on_hit(struct breakpoint *bp, struct Process *proc)
|
|
+{
|
|
+ assert(bp != NULL);
|
|
+ if (bp->cbs != NULL && bp->cbs->on_hit != NULL)
|
|
+ (bp->cbs->on_hit) (bp, proc);
|
|
+}
|
|
+
|
|
+void
|
|
+breakpoint_on_destroy(struct breakpoint *bp)
|
|
+{
|
|
+ assert(bp != NULL);
|
|
+ if (bp->cbs != NULL && bp->cbs->on_destroy != NULL)
|
|
+ (bp->cbs->on_destroy) (bp);
|
|
+}
|
|
+
|
|
/*****************************************************************************/
|
|
|
|
-Breakpoint *
|
|
-address2bpstruct(Process *proc, void *addr) {
|
|
+struct breakpoint *
|
|
+address2bpstruct(Process *proc, void *addr)
|
|
+{
|
|
assert(proc != NULL);
|
|
assert(proc->breakpoints != NULL);
|
|
assert(proc->leader == proc);
|
|
@@ -21,10 +39,11 @@ address2bpstruct(Process *proc, void *addr) {
|
|
return dict_find_entry(proc->breakpoints, addr);
|
|
}
|
|
|
|
-void
|
|
+struct breakpoint *
|
|
insert_breakpoint(Process *proc, void *addr,
|
|
- struct library_symbol *libsym, int enable) {
|
|
- Breakpoint *sbp;
|
|
+ struct library_symbol *libsym, int enable)
|
|
+{
|
|
+ struct breakpoint *sbp;
|
|
|
|
Process * leader = proc->leader;
|
|
|
|
@@ -43,16 +62,16 @@ insert_breakpoint(Process *proc, void *addr,
|
|
debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
|
|
|
|
if (!addr)
|
|
- return;
|
|
+ return NULL;
|
|
|
|
if (libsym)
|
|
libsym->needs_init = 0;
|
|
|
|
sbp = dict_find_entry(leader->breakpoints, addr);
|
|
- if (!sbp) {
|
|
- sbp = calloc(1, sizeof(Breakpoint));
|
|
- if (!sbp) {
|
|
- return; /* TODO FIXME XXX: error_mem */
|
|
+ if (sbp == NULL) {
|
|
+ sbp = calloc(1, sizeof(*sbp));
|
|
+ if (sbp == NULL) {
|
|
+ return NULL; /* TODO FIXME XXX: error_mem */
|
|
}
|
|
dict_enter(leader->breakpoints, addr, sbp);
|
|
sbp->addr = addr;
|
|
@@ -67,11 +86,14 @@ insert_breakpoint(Process *proc, void *addr,
|
|
assert(proc->pid != 0);
|
|
enable_breakpoint(proc, sbp);
|
|
}
|
|
+
|
|
+ return sbp;
|
|
}
|
|
|
|
void
|
|
-delete_breakpoint(Process *proc, void *addr) {
|
|
- Breakpoint *sbp;
|
|
+delete_breakpoint(Process *proc, void *addr)
|
|
+{
|
|
+ struct breakpoint *sbp;
|
|
|
|
debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
|
|
|
|
@@ -91,89 +113,67 @@ delete_breakpoint(Process *proc, void *addr) {
|
|
}
|
|
|
|
static void
|
|
-enable_bp_cb(void *addr, void *sbp, void *proc) {
|
|
+enable_bp_cb(void *addr, void *sbp, void *proc)
|
|
+{
|
|
debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
|
|
- if (((Breakpoint *)sbp)->enabled) {
|
|
+ if (((struct breakpoint *)sbp)->enabled)
|
|
enable_breakpoint(proc, sbp);
|
|
- }
|
|
}
|
|
|
|
void
|
|
-enable_all_breakpoints(Process *proc) {
|
|
+enable_all_breakpoints(Process *proc)
|
|
+{
|
|
debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
|
|
- if (proc->breakpoints_enabled <= 0) {
|
|
-#ifdef __powerpc__
|
|
- unsigned long a;
|
|
|
|
+ debug(1, "Enabling breakpoints for pid %u...", proc->pid);
|
|
+ if (proc->breakpoints) {
|
|
+ dict_apply_to_all(proc->breakpoints, enable_bp_cb,
|
|
+ proc);
|
|
+ }
|
|
+#ifdef __mips__
|
|
+ {
|
|
/*
|
|
- * PPC HACK! (XXX FIXME TODO)
|
|
- * If the dynamic linker hasn't populated the PLT then
|
|
- * dont enable the breakpoints
|
|
+ * I'm sure there is a nicer way to do this. We need to
|
|
+ * insert breakpoints _after_ the child has been started.
|
|
*/
|
|
- if (options.libcalls) {
|
|
- a = ptrace(PTRACE_PEEKTEXT, proc->pid,
|
|
- sym2addr(proc, proc->list_of_symbols),
|
|
- 0);
|
|
- if (a == 0x0)
|
|
- return;
|
|
- }
|
|
-#endif
|
|
-
|
|
- debug(1, "Enabling breakpoints for pid %u...", proc->pid);
|
|
- if (proc->breakpoints) {
|
|
- dict_apply_to_all(proc->breakpoints, enable_bp_cb,
|
|
- proc);
|
|
- }
|
|
-#ifdef __mips__
|
|
- {
|
|
- /*
|
|
- * I'm sure there is a nicer way to do this. We need to
|
|
- * insert breakpoints _after_ the child has been started.
|
|
- */
|
|
- struct library_symbol *sym;
|
|
- struct library_symbol *new_sym;
|
|
- sym=proc->list_of_symbols;
|
|
- while(sym){
|
|
- void *addr= sym2addr(proc,sym);
|
|
- if(!addr){
|
|
- sym=sym->next;
|
|
- continue;
|
|
- }
|
|
- if(dict_find_entry(proc->breakpoints,addr)){
|
|
- sym=sym->next;
|
|
- continue;
|
|
- }
|
|
- debug(2,"inserting bp %p %s",addr,sym->name);
|
|
- new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
|
|
- memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
|
|
- new_sym->next=proc->list_of_symbols;
|
|
- proc->list_of_symbols=new_sym;
|
|
- insert_breakpoint(proc, addr, new_sym);
|
|
+ struct library_symbol *sym;
|
|
+ struct library_symbol *new_sym;
|
|
+ sym=proc->list_of_symbols;
|
|
+ while(sym){
|
|
+ void *addr= sym2addr(proc,sym);
|
|
+ if(!addr){
|
|
sym=sym->next;
|
|
+ continue;
|
|
}
|
|
+ if(dict_find_entry(proc->breakpoints,addr)){
|
|
+ sym=sym->next;
|
|
+ continue;
|
|
+ }
|
|
+ debug(2,"inserting bp %p %s",addr,sym->name);
|
|
+ new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
|
|
+ memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
|
|
+ new_sym->next=proc->list_of_symbols;
|
|
+ proc->list_of_symbols=new_sym;
|
|
+ insert_breakpoint(proc, addr, new_sym);
|
|
+ sym=sym->next;
|
|
}
|
|
-#endif
|
|
}
|
|
- proc->breakpoints_enabled = 1;
|
|
+#endif
|
|
}
|
|
|
|
static void
|
|
-disable_bp_cb(void *addr, void *sbp, void *proc) {
|
|
+disable_bp_cb(void *addr, void *sbp, void *proc)
|
|
+{
|
|
debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
|
|
- if (((Breakpoint *)sbp)->enabled) {
|
|
+ if (((struct breakpoint *)sbp)->enabled)
|
|
disable_breakpoint(proc, sbp);
|
|
- }
|
|
}
|
|
|
|
void
|
|
disable_all_breakpoints(Process *proc) {
|
|
debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
|
|
assert(proc->leader == proc);
|
|
- if (proc->breakpoints_enabled) {
|
|
- debug(1, "Disabling breakpoints for pid %u...", proc->pid);
|
|
- dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
|
|
- }
|
|
- proc->breakpoints_enabled = 0;
|
|
+ dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
|
|
}
|
|
|
|
static void
|
|
@@ -183,11 +183,18 @@ free_bp_cb(void *addr, void *sbp, void *data) {
|
|
free(sbp);
|
|
}
|
|
|
|
+static void
|
|
+entry_callback_hit(struct breakpoint *bp, struct Process *proc)
|
|
+{
|
|
+ if (proc == NULL || proc->leader == NULL)
|
|
+ return;
|
|
+ delete_breakpoint(proc, bp->addr); // xxx
|
|
+ reinitialize_breakpoints(proc->leader);
|
|
+}
|
|
+
|
|
int
|
|
breakpoints_init(Process *proc, int enable)
|
|
{
|
|
- struct library_symbol *sym;
|
|
-
|
|
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);
|
|
@@ -206,9 +213,11 @@ breakpoints_init(Process *proc, int enable)
|
|
destroy_library_symbol_chain(proc->list_of_symbols);
|
|
proc->list_of_symbols = NULL;
|
|
|
|
+ GElf_Addr entry;
|
|
if (options.libcalls && proc->filename) {
|
|
- proc->list_of_symbols = read_elf(proc);
|
|
+ proc->list_of_symbols = read_elf(proc, &entry);
|
|
if (proc->list_of_symbols == NULL) {
|
|
+ fail:
|
|
/* XXX leak breakpoints */
|
|
return -1;
|
|
}
|
|
@@ -235,11 +244,19 @@ breakpoints_init(Process *proc, int enable)
|
|
}
|
|
}
|
|
|
|
- for (sym = proc->list_of_symbols; sym; sym = sym->next)
|
|
- insert_breakpoint(proc, sym2addr(proc, sym), sym, enable);
|
|
+ struct breakpoint *entry_bp
|
|
+ = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1);
|
|
+ if (entry_bp == NULL) {
|
|
+ fprintf(stderr, "fail!\n");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ static struct bp_callbacks entry_callbacks = {
|
|
+ .on_hit = entry_callback_hit,
|
|
+ };
|
|
+ entry_bp->cbs = &entry_callbacks;
|
|
|
|
proc->callstack_depth = 0;
|
|
- proc->breakpoints_enabled = -1;
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/common.h b/common.h
|
|
index 715898d..a47cd89 100644
|
|
--- a/common.h
|
|
+++ b/common.h
|
|
@@ -26,17 +26,6 @@ extern char * command;
|
|
|
|
extern int exiting; /* =1 if we have to exit ASAP */
|
|
|
|
-typedef struct Breakpoint Breakpoint;
|
|
-struct Breakpoint {
|
|
- void * addr;
|
|
- unsigned char orig_value[BREAKPOINT_LENGTH];
|
|
- int enabled;
|
|
- struct library_symbol * libsym;
|
|
-#ifdef __arm__
|
|
- int thumb_mode;
|
|
-#endif
|
|
-};
|
|
-
|
|
enum arg_type {
|
|
ARGTYPE_UNKNOWN = -1,
|
|
ARGTYPE_VOID,
|
|
@@ -187,11 +176,10 @@ struct Process {
|
|
pid_t pid;
|
|
|
|
/* Dictionary of breakpoints (which is a mapping
|
|
- * address->Breakpoint). This is NULL for non-leader
|
|
+ * address->breakpoint). This is NULL for non-leader
|
|
* processes. */
|
|
Dict * breakpoints;
|
|
|
|
- int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */
|
|
int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */
|
|
unsigned int personality;
|
|
int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */
|
|
@@ -296,14 +284,7 @@ extern void destroy_event_handler(Process * proc);
|
|
|
|
extern pid_t execute_program(const char * command, char ** argv);
|
|
extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
|
|
-extern Breakpoint * address2bpstruct(Process * proc, void * addr);
|
|
-extern int breakpoints_init(Process * proc, int enable);
|
|
-extern void insert_breakpoint(Process * proc, void * addr,
|
|
- struct library_symbol * libsym, int enable);
|
|
-extern void delete_breakpoint(Process * proc, void * addr);
|
|
-extern void enable_all_breakpoints(Process * proc);
|
|
extern void disable_all_breakpoints(Process * proc);
|
|
-extern void reinitialize_breakpoints(Process *);
|
|
|
|
extern Process * open_program(char * filename, pid_t pid, int init_breakpoints);
|
|
extern void open_pid(pid_t pid);
|
|
@@ -322,6 +303,8 @@ 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;
|
|
+
|
|
/* Arch-dependent stuff: */
|
|
extern char * pid2name(pid_t pid);
|
|
extern pid_t process_leader(pid_t pid);
|
|
@@ -329,6 +312,7 @@ extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n);
|
|
extern int process_stopped(pid_t pid);
|
|
extern enum process_status process_status(pid_t pid);
|
|
extern void trace_set_options(Process * proc, pid_t pid);
|
|
+extern void wait_for_proc(pid_t pid);
|
|
extern void trace_me(void);
|
|
extern int trace_pid(pid_t pid);
|
|
extern void untrace_pid(pid_t pid);
|
|
@@ -338,13 +322,13 @@ extern void set_instruction_pointer(Process * proc, void * addr);
|
|
extern void * get_stack_pointer(Process * proc);
|
|
extern void * get_return_addr(Process * proc, void * stack_pointer);
|
|
extern void set_return_addr(Process * proc, void * addr);
|
|
-extern void enable_breakpoint(Process * proc, Breakpoint * sbp);
|
|
-extern void disable_breakpoint(Process * proc, Breakpoint * sbp);
|
|
+extern void enable_breakpoint(Process * proc, struct breakpoint *sbp);
|
|
+extern void disable_breakpoint(Process * proc, struct breakpoint *sbp);
|
|
extern int syscall_p(Process * proc, int status, int * sysnum);
|
|
extern void continue_process(pid_t pid);
|
|
extern void continue_after_signal(pid_t pid, int signum);
|
|
extern void continue_after_syscall(Process *proc, int sysnum, int ret_p);
|
|
-extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp);
|
|
+extern void continue_after_breakpoint(Process * proc, struct breakpoint *sbp);
|
|
extern void continue_after_vfork(Process * proc);
|
|
extern void ltrace_exiting(void);
|
|
extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
|
|
diff --git a/execute_program.c b/execute_program.c
|
|
index 47f514d..859f32c 100644
|
|
--- a/execute_program.c
|
|
+++ b/execute_program.c
|
|
@@ -89,6 +89,8 @@ execute_program(const char * command, char **argv)
|
|
_exit(1);
|
|
}
|
|
|
|
+ wait_for_proc(pid);
|
|
+
|
|
debug(1, "PID=%d", pid);
|
|
|
|
return pid;
|
|
diff --git a/handle_event.c b/handle_event.c
|
|
index 203459c..c146eb9 100644
|
|
--- a/handle_event.c
|
|
+++ b/handle_event.c
|
|
@@ -1,6 +1,6 @@
|
|
+#define _GNU_SOURCE
|
|
#include "config.h"
|
|
|
|
-#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
@@ -9,12 +9,13 @@
|
|
#include <sys/time.h>
|
|
#include <errno.h>
|
|
|
|
-#include "common.h"
|
|
-
|
|
#ifdef __powerpc__
|
|
#include <sys/ptrace.h>
|
|
#endif
|
|
|
|
+#include "common.h"
|
|
+#include "breakpoint.h"
|
|
+
|
|
static void handle_signal(Event *event);
|
|
static void handle_exit(Event *event);
|
|
static void handle_exit_signal(Event *event);
|
|
@@ -155,19 +156,18 @@ address_clone(void * addr, void * data)
|
|
}
|
|
|
|
static void *
|
|
-breakpoint_clone(void * bp, void * data)
|
|
+breakpoint_clone(void *bp, void *data)
|
|
{
|
|
- Breakpoint * b;
|
|
- Dict * map = data;
|
|
+ Dict *map = data;
|
|
debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp);
|
|
- b = malloc(sizeof(Breakpoint));
|
|
+ struct breakpoint *b = malloc(sizeof(*b));
|
|
if (!b) {
|
|
perror("malloc()");
|
|
exit(1);
|
|
}
|
|
- memcpy(b, bp, sizeof(Breakpoint));
|
|
+ memcpy(b, bp, sizeof(*b));
|
|
if (b->libsym != NULL) {
|
|
- struct library_symbol * sym = dict_find_entry(map, b->libsym);
|
|
+ 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);
|
|
@@ -452,9 +452,6 @@ handle_syscall(Event *event) {
|
|
output_left(LT_TOF_SYSCALL, event->proc,
|
|
sysname(event->proc, event->e_un.sysnum));
|
|
}
|
|
- if (event->proc->breakpoints_enabled == 0) {
|
|
- enable_all_breakpoints(event->proc);
|
|
- }
|
|
}
|
|
continue_after_syscall(event->proc, event->e_un.sysnum, 0);
|
|
}
|
|
@@ -462,7 +459,6 @@ handle_syscall(Event *event) {
|
|
static void
|
|
handle_exec(Event * event) {
|
|
Process * proc = event->proc;
|
|
- pid_t saved_pid;
|
|
|
|
debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid);
|
|
if (proc->state == STATE_IGNORED) {
|
|
@@ -476,10 +472,7 @@ handle_exec(Event * event) {
|
|
proc->arch_ptr = NULL;
|
|
free(proc->filename);
|
|
proc->filename = pid2name(proc->pid);
|
|
- saved_pid = proc->pid;
|
|
- proc->pid = 0;
|
|
breakpoints_init(proc, 0);
|
|
- proc->pid = saved_pid;
|
|
proc->callstack_depth = 0;
|
|
continue_process(proc->pid);
|
|
}
|
|
@@ -493,9 +486,6 @@ handle_arch_syscall(Event *event) {
|
|
output_left(LT_TOF_SYSCALL, event->proc,
|
|
arch_sysname(event->proc, event->e_un.sysnum));
|
|
}
|
|
- if (event->proc->breakpoints_enabled == 0) {
|
|
- enable_all_breakpoints(event->proc);
|
|
- }
|
|
}
|
|
continue_process(event->proc->pid);
|
|
}
|
|
@@ -559,14 +549,11 @@ handle_arch_sysret(Event *event) {
|
|
continue_process(event->proc->pid);
|
|
}
|
|
|
|
-#ifdef __powerpc__
|
|
-void *get_count_register (Process *proc);
|
|
-#endif
|
|
-
|
|
static void
|
|
-handle_breakpoint(Event *event) {
|
|
+handle_breakpoint(Event *event)
|
|
+{
|
|
int i, j;
|
|
- Breakpoint *sbp;
|
|
+ struct breakpoint *sbp;
|
|
Process *leader = event->proc->leader;
|
|
|
|
/* The leader has terminated. */
|
|
@@ -578,28 +565,6 @@ handle_breakpoint(Event *event) {
|
|
debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr);
|
|
debug(2, "event: breakpoint (%p)", event->e_un.brk_addr);
|
|
|
|
-#ifdef __powerpc__
|
|
- /* Need to skip following NOP's to prevent a fake function from being stacked. */
|
|
- long stub_addr = (long) get_count_register(event->proc);
|
|
- Breakpoint *stub_bp = NULL;
|
|
- char nop_instruction[] = PPC_NOP;
|
|
-
|
|
- stub_bp = address2bpstruct(leader, event->e_un.brk_addr);
|
|
-
|
|
- if (stub_bp) {
|
|
- unsigned char *bp_instruction = stub_bp->orig_value;
|
|
-
|
|
- if (memcmp(bp_instruction, nop_instruction,
|
|
- PPC_NOP_LENGTH) == 0) {
|
|
- if (stub_addr != (long) event->e_un.brk_addr) {
|
|
- set_instruction_pointer (event->proc, event->e_un.brk_addr + 4);
|
|
- continue_process(event->proc->pid);
|
|
- return;
|
|
- }
|
|
- }
|
|
- }
|
|
-#endif
|
|
-
|
|
for (i = event->proc->callstack_depth - 1; i >= 0; i--) {
|
|
if (event->e_un.brk_addr ==
|
|
event->proc->callstack[i].return_addr) {
|
|
@@ -679,6 +644,8 @@ handle_breakpoint(Event *event) {
|
|
}
|
|
|
|
if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) {
|
|
+ breakpoint_on_hit(sbp, event->proc);
|
|
+
|
|
if (sbp->libsym == NULL) {
|
|
continue_after_breakpoint(event->proc, sbp);
|
|
return;
|
|
@@ -696,12 +663,6 @@ handle_breakpoint(Event *event) {
|
|
callstack_push_symfunc(event->proc, sbp->libsym);
|
|
output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name);
|
|
}
|
|
-#ifdef PLT_REINITALISATION_BP
|
|
- if (event->proc->need_to_reinitialize_breakpoints
|
|
- && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) ==
|
|
- 0))
|
|
- reinitialize_breakpoints(leader);
|
|
-#endif
|
|
|
|
continue_after_breakpoint(event->proc, sbp);
|
|
return;
|
|
diff --git a/ltrace-elf.c b/ltrace-elf.c
|
|
index 8dbc298..f7fc239 100644
|
|
--- a/ltrace-elf.c
|
|
+++ b/ltrace-elf.c
|
|
@@ -662,7 +662,8 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) {
|
|
}
|
|
|
|
struct library_symbol *
|
|
-read_elf(Process *proc) {
|
|
+read_elf(Process *proc, GElf_Addr *entryp)
|
|
+{
|
|
struct ltelf lte[MAX_LIBRARIES + 1];
|
|
size_t i;
|
|
struct opt_x_t *xptr;
|
|
@@ -700,7 +701,6 @@ read_elf(Process *proc) {
|
|
#ifdef __mips__
|
|
// MIPS doesn't use the PLT and the GOT entries get changed
|
|
// on startup.
|
|
- proc->need_to_reinitialize_breakpoints = 1;
|
|
for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){
|
|
GElf_Sym sym;
|
|
const char *name;
|
|
@@ -745,11 +745,6 @@ read_elf(Process *proc) {
|
|
"Couldn't get relocation from \"%s\"",
|
|
proc->filename);
|
|
|
|
-#ifdef PLT_REINITALISATION_BP
|
|
- if (!sym.st_value && PLTs_initialized_by_here)
|
|
- proc->need_to_reinitialize_breakpoints = 1;
|
|
-#endif
|
|
-
|
|
name = lte->dynstr + sym.st_name;
|
|
count = library_num ? library_num+1 : 0;
|
|
|
|
@@ -772,30 +767,6 @@ read_elf(Process *proc) {
|
|
}
|
|
}
|
|
#endif // !__mips__
|
|
-#ifdef PLT_REINITALISATION_BP
|
|
- struct opt_x_t *main_cheat;
|
|
-
|
|
- if (proc->need_to_reinitialize_breakpoints) {
|
|
- /* Add "PLTs_initialized_by_here" to opt_x list, if not
|
|
- already there. */
|
|
- main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t));
|
|
- if (main_cheat == NULL)
|
|
- error(EXIT_FAILURE, 0, "Couldn't allocate memory");
|
|
- main_cheat->next = opt_x_loc;
|
|
- main_cheat->found = 0;
|
|
- main_cheat->name = PLTs_initialized_by_here;
|
|
-
|
|
- for (xptr = opt_x_loc; xptr; xptr = xptr->next)
|
|
- if (strcmp(xptr->name, PLTs_initialized_by_here) == 0
|
|
- && main_cheat) {
|
|
- free(main_cheat);
|
|
- main_cheat = NULL;
|
|
- break;
|
|
- }
|
|
- if (main_cheat)
|
|
- opt_x_loc = main_cheat;
|
|
- }
|
|
-#endif
|
|
} else {
|
|
lib_tail = &library_symbols;
|
|
}
|
|
@@ -850,16 +821,17 @@ read_elf(Process *proc) {
|
|
}
|
|
}
|
|
|
|
+ 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) {
|
|
- add_library_symbol (
|
|
- opd2addr (lte, lte->ehdr.e_entry),
|
|
- PLTs_initialized_by_here,
|
|
- lib_tail, 1, 0);
|
|
fprintf (stderr, "WARNING: Using e_ent"
|
|
"ry from elf header (%p) for "
|
|
"address of \"%s\"\n", (void*)
|
|
diff --git a/ltrace-elf.h b/ltrace-elf.h
|
|
index 3b675c5..4da8a0a 100644
|
|
--- a/ltrace-elf.h
|
|
+++ b/ltrace-elf.h
|
|
@@ -45,7 +45,7 @@ extern size_t library_num;
|
|
extern char *library[MAX_LIBRARIES];
|
|
|
|
extern int open_elf(struct ltelf *lte, const char *filename);
|
|
-extern struct library_symbol *read_elf(Process *);
|
|
+extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp);
|
|
|
|
extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *);
|
|
|
|
diff --git a/proc.c b/proc.c
|
|
index f4d3396..5febc3f 100644
|
|
--- a/proc.c
|
|
+++ b/proc.c
|
|
@@ -14,6 +14,7 @@
|
|
#include <error.h>
|
|
|
|
#include "common.h"
|
|
+#include "breakpoint.h"
|
|
|
|
Process *
|
|
open_program(char *filename, pid_t pid, int enable) {
|
|
@@ -26,7 +27,6 @@ open_program(char *filename, pid_t pid, int enable) {
|
|
}
|
|
|
|
proc->filename = strdup(filename);
|
|
- proc->breakpoints_enabled = -1;
|
|
proc->pid = pid;
|
|
#if defined(HAVE_LIBUNWIND)
|
|
proc->unwind_priv = _UPT_create(pid);
|
|
@@ -39,13 +39,15 @@ open_program(char *filename, pid_t pid, int enable) {
|
|
return NULL;
|
|
}
|
|
|
|
- if (proc->leader == 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;
|
|
}
|
|
+ }
|
|
|
|
return proc;
|
|
}
|
|
@@ -73,11 +75,10 @@ open_one_pid(pid_t pid)
|
|
return 0;
|
|
}
|
|
|
|
-enum pcb_status
|
|
+static enum pcb_status
|
|
start_one_pid(Process * proc, void * data)
|
|
{
|
|
continue_process(proc->pid);
|
|
- proc->breakpoints_enabled = 1;
|
|
return pcb_cont;
|
|
}
|
|
|
|
@@ -116,7 +117,7 @@ open_pid(pid_t pid)
|
|
if (process_tasks(pid, &tasks, &ntasks) < 0) {
|
|
fprintf(stderr, "Cannot obtain tasks of pid %u: %s\n",
|
|
pid, strerror(errno));
|
|
- goto start;
|
|
+ break;
|
|
}
|
|
|
|
have_all = 1;
|
|
@@ -135,7 +136,6 @@ open_pid(pid_t pid)
|
|
/* Done. Now initialize breakpoints and then continue
|
|
* everyone. */
|
|
Process * leader;
|
|
-start:
|
|
leader = pid2proc(pid)->leader;
|
|
enable_all_breakpoints(leader);
|
|
|
|
diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c
|
|
index 4e17940..493f973 100644
|
|
--- a/sysdeps/linux-gnu/arm/breakpoint.c
|
|
+++ b/sysdeps/linux-gnu/arm/breakpoint.c
|
|
@@ -27,7 +27,8 @@
|
|
#include "common.h"
|
|
|
|
void
|
|
-arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) {
|
|
+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp)
|
|
+{
|
|
unsigned int i, j;
|
|
const unsigned char break_insn[] = BREAKPOINT_VALUE;
|
|
const unsigned char thumb_break_insn[] = THUMB_BREAKPOINT_VALUE;
|
|
@@ -59,7 +60,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) {
|
|
}
|
|
|
|
void
|
|
-arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) {
|
|
+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp)
|
|
+{
|
|
unsigned int i, j;
|
|
|
|
debug(1, "arch_disable_breakpoint(%d,%p)", pid, sbp->addr);
|
|
diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c
|
|
index 5a49e9d..b98374b 100644
|
|
--- a/sysdeps/linux-gnu/breakpoint.c
|
|
+++ b/sysdeps/linux-gnu/breakpoint.c
|
|
@@ -5,12 +5,13 @@
|
|
|
|
#include "common.h"
|
|
#include "arch.h"
|
|
+#include "breakpoint.h"
|
|
|
|
#ifdef ARCH_HAVE_ENABLE_BREAKPOINT
|
|
-extern void arch_enable_breakpoint(pid_t, Breakpoint *);
|
|
+extern void arch_enable_breakpoint(pid_t, struct breakpoint *);
|
|
#else /* ARCH_HAVE_ENABLE_BREAKPOINT */
|
|
void
|
|
-arch_enable_breakpoint(pid_t pid, Breakpoint *sbp)
|
|
+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp)
|
|
{
|
|
static unsigned char break_insn[] = BREAKPOINT_VALUE;
|
|
unsigned int i, j;
|
|
@@ -38,7 +39,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp)
|
|
#endif /* ARCH_HAVE_ENABLE_BREAKPOINT */
|
|
|
|
void
|
|
-enable_breakpoint(Process * proc, Breakpoint *sbp) {
|
|
+enable_breakpoint(Process *proc, struct breakpoint *sbp)
|
|
+{
|
|
if (sbp->libsym) {
|
|
debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name);
|
|
} else {
|
|
@@ -48,10 +50,10 @@ enable_breakpoint(Process * proc, Breakpoint *sbp) {
|
|
}
|
|
|
|
#ifdef ARCH_HAVE_DISABLE_BREAKPOINT
|
|
-extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp);
|
|
+extern void arch_disable_breakpoint(pid_t, const struct breakpoint *sbp);
|
|
#else /* ARCH_HAVE_DISABLE_BREAKPOINT */
|
|
void
|
|
-arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp)
|
|
+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp)
|
|
{
|
|
unsigned int i, j;
|
|
|
|
@@ -78,7 +80,8 @@ arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp)
|
|
#endif /* ARCH_HAVE_DISABLE_BREAKPOINT */
|
|
|
|
void
|
|
-disable_breakpoint(Process * proc, Breakpoint *sbp) {
|
|
+disable_breakpoint(Process *proc, struct breakpoint *sbp)
|
|
+{
|
|
if (sbp->libsym) {
|
|
debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name);
|
|
} else {
|
|
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
|
|
index 021192f..3174c1a 100644
|
|
--- a/sysdeps/linux-gnu/events.c
|
|
+++ b/sysdeps/linux-gnu/events.c
|
|
@@ -12,6 +12,7 @@
|
|
#include <unistd.h>
|
|
|
|
#include "common.h"
|
|
+#include "breakpoint.h"
|
|
|
|
static Event event;
|
|
|
|
@@ -166,19 +167,9 @@ next_event(void)
|
|
}
|
|
get_arch_dep(event.proc);
|
|
debug(3, "event from pid %u", pid);
|
|
- if (event.proc->breakpoints_enabled == -1)
|
|
- trace_set_options(event.proc, event.proc->pid);
|
|
Process *leader = event.proc->leader;
|
|
if (leader == event.proc) {
|
|
- if (event.proc->breakpoints_enabled == -1) {
|
|
- event.type = EVENT_NONE;
|
|
- enable_all_breakpoints(event.proc);
|
|
- continue_process(event.proc->pid);
|
|
- debug(DEBUG_EVENT,
|
|
- "event: NONE: pid=%d (enabling breakpoints)",
|
|
- pid);
|
|
- return &event;
|
|
- } else if (!event.proc->libdl_hooked) {
|
|
+ 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;
|
|
diff --git a/sysdeps/linux-gnu/ia64/breakpoint.c b/sysdeps/linux-gnu/ia64/breakpoint.c
|
|
index 45ee11e..a0bfaf9 100644
|
|
--- a/sysdeps/linux-gnu/ia64/breakpoint.c
|
|
+++ b/sysdeps/linux-gnu/ia64/breakpoint.c
|
|
@@ -150,7 +150,8 @@ union bundle_t {
|
|
};
|
|
|
|
void
|
|
-arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) {
|
|
+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp)
|
|
+{
|
|
|
|
unsigned long addr = (unsigned long)sbp->addr;
|
|
union bundle_t bundle;
|
|
@@ -187,7 +188,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) {
|
|
}
|
|
|
|
void
|
|
-arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) {
|
|
+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp)
|
|
+{
|
|
|
|
unsigned long addr = (unsigned long)sbp->addr;
|
|
int slotnum = (int)(addr & 0x0f) & 0x3;
|
|
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
|
|
index a99593c..3350117 100644
|
|
--- a/sysdeps/linux-gnu/proc.c
|
|
+++ b/sysdeps/linux-gnu/proc.c
|
|
@@ -1,6 +1,5 @@
|
|
#define _GNU_SOURCE /* For getline. */
|
|
#include "config.h"
|
|
-#include "common.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
@@ -17,6 +16,8 @@
|
|
#include <sys/syscall.h>
|
|
#include <error.h>
|
|
|
|
+#include "common.h"
|
|
+#include "breakpoint.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
|
|
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
|
|
index db18df0..b4d8fd1 100644
|
|
--- a/sysdeps/linux-gnu/trace.c
|
|
+++ b/sysdeps/linux-gnu/trace.c
|
|
@@ -11,6 +11,7 @@
|
|
|
|
#include "common.h"
|
|
#include "config.h"
|
|
+#include "breakpoint.h"
|
|
|
|
/* If the system headers did not provide the constants, hard-code the normal
|
|
values. */
|
|
@@ -77,6 +78,29 @@ trace_me(void) {
|
|
}
|
|
}
|
|
|
|
+/* There's a (hopefully) brief period of time after the child process
|
|
+ * exec's when we can't trace it yet. Here we wait for kernel to
|
|
+ * prepare the process. */
|
|
+void
|
|
+wait_for_proc(pid_t pid)
|
|
+{
|
|
+ size_t i;
|
|
+ for (i = 0; i < 100; ++i) {
|
|
+ /* We read from memory address 0, but that shouldn't
|
|
+ * be a problem: the reading will just fail. We are
|
|
+ * looking for a particular reason of failure. */
|
|
+ if (ptrace(PTRACE_PEEKTEXT, pid, 0, 0) != -1
|
|
+ || errno != ESRCH)
|
|
+ return;
|
|
+
|
|
+ usleep(1000);
|
|
+ }
|
|
+
|
|
+ fprintf(stderr, "\
|
|
+I consistently fail to read a word from the freshly launched process.\n\
|
|
+I'll now try to proceed with tracing, but this shouldn't be happening.\n");
|
|
+}
|
|
+
|
|
int
|
|
trace_pid(pid_t pid) {
|
|
debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid);
|
|
@@ -193,7 +217,7 @@ struct process_stopping_handler
|
|
Process * task_enabling_breakpoint;
|
|
|
|
/* The pointer being re-enabled. */
|
|
- Breakpoint * breakpoint_being_enabled;
|
|
+ struct breakpoint *breakpoint_being_enabled;
|
|
|
|
enum {
|
|
/* We are waiting for everyone to land in t/T. */
|
|
@@ -364,7 +388,7 @@ static void
|
|
ugly_workaround(Process * proc)
|
|
{
|
|
void * ip = get_instruction_pointer(proc);
|
|
- Breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip);
|
|
+ struct breakpoint *sbp = dict_find_entry(proc->leader->breakpoints, ip);
|
|
if (sbp != NULL)
|
|
enable_breakpoint(proc, sbp);
|
|
else
|
|
@@ -578,7 +602,7 @@ process_stopping_on_event(Event_Handler * super, Event * event)
|
|
struct process_stopping_handler * self = (void *)super;
|
|
Process * task = event->proc;
|
|
Process * leader = task->leader;
|
|
- Breakpoint * sbp = self->breakpoint_being_enabled;
|
|
+ struct breakpoint *sbp = self->breakpoint_being_enabled;
|
|
Process * teb = self->task_enabling_breakpoint;
|
|
|
|
debug(DEBUG_PROCESS,
|
|
@@ -687,7 +711,7 @@ process_stopping_destroy(Event_Handler * super)
|
|
}
|
|
|
|
void
|
|
-continue_after_breakpoint(Process *proc, Breakpoint *sbp)
|
|
+continue_after_breakpoint(Process *proc, struct breakpoint *sbp)
|
|
{
|
|
set_instruction_pointer(proc, sbp->addr);
|
|
if (sbp->enabled == 0) {
|
|
@@ -851,7 +875,7 @@ static Event *
|
|
process_vfork_on_event(Event_Handler * super, Event * event)
|
|
{
|
|
struct process_vfork_handler * self = (void *)super;
|
|
- Breakpoint * sbp;
|
|
+ struct breakpoint *sbp;
|
|
assert(self != NULL);
|
|
|
|
switch (event->type) {
|