diff options
Diffstat (limited to 'tools/debugger/pdb/linux-2.6-module/debug.c')
-rw-r--r-- | tools/debugger/pdb/linux-2.6-module/debug.c | 389 |
1 files changed, 309 insertions, 80 deletions
diff --git a/tools/debugger/pdb/linux-2.6-module/debug.c b/tools/debugger/pdb/linux-2.6-module/debug.c index ba1825cc30..983e02dccc 100644 --- a/tools/debugger/pdb/linux-2.6-module/debug.c +++ b/tools/debugger/pdb/linux-2.6-module/debug.c @@ -3,49 +3,54 @@ * pdb debug functionality for processes. */ - #include <linux/module.h> +#include <linux/mm.h> #include <linux/sched.h> +#include <asm-i386/kdebug.h> +#include <asm-xen/asm-i386/processor.h> #include <asm-xen/asm-i386/ptrace.h> #include <asm-xen/xen-public/xen.h> - #include "pdb_module.h" +#include "pdb_debug.h" + +#define BWC_DEBUG 1 +#define BWC_INT3 3 +typedef struct bwcpoint /* break/watch/catch point */ +{ + struct list_head list; + memory_t address; + u32 domain; + u32 process; + u8 old_value; /* old value for software bkpt */ + u8 type; /* BWC_??? */ +} bwcpoint_t, *bwcpoint_p; + +static bwcpoint_t bwcpoint_list; + +void +pdb_initialize_bwcpoint (void) +{ + memset((void *) &bwcpoint_list, 0, sizeof(bwcpoint_t)); + INIT_LIST_HEAD(&bwcpoint_list.list); + + return; +} -EXPORT_SYMBOL(pdb_attach); -EXPORT_SYMBOL(pdb_detach); int -pdb_attach (int pid) +pdb_suspend (struct task_struct *target) { - struct task_struct *target; u32 rc = 0; - printk ("pdb attach: 0x%x\n", pid); - - read_lock(&tasklist_lock); - target = find_task_by_pid(pid); - if (target) - get_task_struct(target); - read_unlock(&tasklist_lock); - force_sig(SIGSTOP, target); /* force_sig_specific ??? */ return rc; } int -pdb_detach (int pid) +pdb_resume (struct task_struct *target) { int rc = 0; - struct task_struct *target; - - printk ("pdb detach: 0x%x\n", pid); - - read_lock(&tasklist_lock); - target = find_task_by_pid(pid); - if (target) - get_task_struct(target); - read_unlock(&tasklist_lock); wake_up_process(target); @@ -55,7 +60,6 @@ pdb_detach (int pid) /* * from linux-2.6.11/arch/i386/kernel/ptrace.c::getreg() */ - static unsigned long _pdb_get_register (struct task_struct *target, int reg) { @@ -65,20 +69,20 @@ _pdb_get_register (struct task_struct *target, int reg) switch (reg) { - case FS: + case LINUX_FS: result = target->thread.fs; break; - case GS: + case LINUX_GS: result = target->thread.gs; break; - case DS: - case ES: - case SS: - case CS: + case LINUX_DS: + case LINUX_ES: + case LINUX_SS: + case LINUX_CS: result = 0xffff; /* fall through */ default: - if (reg > GS) + if (reg > LINUX_GS) reg -= 2; offset = reg * sizeof(long); @@ -91,17 +95,51 @@ _pdb_get_register (struct task_struct *target, int reg) return result; } +/* + * from linux-2.6.11/arch/i386/kernel/ptrace.c::putreg() + */ +static void +_pdb_set_register (struct task_struct *target, int reg, unsigned long val) +{ + unsigned long offset; + unsigned char *stack; + unsigned long value = val; + + switch (reg) + { + case LINUX_FS: + target->thread.fs = value; + return; + case LINUX_GS: + target->thread.gs = value; + return; + case LINUX_DS: + case LINUX_ES: + value &= 0xffff; + break; + case LINUX_SS: + case LINUX_CS: + value &= 0xffff; + break; + case LINUX_EFL: + break; + } + + if (reg > LINUX_GS) + reg -= 2; + offset = reg * sizeof(long); + offset -= sizeof(struct pt_regs); + stack = (unsigned char *)target->thread.esp0; + stack += offset; + *(unsigned long *) stack = value; + + return; +} + int -pdb_read_register (int pid, pdb_op_rd_regs_p op) +pdb_read_registers (struct task_struct *target, pdb_op_rd_regs_p op) { int rc = 0; - struct task_struct *target; - - read_lock(&tasklist_lock); - target = find_task_by_pid(pid); - if (target) - get_task_struct(target); - read_unlock(&tasklist_lock); op->reg[ 0] = _pdb_get_register(target, LINUX_EAX); op->reg[ 1] = _pdb_get_register(target, LINUX_ECX); @@ -124,59 +162,250 @@ pdb_read_register (int pid, pdb_op_rd_regs_p op) return rc; } -/* - * from linux-2.6.11/arch/i386/kernel/ptrace.c::putreg() - */ int -pdb_write_register (int pid, pdb_op_wr_reg_p op) +pdb_write_register (struct task_struct *target, pdb_op_wr_reg_p op) { int rc = 0; - struct task_struct *target; - unsigned long offset; - unsigned char *stack; - unsigned long value = op->value; - /* - printk ("pdb write register: 0x%x %2d 0x%lx\n", pid, op->reg, value); - */ + _pdb_set_register(target, op->reg, op->value); + + return rc; +} + +int +pdb_access_memory (struct task_struct *target, unsigned long address, + void *buffer, int length, int write) +{ + int rc = 0; + + access_process_vm(target, address, buffer, length, write); + + return rc; +} + +int +pdb_continue (struct task_struct *target) +{ + int rc = 0; + unsigned long eflags; + + eflags = _pdb_get_register(target, LINUX_EFL); + eflags &= ~X86_EFLAGS_TF; + _pdb_set_register(target, LINUX_EFL, eflags); + + wake_up_process(target); - read_lock(&tasklist_lock); - target = find_task_by_pid(pid); - if (target) - get_task_struct(target); - read_unlock(&tasklist_lock); + return rc; +} - switch (op->reg) +int +pdb_step (struct task_struct *target) +{ + int rc = 0; + unsigned long eflags; + bwcpoint_p bkpt; + + eflags = _pdb_get_register(target, LINUX_EFL); + eflags |= X86_EFLAGS_TF; + _pdb_set_register(target, LINUX_EFL, eflags); + + bkpt = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL); + if ( bkpt == NULL ) { - case FS: - target->thread.fs = value; - return rc; - case GS: - target->thread.gs = value; - return rc; - case DS: - case ES: - value &= 0xffff; - break; - case SS: - case CS: - value &= 0xffff; - break; - case EFL: - break; + printk("error: unable to allocation memory\n"); + return -1; } - if (op->reg > GS) - op->reg -= 2; - offset = op->reg * sizeof(long); - offset -= sizeof(struct pt_regs); - stack = (unsigned char *)target->thread.esp0; - stack += offset; - *(unsigned long *) stack = op->value; + bkpt->process = target->pid; + bkpt->address = 0; + bkpt->type = BWC_DEBUG; + + list_add(&bkpt->list, &bwcpoint_list.list); + + wake_up_process(target); + + return rc; +} + +int +pdb_insert_memory_breakpoint (struct task_struct *target, + memory_t address, u32 length) +{ + int rc = 0; + bwcpoint_p bkpt; + u8 breakpoint_opcode = 0xcc; + + printk("insert breakpoint %d:%lx len: %d\n", target->pid, address, length); + + bkpt = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL); + if ( bkpt == NULL ) + { + printk("error: unable to allocation memory\n"); + return -1; + } + + if ( length != 1 ) + { + printk("error: breakpoint length should be 1\n"); + kfree(bkpt); + return -1; + } + + bkpt->process = target->pid; + bkpt->address = address; + bkpt->type = BWC_INT3; + + pdb_access_memory(target, address, &bkpt->old_value, 1, 0); + pdb_access_memory(target, address, &breakpoint_opcode, 1, 1); + + list_add(&bkpt->list, &bwcpoint_list.list); + + printk("breakpoint_set %d:%lx OLD: 0x%x\n", + target->pid, address, bkpt->old_value); + + return rc; +} + +int +pdb_remove_memory_breakpoint (struct task_struct *target, + memory_t address, u32 length) +{ + int rc = 0; + bwcpoint_p bkpt = NULL; + + printk ("remove breakpoint %d:%lx\n", target->pid, address); + + struct list_head *entry; + list_for_each(entry, &bwcpoint_list.list) + { + bkpt = list_entry(entry, bwcpoint_t, list); + if ( target->pid == bkpt->process && + address == bkpt->address && + bkpt->type == BWC_INT3 ) + break; + } + + if (bkpt == &bwcpoint_list || bkpt == NULL) + { + printk ("error: no breakpoint found\n"); + return -1; + } + + list_del(&bkpt->list); + + pdb_access_memory(target, address, &bkpt->old_value, 1, 1); + + kfree(bkpt); return rc; } + +/***************************************************************/ + +int +pdb_exceptions_notify (struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = (struct die_args *)data; + + switch (val) + { + case DIE_DEBUG: + if (pdb_debug_fn(args->regs, args->trapnr, args->err)) + return NOTIFY_STOP; + break; + case DIE_TRAP: + if (args->trapnr == 3 && pdb_int3_fn(args->regs, args->err)) + return NOTIFY_STOP; + break; + case DIE_INT3: /* without kprobes, we should never see DIE_INT3 */ + case DIE_GPF: + case DIE_PAGE_FAULT: + default: + break; + } + + return NOTIFY_DONE; +} + + +int +pdb_debug_fn (struct pt_regs *regs, long error_code, + unsigned int condition) +{ + pdb_response_t resp; + bwcpoint_p bkpt = NULL; + + struct list_head *entry; + list_for_each(entry, &bwcpoint_list.list) + { + bkpt = list_entry(entry, bwcpoint_t, list); + if ( current->pid == bkpt->process && + bkpt->type == BWC_DEBUG ) + break; + } + + if (bkpt == &bwcpoint_list || bkpt == NULL) + { + printk("not my debug 0x%x 0x%lx\n", current->pid, regs->eip); + return 0; + } + + list_del(&bkpt->list); + + pdb_suspend(current); + + printk("(pdb) debug pid: %d, eip: 0x%08lx\n", current->pid, regs->eip); + + regs->eflags &= ~X86_EFLAGS_TF; + set_tsk_thread_flag(current, TIF_SINGLESTEP); + + resp.operation = PDB_OPCODE_STEP; + resp.process = current->pid; + resp.status = PDB_RESPONSE_OKAY; + + pdb_send_response(&resp); + + return 1; +} + + +int +pdb_int3_fn (struct pt_regs *regs, long error_code) +{ + pdb_response_t resp; + bwcpoint_p bkpt = NULL; + + struct list_head *entry; + list_for_each(entry, &bwcpoint_list.list) + { + bkpt = list_entry(entry, bwcpoint_t, list); + if ( current->pid == bkpt->process && + regs->eip == bkpt->address && + bkpt->type == BWC_INT3 ) + break; + } + + if (bkpt == &bwcpoint_list || bkpt == NULL) + { + printk("not my int3 bkpt 0x%x 0x%lx\n", current->pid, regs->eip); + return 0; + } + + printk("(pdb) int3 pid: %d, eip: 0x%08lx\n", current->pid, regs->eip); + + pdb_suspend(current); + + resp.operation = PDB_OPCODE_CONTINUE; + resp.process = current->pid; + resp.status = PDB_RESPONSE_OKAY; + + pdb_send_response(&resp); + + return 1; +} + /* * Local variables: * mode: C |