aboutsummaryrefslogtreecommitdiffstats
path: root/tools/debugger/pdb/linux-2.6-module/debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/debugger/pdb/linux-2.6-module/debug.c')
-rw-r--r--tools/debugger/pdb/linux-2.6-module/debug.c389
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