From 92e40e9ec5d833373e3649e4109614fffe16e21b Mon Sep 17 00:00:00 2001 From: Stefani Seibold Date: Sun, 26 Apr 2015 16:08:45 +0200 Subject: [PATCH] fixes --- .gitignore | 32 +++++++ README | 0 backend.h | 4 +- breakpoint.c | 10 ++- client/client.c | 17 ++-- client/process.c | 85 +++++++++--------- client/process.h | 11 +-- client/readline.c | 1 + dwarf.c | 24 +++-- event.c | 24 +++-- event.h | 3 +- memtrace.h | 4 + mtelf.c | 6 +- options.c | 13 ++- options.h | 1 + report.c | 27 ++---- report.h | 2 +- sysdeps/linux-gnu/arm/regs.c | 3 + sysdeps/linux-gnu/backtrace.c | 26 +++--- sysdeps/linux-gnu/ppc/regs.c | 3 + sysdeps/linux-gnu/trace.c | 115 ++++++++++++++---------- sysdeps/linux-gnu/x86/arch.c | 116 +++++++++++++++--------- sysdeps/linux-gnu/x86/dwarf-x86.c | 2 +- sysdeps/linux-gnu/x86/regs.c | 42 ++++----- task.c | 141 +++++++++++++++--------------- task.h | 9 +- trace.c | 3 +- 27 files changed, 435 insertions(+), 289 deletions(-) create mode 100644 .gitignore create mode 100644 README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a14398 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +.* +*.o +*.o.* +*.a +*.s +*.so +*.lo +*.la +*.log +*.lst +*.symtypes +*.elf +*.bin +*.gz +*.bz2 +*.lzma +*.xz +*.lz4 +*.lzo +*.patch +*.gcno +*.orig +*.save +*~ +Makefile +config.h +config.status +stamp-h1 +libtool +docross +doremote +mtrace diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/backend.h b/backend.h index 5620139..61959f4 100644 --- a/backend.h +++ b/backend.h @@ -82,10 +82,10 @@ int handle_singlestep(struct task *task, int (*singlestep)(struct task *task)); arch_addr_t get_return_addr(struct task *task); /* set instruction hw breakpoint */ -int set_hw_bp(struct task *task, unsigned int slot, arch_addr_t addr); +int set_hw_bp(struct task *task, unsigned int n, arch_addr_t addr); /* remove instruction hw breakpoint */ -int reset_hw_bp(struct task *task, unsigned int slot, arch_addr_t addr); +int reset_hw_bp(struct task *task, unsigned int n); /* save the process context (state, registers, stack pointer) */ int fetch_context(struct task *task); diff --git a/breakpoint.c b/breakpoint.c index 9a3e523..2d5905a 100644 --- a/breakpoint.c +++ b/breakpoint.c @@ -153,7 +153,7 @@ static void disable_hw_bp(struct task *task, struct breakpoint *bp) task->hw_bp[slot] = NULL; - if (reset_hw_bp(task, slot, bp->addr) == -1) + if (reset_hw_bp(task, slot) == -1) fatal("reset_hw_bp"); } @@ -272,6 +272,7 @@ void breakpoint_enable(struct task *task, struct breakpoint *bp) debug(DEBUG_PROCESS, "pid=%d, addr=%#lx", task->pid, bp->addr); if (!bp->enabled) { + stop_threads(task); #if HW_BREAKPOINTS > 0 if (bp->type != SW_BP) { if (bp->type == HW_BP) @@ -280,7 +281,6 @@ void breakpoint_enable(struct task *task, struct breakpoint *bp) else #endif { - stop_threads(task); enable_sw_breakpoint(task, bp); } bp->enabled = 1; @@ -292,6 +292,7 @@ void breakpoint_disable(struct task *task, struct breakpoint *bp) debug(DEBUG_PROCESS, "pid=%d, addr=%#lx", task->pid, bp->addr); if (bp->enabled) { + stop_threads(task); #if HW_BREAKPOINTS > 0 if (bp->type != SW_BP) { if (bp->type == HW_BP) @@ -302,7 +303,6 @@ void breakpoint_disable(struct task *task, struct breakpoint *bp) else #endif { - stop_threads(task); disable_sw_breakpoint(task, bp); } bp->enabled = 0; @@ -453,6 +453,8 @@ void breakpoint_clear_all(struct task *leader) void breakpoint_setup(struct task *leader) { + assert(leader->breakpoints == NULL); + leader->breakpoints = dict_init(12401, target_address_hash, target_address_cmp); } @@ -475,7 +477,7 @@ static int clone_single_cb(unsigned long key, const void *value, void *data) new_bp->type = bp->type; new_bp->ext = ext; -#if HW_BREAKPOINT > 0 +#if HW_BREAKPOINTS > 0 if (new_bp->type != SW_BP) { new_bp->hw_bp_slot = bp->hw_bp_slot; diff --git a/client/client.c b/client/client.c index 6c698bd..aee3e4b 100644 --- a/client/client.c +++ b/client/client.c @@ -126,7 +126,7 @@ static void swap_msg(struct mt_msg *mt_msg) mt_msg->tid = bswap_32(mt_msg->tid); } -static int socket_read_msg(struct mt_msg *mt_msg, void **payload, int *swap_endian) +static int socket_read_msg(struct mt_msg *mt_msg, void **payload, unsigned int *swap_endian) { if (TEMP_FAILURE_RETRY(safe_read(client_fd, mt_msg, sizeof(*mt_msg))) <= 0) return FALSE; @@ -156,6 +156,13 @@ static pid_t pid_payload(struct process *process, void *payload) return process->val32(mt_pid->pid); } +static unsigned int attached_payload(void *payload) +{ + struct mt_attached_payload *mt_attached = payload; + + return mt_attached->attached; +} + void client_close(void) { if (client_fd != -1) { @@ -179,7 +186,7 @@ static int client_func(void) struct mt_msg mt_msg; struct process *process; void *payload = NULL; - int swap_endian; + unsigned int swap_endian; if (socket_read_msg(&mt_msg, &payload, &swap_endian) == FALSE) { client_broken(); @@ -204,7 +211,7 @@ static int client_func(void) else { process = client_find_process(mt_msg.pid); if (!process) { - process = process_new(mt_msg.pid, swap_endian, 0, mt_info.do_trace); + process = process_new(mt_msg.pid, swap_endian, mt_info.do_trace); client_add_process(process); } @@ -242,7 +249,7 @@ static int client_func(void) process_duplicate(process, client_find_process(pid_payload(process, payload))); break; case MT_ATTACH: - process_reinit(process, swap_endian, 0); + process_reinit(process, swap_endian, 0, attached_payload(payload)); break; case MT_ATTACH64: if (!IS64BIT) { @@ -250,7 +257,7 @@ static int client_func(void) process_set_status(process, MT_PROCESS_IGNORE); break; } - process_reinit(process, swap_endian, 1); + process_reinit(process, swap_endian, 1, attached_payload(payload)); break; case MT_ABOUT_EXIT: process_about_exit(process); diff --git a/client/process.c b/client/process.c index d1aa07b..73f506f 100644 --- a/client/process.c +++ b/client/process.c @@ -80,6 +80,41 @@ struct map { int ignore; }; +static const char *str_operation(enum mt_operation operation) +{ + switch(operation) { + case MT_MALLOC: + return "malloc"; + case MT_REALLOC_ENTER: + return "realloc enter"; + case MT_REALLOC: + return "realloc"; + case MT_REALLOC_FAILED: + return "realloc failed"; + case MT_MEMALIGN: + return "memalign"; + case MT_POSIX_MEMALIGN: + return "posix_memalign"; + case MT_ALIGNED_ALLOC: + return "aligned_alloc"; + case MT_VALLOC: + return "valloc"; + case MT_PVALLOC: + return "pvalloc"; + case MT_MMAP: + return "mmap"; + case MT_MMAP64: + return "mmap64"; + case MT_FREE: + return "free"; + case MT_MUNMAP: + return "munmap"; + default: + break; + } + return "unknow operation"; +} + static unsigned long get_uint64(void *p) { uint64_t v; @@ -624,7 +659,7 @@ void process_del_map(struct process *process, void *payload, uint32_t payload_le fatal("process_del_map"); } -static void process_init(struct process *process, int swap_endian, int is_64bit) +static void process_init(struct process *process, unsigned int swap_endian, unsigned int is_64bit, unsigned int attached) { if (is_64bit) { process->ptr_size = sizeof(uint64_t); @@ -642,6 +677,7 @@ static void process_init(struct process *process, int swap_endian, int is_64bit) process->val64 = swap_endian ? val64_swap : val64; process->is_64bit = is_64bit; + process->attached = attached; process->swap_endian = swap_endian; process->status = MT_PROCESS_RUNNING; process->filename = NULL; @@ -708,7 +744,7 @@ void process_duplicate(struct process *process, struct process *copy) struct list_head *it; process_reset(process); - process_init(process, copy->swap_endian, copy->is_64bit); + process_init(process, copy->swap_endian, copy->is_64bit, copy->attached); if (!copy) return; @@ -800,41 +836,6 @@ static int sort_total(const struct rb_stack **p, const struct rb_stack **q) return sort_allocations(p, q); } -static const char *str_operation(enum mt_operation operation) -{ - switch(operation) { - case MT_MALLOC: - return "malloc"; - case MT_REALLOC_ENTER: - return "realloc enter"; - case MT_REALLOC: - return "realloc"; - case MT_REALLOC_FAILED: - return "realloc failed"; - case MT_MEMALIGN: - return "memalign"; - case MT_POSIX_MEMALIGN: - return "posix_memalign"; - case MT_ALIGNED_ALLOC: - return "aligned_alloc"; - case MT_VALLOC: - return "valloc"; - case MT_PVALLOC: - return "pvalloc"; - case MT_MMAP: - return "mmap"; - case MT_MMAP64: - return "mmap64"; - case MT_FREE: - return "free"; - case MT_MUNMAP: - return "munmap"; - default: - break; - } - return "unknow operation"; -} - static void _process_dump(struct process *process, int (*sortby)(const struct rb_stack **, const struct rb_stack **), int (*skipfunc)(struct rb_stack *), FILE *file) { struct rb_stack **arr; @@ -1146,7 +1147,7 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload) process_rb_delete_block(process, block); } else { - if (!options.client || options.wait) { + if (!process->attached) { fprintf(stderr, ">>> block %#lx not found (pid=%d, tid=%d)\n", ptr, process->pid, mt_msg->tid); abort(); } @@ -1209,13 +1210,13 @@ void process_alloc(struct process *process, struct mt_msg *mt_msg, void *payload stack->tsc = process->tsc++; } -void process_reinit(struct process *process, int swap_endian, int is_64bit) +void process_reinit(struct process *process, unsigned int swap_endian, unsigned int is_64bit, unsigned int attached) { process_reset(process); - process_init(process, swap_endian, is_64bit); + process_init(process, swap_endian, is_64bit, attached); } -struct process *process_new(pid_t pid, int swap_endian, int is_64bit, int tracing) +struct process *process_new(pid_t pid, unsigned int swap_endian, unsigned int tracing) { struct process *process = malloc(sizeof(*process)); @@ -1227,7 +1228,7 @@ struct process *process_new(pid_t pid, int swap_endian, int is_64bit, int tracin process->stack_table = RB_ROOT; INIT_LIST_HEAD(&process->map_list); - process_init(process, swap_endian, is_64bit); + process_init(process, swap_endian, 0, 0); return process; } diff --git a/client/process.h b/client/process.h index f3b829a..b375342 100644 --- a/client/process.h +++ b/client/process.h @@ -50,7 +50,6 @@ struct lib { struct process { enum process_status status; pid_t pid; - int tracing; char *filename; unsigned long bytes_used; unsigned long n_allocations; @@ -62,8 +61,10 @@ struct process { struct rb_root stack_table; struct list_head map_list; unsigned long long tsc; - int swap_endian; - int is_64bit; + unsigned int tracing:1; + unsigned int swap_endian:1; + unsigned int is_64bit:1; + unsigned int attached:1; unsigned long (*get_ulong)(void *); void (*put_ulong)(void *, unsigned long); uint16_t (*val16)(uint16_t val); @@ -72,9 +73,9 @@ struct process { uint8_t ptr_size; }; -struct process *process_new(pid_t pid, int swap_endian, int is_64bit, int tracing); +struct process *process_new(pid_t pid, unsigned int swap_endian, unsigned int tracing); void process_reset_allocations(struct process *process); -void process_reinit(struct process *process, int swap_endian, int is_64bit); +void process_reinit(struct process *process, unsigned int swap_endian, unsigned int is_64bit, unsigned int attached); void process_set_clone(struct process *process, struct process *clone); struct process *process_clone_of(struct process *process); void process_delete(struct process *process); diff --git a/client/readline.c b/client/readline.c index 0e82079..116ac22 100644 --- a/client/readline.c +++ b/client/readline.c @@ -781,6 +781,7 @@ static int do_start(struct cmd_opt *cmd, int argc, const char *argv[]) process_reset_allocations(process); process->tracing = 1; + process->attached = 1; client_send_msg(process, MT_START, NULL, 0); diff --git a/dwarf.c b/dwarf.c index c4fe413..5451483 100644 --- a/dwarf.c +++ b/dwarf.c @@ -403,14 +403,16 @@ static inline int dwarf_readw(struct dwarf_addr_space *as, arch_addr_t *addr, ar ret = dwarf_read64(as, addr, &u64); - *valp = u64; + if (valp) + *valp = u64; } else { uint32_t u32; ret = dwarf_read32(as, addr, &u32); - *valp = u32; + if (valp) + *valp = u32; } return ret; } @@ -430,7 +432,8 @@ static int dwarf_read_uleb128(struct dwarf_addr_space *as, arch_addr_t *addr, ar } while (byte & 0x80); - *valp = val; + if (valp) + *valp = val; return 0; } @@ -453,7 +456,8 @@ static int dwarf_read_sleb128(struct dwarf_addr_space *as, arch_addr_t *addr, ar /* sign-extend negative value */ val |= ((arch_addr_t) -1) << shift; - *valp = val; + if (valp) + *valp = val; return 0; } @@ -476,6 +480,16 @@ static int dwarf_read_encoded_pointer(struct dwarf_addr_space *as, int local, arch_addr_t addr; } tmp; +#ifdef DEBUG + struct dwarf_cursor *c = &as->cursor; + struct library *lib = c->lib; + + if (*addr < ARCH_ADDR_T(lib->image_addr)) + fatal("invalid access mem: addr %#lx < %p", *addr, lib->image_addr); + if (*addr >= ARCH_ADDR_T(lib->image_addr + lib->load_size)) + fatal("invalid access mem: addr %#lx >= %p", *addr, lib->image_addr + lib->load_size); +#endif + memset(&tmp, 0, sizeof(tmp)); if (valp) @@ -1522,7 +1536,7 @@ do { \ return ret; tmp2 = u32; if (operand1 == 3) { -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER == __ORDER_LITTLE_ENDIAN tmp2 &= 0xffffff; #else tmp2 >>= 8; diff --git a/event.c b/event.c index 9c00481..a562cc3 100644 --- a/event.c +++ b/event.c @@ -115,7 +115,7 @@ static void show_clone(struct task *task, enum event_type type) static struct task *handle_clone(struct task *task, enum event_type type) { - struct task *newproc; + struct task *newtask; int newpid = task->event.e_un.newpid; debug(DEBUG_FUNCTION, "pid=%d, newpid=%d", task->pid, newpid); @@ -125,19 +125,27 @@ static struct task *handle_clone(struct task *task, enum event_type type) continue_task(task, 0); - newproc = task_clone(task, newpid); - if (!newproc) + newtask = pid2task(newpid); + if (!newtask) goto fail; - if (newproc->leader == newproc) { + if (newtask->leader == newtask) { + if (task_fork(task, newtask) < 0) + goto fail; + if (!options.follow) { - remove_proc(newproc); + remove_proc(newtask); return task; } - report_fork(newproc, task->pid); + + report_fork(newtask, task); + } + else { + if (task_clone(task, newtask) < 0) + goto fail; } - continue_task(newproc, newproc->event.e_un.signum); + continue_task(newtask, newtask->event.e_un.signum); return task; fail: @@ -272,8 +280,8 @@ static struct task *handle_breakpoint(struct task *task) if (task->breakpoint) { task->libsym = libsym; task->breakpoint->on_hit = handle_call_after; + enable_scratch_hw_bp(task, task->breakpoint); } - enable_scratch_hw_bp(task, task->breakpoint); } if (libsym->func->report_in) diff --git a/event.h b/event.h index 964bff1..59236a8 100644 --- a/event.h +++ b/event.h @@ -36,8 +36,7 @@ enum event_type { EVENT_CLONE, EVENT_VFORK, EVENT_EXEC, - EVENT_BREAKPOINT, - EVENT_MAX + EVENT_BREAKPOINT }; struct event { diff --git a/memtrace.h b/memtrace.h index cbed0e3..afe13ab 100644 --- a/memtrace.h +++ b/memtrace.h @@ -52,6 +52,10 @@ struct mt_msg { uint32_t payload_len; }; +struct mt_attached_payload { + uint8_t attached; +}; + struct mt_alloc_payload_64 { uint64_t ptr; uint64_t size; diff --git a/mtelf.c b/mtelf.c index a6607b8..789dbd1 100644 --- a/mtelf.c +++ b/mtelf.c @@ -58,7 +58,11 @@ static int open_elf(struct mt_elf *mte, const char *filename) { mte->filename = filename; - mte->fd = open(filename, O_RDONLY); + if (options.cwd != -1) + mte->fd = openat(options.cwd, filename, O_RDONLY); + else + mte->fd = open(filename, O_RDONLY); + if (mte->fd == -1) return 1; diff --git a/options.c b/options.c index 68563c2..f4b2822 100644 --- a/options.c +++ b/options.c @@ -74,6 +74,7 @@ static void usage(void) " -a, --autoscan scan memory on exit of a traced program\n" " -b, --binpath=path binary search path (may be repeated)\n" " -c, --client connect to socket (path or address)\n" + " -C, --cwd set current working directory\n" #ifdef DEBUG " -D, --debug=MASK enable debugging (see -Dh or --debug=help)\n" " -Dh, --debug=help show help on debugging\n" @@ -184,6 +185,7 @@ char **process_options(int argc, char **argv) options.listen = NULL; options.user = NULL; options.command = NULL; + options.cwd = -1; options.opt_p = NULL; options.opt_F = NULL; options.opt_b = NULL; @@ -198,6 +200,7 @@ char **process_options(int argc, char **argv) { "binpath", 1, 0, 'b' }, { "client", 1, 0, 'c' }, { "config", 1, 0, 'F' }, + { "cwd", 1, 0, 'C' }, { "debug", 1, 0, 'D' }, { "depth", 1, 0, 'd' }, { "help", 0, 0, 'h' }, @@ -218,7 +221,7 @@ char **process_options(int argc, char **argv) }; c = getopt_long(argc, argv, "+aefhisVvw" - "b:c:D:F:l:o:p:P:u:d:S:", + "b:c:C:D:F:l:o:p:P:u:d:S:", long_options, &option_index); @@ -251,6 +254,14 @@ char **process_options(int argc, char **argv) case 'c': options.client = optarg; break; + case 'C': + options.cwd = open(optarg, O_RDONLY|O_DIRECTORY); + + if (options.cwd == -1) { + fprintf(stderr, "%s: `%s' %s\n", progname, optarg, strerror(errno)); + exit(1); + } + break; case 'D': { #ifdef DEBUG diff --git a/options.h b/options.h index 22b63f7..41fe96e 100644 --- a/options.h +++ b/options.h @@ -72,6 +72,7 @@ struct options_t { int wait; /* wait for client connection */ char *port; /* socket port */ char *command; /* command string */ + int cwd; /* current working directory handle */ struct opt_p_t *opt_p; /* attach to process with a given pid */ struct opt_F_t *opt_F; /* alternate configuration file(s) */ struct opt_b_t *opt_b; /* binary search path(s) */ diff --git a/report.c b/report.c index 93ed867..0aafbfb 100644 --- a/report.c +++ b/report.c @@ -307,20 +307,6 @@ static int _report_pvalloc(struct task *task, struct library_symbol *libsym) return report_alloc(task, MT_PVALLOC, ret, len, options.bt_depth); } -#if 1 -static int mt_test(struct task *task, struct library_symbol *libsym) -{ - unsigned int i; - - for(i = 0; i < 13; ++i) { - unsigned long val = fetch_param(task, i); - - fprintf(stderr, "%s:%d %d\n", __FUNCTION__, __LINE__, (int32_t)val); - } - return 0; -} -#endif - static const struct function flist[] = { { "malloc", 2, NULL, _report_malloc }, { "free", 3, report_free, NULL }, @@ -337,16 +323,13 @@ static const struct function flist[] = { #if 0 { "cfree", 14, report_free, NULL }, #endif -#if 1 - { "mt_test", 15, mt_test, NULL }, -#endif }; const struct function *flist_matches_symbol(const char *sym_name) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(flist); ++i) { + for(i = 0; i < ARRAY_SIZE(flist); ++i) { if (!strcmp(sym_name, flist[i].name)) return &flist[i]; } @@ -417,15 +400,17 @@ int report_scan(pid_t pid, const void *data, unsigned int data_len) int report_attach(struct task *task) { + struct mt_attached_payload state = { .attached = task->attached }; + if (!server_connected()) return -1; - return server_send_msg(task->is_64bit ? MT_ATTACH64 : MT_ATTACH, task->pid, 0, NULL, 0); + return server_send_msg(task->is_64bit ? MT_ATTACH64 : MT_ATTACH, task->pid, 0, &state, sizeof(state)); } -int report_fork(struct task *task, pid_t ppid) +int report_fork(struct task *task, struct task *ptask) { - struct mt_pid_payload fork_pid = { .pid = ppid }; + struct mt_pid_payload fork_pid = { .pid = ptask->leader->pid }; if (!server_connected()) return -1; diff --git a/report.h b/report.h index 89868cd..473c0b6 100644 --- a/report.h +++ b/report.h @@ -40,7 +40,7 @@ int report_del_map(struct task *task, struct library *lib); int report_info(int do_trace); int report_scan(pid_t pid, const void *data, unsigned int data_len); int report_attach(struct task *task); -int report_fork(struct task *task, pid_t pid); +int report_fork(struct task *task, struct task *ptask); int report_exit(struct task *task); int report_about_exit(struct task *task); int report_nofollow(struct task *task); diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c index c1a0702..396fef4 100644 --- a/sysdeps/linux-gnu/arm/regs.c +++ b/sysdeps/linux-gnu/arm/regs.c @@ -51,6 +51,9 @@ void set_instruction_pointer(struct task *task, arch_addr_t addr) { unsigned long val = (unsigned long)addr; + if (task->context.regs.ARM_pc == (long)val) + return; + task->context.regs.ARM_pc = val; if (ptrace(PTRACE_POKEUSER, task->pid, offsetof(struct pt_regs, ARM_pc), val) == -1) diff --git a/sysdeps/linux-gnu/backtrace.c b/sysdeps/linux-gnu/backtrace.c index 9d3beaa..af889c7 100644 --- a/sysdeps/linux-gnu/backtrace.c +++ b/sysdeps/linux-gnu/backtrace.c @@ -22,10 +22,13 @@ #include "dwarf.h" #include "task.h" +#include #include int backtrace_init(struct task *task) { + assert(task->backtrace == NULL); + task->backtrace = dwarf_init(task); return task->backtrace != NULL; @@ -33,28 +36,31 @@ int backtrace_init(struct task *task) void backtrace_destroy(struct task *task) { - if (task->backtrace) + if (task->backtrace) { dwarf_destroy(task->backtrace); + + task->backtrace = NULL; + } } int backtrace_init_unwind(struct task *task) { - if (task->backtrace) - return dwarf_init_unwind(task->backtrace); - return -1; + assert(task->backtrace); + + return dwarf_init_unwind(task->backtrace); } unsigned long backtrace_get_ip(struct task *task) { - if (task->backtrace) - return dwarf_get_ip(task->backtrace); - return 0; + assert(task->backtrace); + + return dwarf_get_ip(task->backtrace); } int backtrace_step(struct task *task) { - if (task->backtrace) - return dwarf_step(task->backtrace); - return -1; + assert(task->backtrace); + + return dwarf_step(task->backtrace); } diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c index 09c629e..8210212 100644 --- a/sysdeps/linux-gnu/ppc/regs.c +++ b/sysdeps/linux-gnu/ppc/regs.c @@ -65,6 +65,9 @@ void set_instruction_pointer(struct task *task, arch_addr_t addr) #ifdef __powerpc64__ val = fix_machine(task, val); #endif + if (task->context.regs.nip == val) + return; + task->context.regs.nip = val; if (ptrace(PTRACE_POKEUSER, task->pid, (sizeof(unsigned long) * PT_NIP), val) == -1) diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 6970320..b42e126 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -67,6 +67,12 @@ static int trace_setup(struct task *task, int status, int signum) { int stop_signal; + task->traced = 1; + task->stopped = 1; + task->leader->threads_stopped++; + task->event.type = EVENT_SIGNAL; + task->event.e_un.signum = 0; + if (!WIFSTOPPED(status)) { fprintf(stderr, "%s pid=%d not stopped\n", __FUNCTION__, task->pid); return -1; @@ -74,20 +80,12 @@ static int trace_setup(struct task *task, int status, int signum) stop_signal = WSTOPSIG(status); - if (stop_signal == SIGSTOP) - task->was_stopped = 1; - else - if (stop_signal == signum) - stop_signal = 0; + if (stop_signal != signum) { + task->event.e_un.signum = stop_signal; - task->event.type = EVENT_SIGNAL; - task->event.e_un.signum = stop_signal; - - task->traced = 1; - task->stopped = 1; - - if (task->leader) - task->leader->threads_stopped++; + fprintf(stderr, "%s pid=%d unexpected trace signal (got:%d expected:%d)\n", __FUNCTION__, task->pid, stop_signal, signum); + return -1; + } return 0; } @@ -109,7 +107,12 @@ static int _trace_wait(struct task *task, int signum) int trace_wait(struct task *task) { - return _trace_wait(task, SIGTRAP); + if (_trace_wait(task, SIGTRAP)) + return -1; + + queue_event(task); + + return 0; } static int child_event(struct task *task, enum event_type ev) @@ -126,12 +129,18 @@ static int child_event(struct task *task, enum event_type ev) if (!pid2task(pid)) { struct task *child = task_new(pid, 0); - if (!child || _trace_wait(child, SIGTRAP)) + if (!child) return -1; + + if (_trace_wait(child, SIGSTOP)) { + remove_task(child); + return -1; + } } task->event.e_un.newpid = pid; task->event.type = ev; + return 0; } @@ -213,10 +222,11 @@ static void process_event(struct task *task, int status) int i; arch_addr_t ip; + assert(leader != NULL); + task->stopped = 1; - if (leader) - leader->threads_stopped++; + leader->threads_stopped++; stop_signal = _process_event(task, status); @@ -267,7 +277,7 @@ static void process_event(struct task *task, int status) #if 1 assert(bp->enabled); #else - if (bp->enabled) + if (!bp->enabled) return; #endif @@ -318,31 +328,49 @@ static inline int fix_signal(struct task *task, int signum) int untrace_task(struct task *task, int signum) { + int ret = 0; + + assert(task->leader != NULL); + debug(DEBUG_PROCESS, "pid=%d", task->pid); - if (!task->traced) + if (!task->stopped) return 0; - task->traced = 0; - task->stopped = 0; - - if (task->leader) - task->leader->threads_stopped--; - - if (ptrace(PTRACE_DETACH, task->pid, 0, fix_signal(task, signum)) == -1) { + if (ptrace(PTRACE_SETOPTIONS, task->pid, 0, (void *)0) == -1) { if (errno != ESRCH) - fprintf(stderr, "PTRACE_DETACH pid=%d %s\n", task->pid, strerror(errno)); - return -1; + fprintf(stderr, "PTRACE_SETOPTIONS pid=%d %s\n", task->pid, strerror(errno)); + ret = -1; + goto skip; + } - return 0; + + signum = fix_signal(task, signum); + + if (ptrace(PTRACE_DETACH, task->pid, 0, signum) == -1) { + if (task->traced) { + if (errno != ESRCH) + fprintf(stderr, "PTRACE_DETACH pid=%d %s\n", task->pid, strerror(errno)); + ret = -1; + goto skip; + } + } + + task_kill(task, SIGCONT); +skip: + task->leader->threads_stopped--; + task->stopped = 0; + task->was_stopped = 0; + task->traced = 0; + + return ret; } int trace_attach(struct task *task) { debug(DEBUG_PROCESS, "pid=%d", task->pid); - if (task->traced) - return 0; + 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)); @@ -353,6 +381,8 @@ int trace_attach(struct task *task) if (_trace_wait(task, SIGSTOP)) return -1; + queue_event(task); + return 0; } @@ -373,8 +403,10 @@ int continue_task(struct task *task, int signum) { debug(DEBUG_PROCESS, "pid=%d", task->pid); - if (task->leader) - task->leader->threads_stopped--; + assert(task->leader != NULL); + assert(task->stopped); + + task->leader->threads_stopped--; task->stopped = 0; if (ptrace(PTRACE_CONT, task->pid, 0, fix_signal(task, signum)) == -1) { @@ -388,16 +420,7 @@ static void do_stop_cb(struct task *task, void *data) { if (task->stopped) return; -#if 0 - int status; - int pid = TEMP_FAILURE_RETRY(waitpid(task->pid, &status, __WALL | WNOHANG)); - if (pid == task->pid) { - process_event(task, status); - queue_event(task); - return; - } -#endif task->was_stopped = 1; task_kill(task, SIGSTOP); @@ -407,8 +430,7 @@ void stop_threads(struct task *task) { struct task *leader = task->leader; - if (!leader) - return; + assert(task->leader != NULL); if (leader->threads != leader->threads_stopped) { each_task(leader, &do_stop_cb, NULL); @@ -440,13 +462,11 @@ int handle_singlestep(struct task *task, int (*singlestep)(struct task *task)) if (stop_signal == SIGTRAP) return 0; /* check if was a real breakpoint code there */ - if (!stop_signal) { queue_event(task); return 0; } - if (fix_signal(task, stop_signal) > 0) { queue_event(task); return 1; @@ -503,7 +523,8 @@ struct task *wait_event(void) task = task_new(pid, 0); if (task) - trace_setup(task, status, SIGTRAP); + trace_setup(task, status, SIGSTOP); + return NULL; } diff --git a/sysdeps/linux-gnu/x86/arch.c b/sysdeps/linux-gnu/x86/arch.c index 8d172c8..af0d474 100644 --- a/sysdeps/linux-gnu/x86/arch.c +++ b/sysdeps/linux-gnu/x86/arch.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -40,6 +41,11 @@ static int set_breakpoint_addr(struct task *task, arch_addr_t addr, int n) { int ret; +#ifdef __x86_64__ + if (!task->is_64bit) + addr &= 0xffffffff; +#endif + ret = ptrace(PTRACE_POKEUSER, task->pid, offsetof(struct user, u_debugreg[n]), addr); if (ret) { if (errno != ESRCH) { @@ -50,56 +56,63 @@ static int set_breakpoint_addr(struct task *task, arch_addr_t addr, int n) return 0; } -static int toggle_breakpoint(struct task *task, int n, int type, int len, int local, int global, int set) +static int set_breakpoint_mode(struct task *task, int n, int type, int len, int local, int global) { long ret; - int xtype, xlen; - unsigned long dr7, vdr7; + uint32_t mode; + uint32_t dr7, mask; - switch (type) { + mask = (0b1111 << (16 + 4 * n)) | (0b11 << (2 * n)); + + switch(type) { case BP_X: - xtype = 0; + mode = 0b0000; break; case BP_W: - xtype = 1; + mode = 0b0001; break; case BP_RW: - xtype = 3; + mode = 0b0011; break; + default: + fprintf(stderr, "invalid hw breakpoint type\n"); + return -1; } - switch (len) { + switch(len) { case 1: - xlen = 0; + mode |= 0b0000; break; case 2: - xlen = 4; + mode |= 0b0100; break; case 4: - xlen = 0xc; + mode |= 0b1100; break; case 8: - xlen = 8; + mode |= 0b1000; break; } - vdr7 = (xlen | xtype) << 16; - vdr7 <<= 4 * n; + dr7 = task->arch.dr7 & ~mask; + dr7 |= mode << (16 + 4 * n); + if (local) { - vdr7 |= 1 << (2 * n); - vdr7 |= 1 << 8; + dr7 |= 0b01 << (2 * n); + dr7 |= 1 << 8; } - if (global) { - vdr7 |= 2 << (2 * n); - vdr7 |= 1 << 9; - } - - dr7 = task->arch.dr7; - if (set) - dr7 |= vdr7; else - dr7 &= ~vdr7; + if (!(dr7 & 0b01010101)) + dr7 &= ~(1 << 8); + + if (global) { + dr7 |= 0b10 << (2 * n); + dr7 |= 1 << 9; + } + else + if (!(dr7 & 0b10101010)) + dr7 &= ~(1 << 9); if (dr7 != task->arch.dr7) { task->arch.dr7 = dr7; @@ -115,31 +128,50 @@ static int toggle_breakpoint(struct task *task, int n, int type, int len, int lo return 0; } -int set_hw_bp(struct task *task, unsigned int slot, arch_addr_t addr) +int set_hw_bp(struct task *task, unsigned int n, arch_addr_t addr) { - if (set_breakpoint_addr(task, addr, slot) == -1) + if (reset_hw_bp(task, n) == -1) return -1; - return toggle_breakpoint(task, - slot, /* n */ + if (set_breakpoint_addr(task, addr, n) == -1) + return -1; + + return set_breakpoint_mode(task, + n, /* n */ BP_X, /* type */ 1, /* len */ 1, /* local */ - 0, /* global */ - 1 /* set */ + 0 /* global */ ); } -int reset_hw_bp(struct task *task, unsigned int slot, arch_addr_t addr) +int reset_hw_bp(struct task *task, unsigned int n) { - return toggle_breakpoint(task, - slot, /* n */ - BP_X, /* type */ - 1, /* len */ - 1, /* local */ - 0, /* global */ - 0 /* set */ - ); + long ret; + uint32_t dr7, mask; + + mask = (0b1111 << (16 + 4 * n)) | (0b11 << (2 * n)); + + dr7 = task->arch.dr7 & ~mask; + + if (!(dr7 & 0b01010101)) + dr7 &= ~(1 << 8); + + if (!(dr7 & 0b10101010)) + dr7 &= ~(1 << 9); + + if (dr7 != task->arch.dr7) { + task->arch.dr7 = dr7; + + ret = ptrace(PTRACE_POKEUSER, task->pid, offsetof(struct user, u_debugreg[7]), task->arch.dr7); + if (ret) { + if (errno != ESRCH) { + fprintf(stderr, "PTRACE_POKEUSER u_debugreg[7] pid=%d %s\n", task->pid, strerror(errno)); + return -1; + } + } + } + return 0; } int is_64bit(struct mt_elf *mte) @@ -150,6 +182,7 @@ int is_64bit(struct mt_elf *mte) int arch_task_init(struct task *task) { long ret; + int i; ret = ptrace(PTRACE_PEEKUSER, task->pid, offsetof(struct user, u_debugreg[7]), 0); if (ret == -1 && errno) { @@ -162,6 +195,9 @@ int arch_task_init(struct task *task) task->arch.dr7 = ret; + for(i = 0; i < HW_BREAKPOINTS; ++i) + reset_hw_bp(task, i); + return 0; } diff --git a/sysdeps/linux-gnu/x86/dwarf-x86.c b/sysdeps/linux-gnu/x86/dwarf-x86.c index 4440101..50dfb74 100644 --- a/sysdeps/linux-gnu/x86/dwarf-x86.c +++ b/sysdeps/linux-gnu/x86/dwarf-x86.c @@ -34,7 +34,7 @@ static const uint8_t dwarf_to_regnum_map32[] = { [DWARF_X86_ECX] = ECX, [DWARF_X86_EDX] = EDX, [DWARF_X86_EBX] = EBX, - [DWARF_X86_ESP] = ESP, + [DWARF_X86_ESP] = UESP, [DWARF_X86_EBP] = EBP, [DWARF_X86_ESI] = ESI, [DWARF_X86_EDI] = EDI, diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c index 4214b42..d2dd213 100644 --- a/sysdeps/linux-gnu/x86/regs.c +++ b/sysdeps/linux-gnu/x86/regs.c @@ -1,10 +1,6 @@ /* * This file is part of mtrace. * Copyright (C) 2015 Stefani Seibold - * This file is based on the ltrace source - * Copyright (C) 2012 Petr Machata, Red Hat Inc. - * Copyright (C) 1998,2002,2004,2008,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 @@ -75,10 +71,16 @@ void set_instruction_pointer(struct task *task, arch_addr_t addr) #ifdef __x86_64__ val = fix_machine(task, val); + if (task->context.iregs.rip == val) + return; + task->context.iregs.rip = val; 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) return; @@ -271,52 +273,52 @@ unsigned long fetch_reg(struct task *task, unsigned int reg) #else switch(reg) { case EBX: - val = task->context.iregs.bx; + val = task->context.iregs.ebx; break; case ECX: - val = task->context.iregs.cx; + val = task->context.iregs.ecx; break; case ESI: - val = task->context.iregs.si; + val = task->context.iregs.esi; break; case EDI: - val = task->context.iregs.di; + val = task->context.iregs.edi; break; case EBP: - val = task->context.iregs.bp; + val = task->context.iregs.ebp; break; case EAX: - val = task->context.iregs.cx; + val = task->context.iregs.ecx; break; case DS: - val = task->context.iregs.ds; + val = task->context.iregs.xds; break; case ES: - val = task->context.iregs.es; + val = task->context.iregs.xes; break; case FS: - val = task->context.iregs.fs; + val = task->context.iregs.xfs; break; case GS: - val = task->context.iregs.gs; + val = task->context.iregs.xgs; break; case ORIG_EAX: - val = task->context.iregs.orig_ax; + val = task->context.iregs.orig_eax; break; case EIP: - val = task->context.iregs.ip; + val = task->context.iregs.eip; break; case CS: - val = task->context.iregs.cs; + val = task->context.iregs.xcs; break; case EFL: - val = task->context.iregs.flags; + val = task->context.iregs.eflags; break; case UESP: - val = task->context.iregs.sp; + val = task->context.iregs.esp; break; case SS: - val = task->context.iregs.ss; + val = task->context.iregs.xss; break; default: abort(); diff --git a/task.c b/task.c index 26001fb..8cdb716 100644 --- a/task.c +++ b/task.c @@ -132,14 +132,16 @@ static int leader_setup(struct task *leader) } -static int task_bare_init(struct task *task) +static int task_init(struct task *task, int attached) { pid_t tgid; /* Add process so that we know who the leader is. */ tgid = process_leader(task->pid); - if (!tgid) + if (!tgid){ + fprintf(stderr, "%s no tgid for pid=%d\n", __FUNCTION__, task->pid); return -1; + } if (tgid == task->pid) { task->leader = task; @@ -152,8 +154,10 @@ static int task_bare_init(struct task *task) else { task->leader = pid2task(tgid); - if (!task->leader) + if (!task->leader) { + fprintf(stderr, "%s no leader for tgpid=%d\n", __FUNCTION__, tgid); return -1; + } task->leader->threads++; task->breakpoints = NULL; @@ -162,13 +166,20 @@ static int task_bare_init(struct task *task) list_add_tail(&task->task_list, &task->leader->task_list); } + task->attached = attached; + + if (arch_task_init(task) < 0) + return -1; + + if (os_task_init(task) < 0) + return -1; + return 0; } static void leader_cleanup(struct task *task) { - if (!task->leader) - return; + assert(task->leader); task->leader->threads--; @@ -188,9 +199,11 @@ static void leader_cleanup(struct task *task) static void task_destroy(struct task *task) { breakpoint_hw_destroy(task); - detach_task(task); - leader_cleanup(task); backtrace_destroy(task); + arch_task_destroy(task); + os_task_destroy(task); + leader_cleanup(task); + detach_task(task); list_del(&task->task_list); rb_erase(&task->pid_node, &pid_tree); free(task); @@ -215,22 +228,16 @@ struct task *task_new(pid_t pid, int traced) library_setup(task); - if (arch_task_init(task) < 0) + if (task_init(task, 1) < 0) goto fail1; - if (os_task_init(task) < 0) - goto fail2; - init_event(task); insert_pid(task); return task; -fail2: - arch_task_destroy(task); fail1: task_destroy(task); - free(task); return NULL; } @@ -240,11 +247,13 @@ int process_exec(struct task *task) task->threads_stopped--; + breakpoint_hw_destroy(task); + backtrace_destroy(task); + os_task_destroy(task); + arch_task_destroy(task); leader_cleanup(task); - backtrace_destroy(task); - - if (task_bare_init(task) < 0) + if (task_init(task, 0) < 0) return -1; assert(task->leader == task); @@ -282,9 +291,6 @@ struct task *task_create(const char *command, char **argv) if (!task) goto fail2; - if (task_bare_init(task) < 0) - goto fail2; - if (trace_wait(task)) goto fail1; @@ -294,8 +300,6 @@ struct task *task_create(const char *command, char **argv) if (leader_setup(task) < 0) goto fail1; - queue_event(task); - return task; fail2: fprintf(stderr, "failed to initialize process %d\n", pid); @@ -306,63 +310,67 @@ fail1: return NULL; } -struct task *task_clone(struct task *task, pid_t pid) +int task_clone(struct task *task, struct task *newtask) { - struct task *retp; + assert(newtask->leader != newtask); + assert(newtask->event.type == EVENT_SIGNAL); + assert(newtask->event.e_un.signum == 0); + assert(newtask->traced); + assert(newtask->stopped); + assert(newtask->backtrace == NULL); - assert(task == task->leader); + if (backtrace_init(newtask) < 0) + goto fail; - retp = pid2task(pid); - if (!retp) - goto fail1; + breakpoint_hw_clone(newtask); - assert(!retp->leader); + return 0; +fail: + fprintf(stderr, "failed to clone process %d->%d : %s\n", task->pid, newtask->pid, strerror(errno)); + task_destroy(newtask); - if (task_bare_init(retp) < 0) - goto fail2; + return -1; +} - retp->leader->threads_stopped++; +int task_fork(struct task *task, struct task *newtask) +{ + struct task *leader = task->leader; - if (backtrace_init(retp) < 0) - goto fail2; + assert(newtask->leader == newtask); + assert(newtask->event.type == EVENT_SIGNAL); + assert(newtask->event.e_un.signum == 0); + assert(newtask->traced); + assert(newtask->stopped); + assert(newtask->backtrace == NULL); - /* For non-leader tasks, that's all we need to do. */ - if (retp->leader != retp) { - breakpoint_hw_clone(retp); + if (backtrace_init(newtask) < 0) + goto fail; - return retp; - } + if (library_clone_all(newtask, leader)) + goto fail; - if (library_clone_all(retp, task)) - goto fail2; + if (breakpoint_clone_all(newtask, leader)) + goto fail; - if (breakpoint_clone_all(retp, task)) - goto fail2; - - retp->libsym = task->libsym; - retp->context = task->context; - retp->breakpoint = task->breakpoint; + newtask->libsym = task->libsym; + newtask->context = task->context; + newtask->breakpoint = task->breakpoint; if (task->breakpoint) - retp->breakpoint = breakpoint_find(retp, retp->breakpoint->addr); + newtask->breakpoint = breakpoint_find(newtask, newtask->breakpoint->addr); - if (arch_task_clone(retp, task) < 0) - goto fail2; + if (arch_task_clone(newtask, task) < 0) + goto fail; - /* At this point, retp is fully initialized, except for OS and - * arch parts, and we can call task_destroy. */ - if (os_task_clone(retp, task) < 0) - goto fail3; + if (os_task_clone(newtask, task) < 0) + goto fail; - return retp; -fail3: - arch_task_destroy(retp); -fail2: - task_destroy(retp); -fail1: - fprintf(stderr, "failed to clone process %d->%d : %s\n", task->pid, pid, strerror(errno)); + return 0; +fail: + fprintf(stderr, "failed to fork process %d->%d : %s\n", task->pid, newtask->pid, strerror(errno)); + task_destroy(newtask); - return NULL; + return -1; } static struct task *open_one_pid(pid_t pid) @@ -375,17 +383,12 @@ static struct task *open_one_pid(pid_t pid) if (task == NULL) goto fail1; - if (task_bare_init(task) < 0) - goto fail2; - if (trace_attach(task) < 0) goto fail2; if (trace_set_options(task) < 0) goto fail2; - queue_event(task); - return task; fail2: remove_task(task); @@ -445,7 +448,7 @@ void open_pid(pid_t pid) struct task *child = open_one_pid(tasks[i]); if (child) { - if (backtrace_init(child) < 0) + if (task_clone(child->leader, child) < 0) goto fail2; } @@ -506,8 +509,6 @@ void remove_task(struct task *task) { debug(DEBUG_FUNCTION, "pid=%d", task->pid); - arch_task_destroy(task); - os_task_destroy(task); task_destroy(task); } diff --git a/task.h b/task.h index 016c77e..59810a2 100644 --- a/task.h +++ b/task.h @@ -48,6 +48,7 @@ struct task { unsigned int traced:1; unsigned int was_stopped:1; unsigned int is_64bit:1; + unsigned int attached:1; /* Dictionary of breakpoints */ struct dict *breakpoints; @@ -110,9 +111,11 @@ void open_pid(pid_t pid); struct task *pid2task(pid_t pid); -/* Clone the contents of PROC - * Returns the new task pointer on succes or NULL on failure. */ -struct task *task_clone(struct task *task, pid_t pid); +/* Clone the contents of a task */ +int task_clone(struct task *task, struct task *newtask); + +/* Fork the contents of a task */ +int task_fork(struct task *task, struct task *newtask); /* get first process of leader list */ struct task *get_first_process(void); diff --git a/trace.c b/trace.c index ae05921..9651f42 100644 --- a/trace.c +++ b/trace.c @@ -58,9 +58,9 @@ int skip_breakpoint(struct task *task, struct breakpoint *bp) ret = do_singlestep(task); breakpoint_enable(task, bp); if (ret) { -//fprintf(stderr, "%s:%d %d\n", __FUNCTION__, __LINE__, ret); if (ret == 1) task->skip_bp = bp; + return ret; } } @@ -76,6 +76,7 @@ void detach_task(struct task *task) sig = task->event.e_un.signum; remove_event(task); + breakpoint_hw_destroy(task); untrace_task(task, sig); }