From 6e5a20e2b8b7045cafef5a2752f3b1a87d47525a Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 8 Feb 2012 00:18:26 +0100 Subject: [PATCH] Add upstream patches for initial breakpoint insertion --- ltrace-0.6.0-process-start.patch | 1053 ++++++++++++++++++++++++++++++ ltrace.spec | 9 +- 2 files changed, 1061 insertions(+), 1 deletion(-) create mode 100644 ltrace-0.6.0-process-start.patch diff --git a/ltrace-0.6.0-process-start.patch b/ltrace-0.6.0-process-start.patch new file mode 100644 index 0000000..1a95e25 --- /dev/null +++ b/ltrace-0.6.0-process-start.patch @@ -0,0 +1,1053 @@ +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 + #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 + #include + #include +@@ -9,12 +9,13 @@ + #include + #include + +-#include "common.h" +- + #ifdef __powerpc__ + #include + #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; idynsym_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 + + #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 + + #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 + #include +@@ -17,6 +16,8 @@ + #include + #include + ++#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) { diff --git a/ltrace.spec b/ltrace.spec index e3bfaf5..c0840e5 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,7 +1,7 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace Version: 0.6.0 -Release: 3%{?dist} +Release: 4%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers @@ -27,6 +27,7 @@ Patch9: ltrace-0.6.0-ppc-args.patch Patch10: ltrace-0.6.0-ppc-shift.patch Patch11: ltrace-0.6.0-vfork.patch Patch12: ltrace-0.6.0-thread-races.patch +Patch13: ltrace-0.6.0-process-start.patch %description Ltrace is a debugging program which runs a specified command until the @@ -52,6 +53,7 @@ execution of processes. %patch10 -p1 %patch11 -p1 %patch12 -p1 +%patch13 -p1 sed -i -e 's/-o root -g root//' Makefile.in %build @@ -84,6 +86,11 @@ rm -rf $RPM_BUILD_ROOT %config(noreplace) %{_sysconfdir}/ltrace.conf %changelog +* Tue Feb 7 2012 Petr Machata - 0.6.0-4 +- Add upstream patches for initial breakpoint insertion. This mostly + fixes tracing on PPC. +- Resolves: #773050 + * Fri Nov 25 2011 Petr Machata - 0.6.0-3 - Add several upstream patches that fix various races in tracing multi-threaded processes