This commit is contained in:
Stefani Seibold 2015-04-26 16:08:45 +02:00
parent 0a26d61e70
commit 92e40e9ec5
27 changed files with 435 additions and 289 deletions

32
.gitignore vendored Normal file
View File

@ -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

0
README Normal file
View File

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

24
dwarf.c
View File

@ -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;

24
event.c
View File

@ -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)

View File

@ -36,8 +36,7 @@ enum event_type {
EVENT_CLONE,
EVENT_VFORK,
EVENT_EXEC,
EVENT_BREAKPOINT,
EVENT_MAX
EVENT_BREAKPOINT
};
struct event {

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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) */

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -22,10 +22,13 @@
#include "dwarf.h"
#include "task.h"
#include <assert.h>
#include <stdio.h>
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);
}

View File

@ -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)

View File

@ -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;
}

View File

@ -19,6 +19,7 @@
*/
#include <assert.h>
#include <backend.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@ -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;
}

View File

@ -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,

View File

@ -1,10 +1,6 @@
/*
* This file is part of mtrace.
* Copyright (C) 2015 Stefani Seibold <stefani@seibold.net>
* 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();

141
task.c
View File

@ -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);
}

9
task.h
View File

@ -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);

View File

@ -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);
}