From e3652d8901a3e72b4dce9acb2cbcdb0e29774f29 Mon Sep 17 00:00:00 2001 From: Stefani Seibold Date: Wed, 29 Apr 2015 15:58:53 +0200 Subject: [PATCH] fix attach for 64 bit processes init the backtrace for the cloned threads after the leader is initialized --- dwarf.c | 20 ++++++++++---------- dwarf.h | 2 +- event.c | 1 - report.c | 8 ++++---- sysdeps/linux-gnu/arm/regs.c | 15 +++++++++++---- sysdeps/linux-gnu/ppc/regs.c | 18 ++++++++++++------ sysdeps/linux-gnu/proc.c | 2 +- sysdeps/linux-gnu/trace.c | 24 +++++++++++++----------- sysdeps/linux-gnu/x86/arch.c | 2 +- sysdeps/linux-gnu/x86/dwarf-x86.c | 8 ++++---- sysdeps/linux-gnu/x86/regs.c | 20 +++++++++++++------- task.c | 21 +++++++++++++-------- task.h | 6 ++++++ 13 files changed, 89 insertions(+), 58 deletions(-) diff --git a/dwarf.c b/dwarf.c index 3f7e20d..a714e04 100644 --- a/dwarf.c +++ b/dwarf.c @@ -467,7 +467,7 @@ static int dwarf_read_encoded_pointer(struct dwarf_addr_space *as, int local, struct dwarf_addr_space *indirect_as = as; arch_addr_t val, initial_addr = *addr; arch_addr_t gp = as->cursor.lib->gp; - int is_64bit = as->task->is_64bit; + int is_64bit = task_is_64bit(as->task); void *tmp_ptr; int ret; union { @@ -641,7 +641,7 @@ static int parse_cie(struct dwarf_addr_space *as, arch_addr_t addr, struct dwarf "address-unit sized constants". The `R' augmentation can be used to override this, but by default, we pick an address-sized unit for fde_encoding. */ - if (as->task->is_64bit) + if (task_is_64bit(as->task)) fde_encoding = DW_EH_PE_udata8; else fde_encoding = DW_EH_PE_udata4; @@ -958,8 +958,8 @@ static int dwarf_search_unwind_table(struct dwarf_addr_space *as, arch_addr_t ip dci->start_ip -= ARCH_ADDR_T(lib->image_addr) - lib->load_addr; - if (!as->task->is_64bit) - dci->start_ip = (uint32_t)dci->start_ip; + if (!task_is_64bit(as->task)) + dci->start_ip &= 0xffffffff; if (ip < dci->start_ip || ip >= dci->start_ip + dci->ip_range) { debug(DEBUG_DWARF, "IP %#lx out of range %#lx-%#lx", ip, dci->start_ip, dci->start_ip + dci->ip_range); @@ -991,11 +991,11 @@ int dwarf_get(struct dwarf_addr_space *as, struct dwarf_loc loc, arch_addr_t *va if (DWARF_IS_REG_LOC(loc)) return dwarf_access_reg(as, val, valp); - if (!as->task->is_64bit) + if (!task_is_64bit(as->task)) val &= 0xffffffff; if (DWARF_IS_MEM_LOC(loc)) - return dwarf_readw(as, &val, valp, as->task->is_64bit); + return dwarf_readw(as, &val, valp, task_is_64bit(as->task)); *valp = val; return 0; @@ -1331,7 +1331,7 @@ static int parse_fde(struct dwarf_addr_space *as, arch_addr_t ip, struct dwarf_r static long sword(struct dwarf_addr_space *as, arch_addr_t val) { - if (as->task->is_64bit) + if (task_is_64bit(as->task)) return (int64_t)val; else return (int32_t)val; @@ -1348,7 +1348,7 @@ static arch_addr_t read_operand(struct dwarf_addr_space *as, arch_addr_t *addr, int ret; if (operand_type == ADDR) { - if (as->task->is_64bit) + if (task_is_64bit(as->task)) operand_type = VAL64; else operand_type = VAL32; @@ -1509,7 +1509,7 @@ do { \ break; case DW_OP_deref: tmp1 = pop(); - if ((ret = dwarf_readw(as, &tmp1, &tmp2, as->task->is_64bit)) < 0) + if ((ret = dwarf_readw(as, &tmp1, &tmp2, task_is_64bit(as->task))) < 0) return ret; push(tmp2); break; @@ -1945,7 +1945,7 @@ fail: unsigned int i; for(i = 0; i < 16; ++i) { - if (dwarf_readw(as, &cfa, &ip, as->task->is_64bit)) + if (dwarf_readw(as, &cfa, &ip, task_is_64bit(as->task))) break; if (!dwarf_locate_map(as, ip)) { diff --git a/dwarf.h b/dwarf.h index 31cb72b..e2953e9 100644 --- a/dwarf.h +++ b/dwarf.h @@ -46,7 +46,7 @@ #define DWARF_IS_REG_LOC(l) ((l).type == DWARF_LOC_TYPE_REG) #define DWARF_IS_VAL_LOC(l) ((l).type == DWARF_LOC_TYPE_VAL) -#define DWARF_ADDR_SIZE(as) ((as)->task->is_64bit ? 8 : 4) +#define DWARF_ADDR_SIZE(as) (task_is_64bit((as)->task) ? 8 : 4) #define DWARF_ENOMEM 1 /* out of memory */ #define DWARF_EBADREG 2 /* bad register number */ diff --git a/event.c b/event.c index 6d53f8e..19e1f36 100644 --- a/event.c +++ b/event.c @@ -265,7 +265,6 @@ static struct task *handle_breakpoint(struct task *task) } if (breakpoint_on_hit(task, bp)) { - set_instruction_pointer(task, bp->addr); continue_task(task, 0); return task; } diff --git a/report.c b/report.c index 3f6f29b..c43ed24 100644 --- a/report.c +++ b/report.c @@ -98,7 +98,7 @@ static int report_alloc(struct task *task, enum mt_operation op, unsigned long p debug(DEBUG_FUNCTION, "%d [%d]: %#lx %lu", op, task->pid, ptr, size); - if (task->is_64bit) + if (task_is_64bit(task)) return report_alloc64(task, op, ptr, size, depth); else return report_alloc32(task, op, ptr, size, depth); @@ -202,7 +202,7 @@ static int _report_mmap64(struct task *task, struct library_symbol *libsym) size.l = fetch_param(task, 1); - if (!task->is_64bit) { + if (!task_is_64bit(task)) { size.v.v1 = fetch_param(task, 1); size.v.v2 = fetch_param(task, 2); } @@ -248,7 +248,7 @@ static int _report_posix_memalign(struct task *task, struct library_symbol *libs unsigned long ptr = fetch_param(task, 0); unsigned long new_ptr; - if (task->is_64bit) + if (task_is_64bit(task)) copy_from_proc(task, ARCH_ADDR_T(ptr), &new_ptr, sizeof(new_ptr)); else { uint32_t tmp; @@ -421,7 +421,7 @@ int report_attach(struct task *task) if (!server_connected()) return -1; - return server_send_msg(task->is_64bit ? MT_ATTACH64 : MT_ATTACH, task->pid, 0, &state, sizeof(state)); + return server_send_msg(task_is_64bit(task) ? MT_ATTACH64 : MT_ATTACH, task->pid, 0, &state, sizeof(state)); } int report_fork(struct task *task, struct task *ptask) diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c index 396fef4..9efa4b1 100644 --- a/sysdeps/linux-gnu/arm/regs.c +++ b/sysdeps/linux-gnu/arm/regs.c @@ -56,8 +56,10 @@ void set_instruction_pointer(struct task *task, arch_addr_t addr) task->context.regs.ARM_pc = val; - if (ptrace(PTRACE_POKEUSER, task->pid, offsetof(struct pt_regs, ARM_pc), val) == -1) - fprintf(stderr, "pid=%d Couldn't set instruction pointer: %s\n", task->pid, strerror(errno)); + if (ptrace(PTRACE_POKEUSER, task->pid, offsetof(struct pt_regs, ARM_pc), val) == -1) { + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't set instruction pointer: %s\n", task->pid, strerror(errno)); + } } arch_addr_t get_return_addr(struct task *task) @@ -67,8 +69,13 @@ arch_addr_t get_return_addr(struct task *task) int fetch_context(struct task *task) { - if (ptrace(PTRACE_GETREGS, task->pid, 0, &task->context.regs) == -1) - fprintf(stderr, "pid=%d Couldn't fetch register context: %s\n", task->pid, strerror(errno)); + if (ptrace(PTRACE_GETREGS, task->pid, 0, &task->context.regs) == -1) { + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't fetch register context: %s\n", task->pid, strerror(errno)); + + memset(&task->context.regs, 0, sizeof(task->context.regs)); + return -1; + } return 0; } diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c index 8210212..f7088ab 100644 --- a/sysdeps/linux-gnu/ppc/regs.c +++ b/sysdeps/linux-gnu/ppc/regs.c @@ -43,7 +43,7 @@ static inline unsigned long fix_machine(struct task *task, unsigned long val) { - if (!task->is_64bit) + if (!task_is_64bit(task)) val &= 0xffffffff; return val; @@ -70,8 +70,10 @@ void set_instruction_pointer(struct task *task, arch_addr_t addr) task->context.regs.nip = val; - if (ptrace(PTRACE_POKEUSER, task->pid, (sizeof(unsigned long) * PT_NIP), val) == -1) - fprintf(stderr, "pid=%d Couldn't set instruction pointer: %s\n", task->pid, strerror(errno)); + if (ptrace(PTRACE_POKEUSER, task->pid, sizeof(unsigned long) * PT_NIP, val) == -1) { + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't set instruction pointer: %s\n", task->pid, strerror(errno)); + } } arch_addr_t get_return_addr(struct task *task) @@ -84,9 +86,13 @@ arch_addr_t get_return_addr(struct task *task) int fetch_context(struct task *task) { - if (ptrace(PTRACE_GETREGS, task->pid, 0, &task->context.regs) == -1) - fprintf(stderr, "pid=%d Couldn't fetch register context: %s\n", task->pid, strerror(errno)); + if (ptrace(PTRACE_GETREGS, task->pid, 0, &task->context.regs) == -1) { + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't fetch register context: %s\n", task->pid, strerror(errno)); + memset(&task->context.regs, 0, sizeof(task->context.regs)); + return -1; + } return 0; } @@ -146,7 +152,7 @@ unsigned long fetch_param(struct task *task, unsigned int param) break; default: #ifdef __powerpc64__ - if (task->is_64bit) { + if (task_is_64bit(task)) { val = fetch_stack_64(task, param); break; } diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c index 4241127..9e2b2b5 100644 --- a/sysdeps/linux-gnu/proc.c +++ b/sysdeps/linux-gnu/proc.c @@ -191,7 +191,7 @@ int process_tasks(pid_t pid, pid_t ** ret_tasks, size_t *ret_n) * branch. */ static void *select_32_64(struct task *task, void *p32, void *p64) { - if (sizeof(long) == 4 || !task->is_64bit) + if (sizeof(long) == 4 || !task_is_64bit(task)) return p32; else return p64; diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 1c3e3dc..deebd90 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -219,7 +219,6 @@ static void process_event(struct task *task, int status) int stop_signal; struct task *leader = task->leader; struct breakpoint *bp = NULL; - unsigned int i; arch_addr_t ip; assert(leader != NULL); @@ -248,11 +247,11 @@ static void process_event(struct task *task, int status) return; } - if (!leader) - return; - ip = get_instruction_pointer(task); +#if HW_BREAKPOINTS > 0 + unsigned int i; + for(i = 0; i < HW_BREAKPOINTS; ++i) { if (task->hw_bp[i] && task->hw_bp[i]->addr == ip) { bp = task->hw_bp[i]; @@ -261,13 +260,13 @@ static void process_event(struct task *task, int status) } if (bp) { -#if HW_BREAKPOINTS > 0 assert(bp->type != SW_BP); assert(bp->hw_bp_slot == i); -#endif } - else { - bp = breakpoint_find(leader, get_instruction_pointer(task) - DECR_PC_AFTER_BREAK); + else +#endif + { + bp = breakpoint_find(leader, ip - DECR_PC_AFTER_BREAK); if (!bp) return; assert(bp->type == SW_BP); @@ -373,7 +372,8 @@ int trace_attach(struct task *task) assert(task->traced == 0); if (ptrace(PTRACE_ATTACH, task->pid, 0, 0) == -1) { - fprintf(stderr, "PTRACE_ATTACH pid=%d %s\n", task->pid, strerror(errno)); + if (errno != ESRCH) + fprintf(stderr, "PTRACE_ATTACH pid=%d %s\n", task->pid, strerror(errno)); trace_fail_warning(); return -1; } @@ -393,7 +393,8 @@ int trace_set_options(struct task *task) debug(DEBUG_PROCESS, "pid=%d", task->pid); if (ptrace(PTRACE_SETOPTIONS, task->pid, 0, (void *)options) == -1) { - fprintf(stderr, "PTRACE_SETOPTIONS pid=%d %s\n", task->pid, strerror(errno)); + if (errno != ESRCH) + fprintf(stderr, "PTRACE_SETOPTIONS pid=%d %s\n", task->pid, strerror(errno)); return -1; } return 0; @@ -410,7 +411,8 @@ int continue_task(struct task *task, int signum) task->stopped = 0; if (ptrace(PTRACE_CONT, task->pid, 0, fix_signal(task, signum)) == -1) { - fprintf(stderr, "PTRACE_CONT pid=%d %s\n", task->pid, strerror(errno)); + if (errno != ESRCH) + fprintf(stderr, "PTRACE_CONT pid=%d %s\n", task->pid, strerror(errno)); return -1; } return 0; diff --git a/sysdeps/linux-gnu/x86/arch.c b/sysdeps/linux-gnu/x86/arch.c index c3e3cef..a135c25 100644 --- a/sysdeps/linux-gnu/x86/arch.c +++ b/sysdeps/linux-gnu/x86/arch.c @@ -64,7 +64,7 @@ static int set_breakpoint_addr(struct task *task, arch_addr_t addr, unsigned int long ret; #ifdef __x86_64__ - if (!task->is_64bit) + if (!task_is_64bit(task)) addr &= 0xffffffff; #endif diff --git a/sysdeps/linux-gnu/x86/dwarf-x86.c b/sysdeps/linux-gnu/x86/dwarf-x86.c index 50dfb74..3addeb1 100644 --- a/sysdeps/linux-gnu/x86/dwarf-x86.c +++ b/sysdeps/linux-gnu/x86/dwarf-x86.c @@ -131,7 +131,7 @@ static int is_plt_entry(struct dwarf_addr_space *as) int dwarf_arch_init(struct dwarf_addr_space *as) { #ifdef __x86_64__ - if (as->task->is_64bit) { + if (task_is_64bit(as->task)) { as->ip_reg = arch_reg64.ip; as->ret_reg = arch_reg64.sp; as->num_regs = ARRAY_SIZE(dwarf_to_regnum_map64); @@ -151,7 +151,7 @@ int dwarf_arch_init_unwind(struct dwarf_addr_space *as) struct dwarf_cursor *c = &as->cursor; #ifdef __x86_64__ - if (as->task->is_64bit) { + if (task_is_64bit(as->task)) { c->loc[DWARF_X86_RAX] = DWARF_REG_LOC(DWARF_X86_RAX); c->loc[DWARF_X86_RDX] = DWARF_REG_LOC(DWARF_X86_RDX); c->loc[DWARF_X86_RCX] = DWARF_REG_LOC(DWARF_X86_RCX); @@ -204,7 +204,7 @@ int dwarf_arch_step(struct dwarf_addr_space *as) int ret; #ifdef __x86_64__ - arch_reg = as->task->is_64bit ? &arch_reg64 : &arch_reg32; + arch_reg = task_is_64bit(as->task) ? &arch_reg64 : &arch_reg32; #else arch_reg = &arch_reg32; #endif @@ -268,7 +268,7 @@ int dwarf_arch_step(struct dwarf_addr_space *as) int dwarf_arch_map_reg(struct dwarf_addr_space *as, unsigned int reg) { #ifdef __x86_64__ - if (as->task->is_64bit) { + if (task_is_64bit(as->task)) { if (reg >= ARRAY_SIZE(dwarf_to_regnum_map64)) return -DWARF_EBADREG; diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c index d2dd213..ca61570 100644 --- a/sysdeps/linux-gnu/x86/regs.c +++ b/sysdeps/linux-gnu/x86/regs.c @@ -40,7 +40,7 @@ static inline unsigned long fix_machine(struct task *task, unsigned long val) { - if (!task->is_64bit) + if (!task_is_64bit(task)) val &= 0xffffffff; return val; @@ -75,17 +75,18 @@ void set_instruction_pointer(struct task *task, arch_addr_t addr) return; task->context.iregs.rip = val; - if (ptrace(PTRACE_POKEUSER, task->pid, (sizeof(unsigned long) * RIP), val) != -1) + if (ptrace(PTRACE_POKEUSER, task->pid, sizeof(unsigned long) * RIP, val) != -1) return; #else if (task->context.iregs.eip == (long)val) return; task->context.iregs.eip = val; - if (ptrace(PTRACE_POKEUSER, task->pid, (sizeof(unsigned long) * EIP), val) != -1) + if (ptrace(PTRACE_POKEUSER, task->pid, sizeof(unsigned long) * EIP, val) != -1) return; #endif - fprintf(stderr, "pid=%d Couldn't set instruction pointer: %s\n", task->pid, strerror(errno)); + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't set instruction pointer: %s\n", task->pid, strerror(errno)); } arch_addr_t get_return_addr(struct task *task) @@ -96,7 +97,9 @@ arch_addr_t get_return_addr(struct task *task) a = ptrace(PTRACE_PEEKTEXT, task->pid, get_stack_pointer(task), 0); if (a == -1 && errno) { - fprintf(stderr, "pid=%d Couldn't read return value: %s\n", task->pid, strerror(errno)); + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't read return value: %s\n", task->pid, strerror(errno)); + return ARCH_ADDR_T(0); } @@ -109,7 +112,10 @@ arch_addr_t get_return_addr(struct task *task) int fetch_context(struct task *task) { if (ptrace(PTRACE_GETREGS, task->pid, 0, &task->context.iregs) == -1) { - fprintf(stderr, "pid=%d Couldn't fetch register context: %s\n", task->pid, strerror(errno)); + if (errno != ESRCH) + fprintf(stderr, "pid=%d Couldn't fetch register context: %s\n", task->pid, strerror(errno)); + + memset(&task->context.iregs, 0, sizeof(task->context.iregs)); return -1; } @@ -156,7 +162,7 @@ static unsigned long fetch_param_64(struct task *task, unsigned int param) unsigned long fetch_param(struct task *task, unsigned int param) { #ifdef __x86_64__ - if (task->is_64bit) + if (task_is_64bit(task)) return fetch_param_64(task, param); unsigned long sp = fix_machine(task, task->saved_context.iregs.rsp); diff --git a/task.c b/task.c index 7417c94..b23f096 100644 --- a/task.c +++ b/task.c @@ -403,6 +403,7 @@ static void show_attached(struct task *task, void *data) void open_pid(pid_t pid) { struct task *leader; + struct list_head *it; debug(DEBUG_PROCESS, "pid=%d", pid); @@ -441,14 +442,7 @@ void open_pid(pid_t pid) have_all = 1; for (i = 0; i < ntasks; ++i) { if (!pid2task(tasks[i])) { - struct task *task = open_one_pid(tasks[i]); - - if (task) { - if (backtrace_init(task) < 0) { - fprintf(stderr, "Cannot init backtrace for pid %d: %s\n", pid, strerror(errno)); - goto fail1; - } - } + open_one_pid(tasks[i]); have_all = 0; } @@ -464,6 +458,17 @@ void open_pid(pid_t pid) if (leader_setup(leader) < 0) goto fail1; + list_for_each(it, &leader->task_list) { + struct task *task = container_of(it, struct task, task_list); + + task->is_64bit = leader->is_64bit; + + if (backtrace_init(task) < 0) { + fprintf(stderr, "Cannot init backtrace for pid %d: %s\n", pid, strerror(errno)); + goto fail1; + } + }; + if (options.verbose) each_task(leader, &show_attached, NULL); diff --git a/task.h b/task.h index 59810a2..824391d 100644 --- a/task.h +++ b/task.h @@ -138,5 +138,11 @@ void remove_proc(struct task *leader); /* return true if no more task is traced */ int task_list_empty(void); +/* return true if task is 64 bit */ +static inline int task_is_64bit(struct task *task) +{ + return task->is_64bit; +} + #endif