aboutsummaryrefslogtreecommitdiffstats
path: root/xen
diff options
context:
space:
mode:
authorach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk>2004-05-14 15:03:43 +0000
committerach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk>2004-05-14 15:03:43 +0000
commitdff38cfe330891949573efc2dee9d6d5b9ddde7d (patch)
tree66f23ffe992e01daa2b2fd02387ed1f8f956abdd /xen
parent360b982a1d98e77e5a4e20dccbdd7a5afc15fa50 (diff)
downloadxen-dff38cfe330891949573efc2dee9d6d5b9ddde7d.tar.gz
xen-dff38cfe330891949573efc2dee9d6d5b9ddde7d.tar.bz2
xen-dff38cfe330891949573efc2dee9d6d5b9ddde7d.zip
bitkeeper revision 1.891.1.22 (40a4dfcfIxTtNtL9Vh3WBZEZINWxqg)
vertical debugging: pdb can break when entering / exiting a system call from user space
Diffstat (limited to 'xen')
-rw-r--r--xen/arch/i386/pdb-linux.c100
-rw-r--r--xen/arch/i386/pdb-stub.c174
-rw-r--r--xen/arch/i386/traps.c13
-rw-r--r--xen/include/asm-i386/pdb.h29
-rw-r--r--xen/include/asm-i386/processor.h6
5 files changed, 255 insertions, 67 deletions
diff --git a/xen/arch/i386/pdb-linux.c b/xen/arch/i386/pdb-linux.c
new file mode 100644
index 0000000000..fd0fc5ed78
--- /dev/null
+++ b/xen/arch/i386/pdb-linux.c
@@ -0,0 +1,100 @@
+
+/*
+ * pervasive debugger
+ * www.cl.cam.ac.uk/netos/pdb
+ *
+ * alex ho
+ * 2004
+ * university of cambridge computer laboratory
+ *
+ * linux & i386 dependent code. bleech.
+ */
+
+#include <asm/pdb.h>
+
+/* offset to the first instruction in the linux system call code
+ where we can safely set a breakpoint */
+unsigned int pdb_linux_syscall_enter_bkpt_offset = 20;
+
+/* offset to eflags saved on the stack after an int 80 */
+unsigned int pdb_linux_syscall_eflags_offset = 48;
+
+/* offset to the instruction pointer saved on the stack after an int 80 */
+unsigned int pdb_linux_syscall_eip_offset = 40;
+
+unsigned char
+pdb_linux_set_bkpt (unsigned long addr)
+{
+ unsigned char old_instruction = *(unsigned char *)addr;
+ *(unsigned char *)addr = 0xcc;
+ return old_instruction;
+}
+
+void
+pdb_linux_clr_bkpt (unsigned long addr, unsigned char value)
+{
+ *(unsigned char *)addr = value;
+}
+
+void
+pdb_linux_syscall_enter_bkpt (struct pt_regs *regs, long error_code,
+ trap_info_t *ti)
+{
+ /* set at breakpoint at the beginning of the
+ system call in the target domain */
+
+ pdb_system_call_enter_instr = pdb_linux_set_bkpt(ti->address +
+ pdb_linux_syscall_enter_bkpt_offset);
+ pdb_system_call = 1;
+}
+
+void
+pdb_linux_syscall_exit_bkpt (struct pt_regs *regs, struct pdb_context *pdb_ctx)
+{
+ /*
+ we've hit an int 0x80 in a user's program, jumped into xen
+ (traps.c::do_general_protection()) which re-wrote the next
+ instruction in the os kernel to 0xcc, and then hit that
+ exception.
+
+ we need to re-write the return instruction in the user's
+ program so that we know when we have finished the system call
+ and are back in the user's program.
+
+ at this point our stack should look something like this:
+
+ esp = 0x80a59f0
+ esp + 4 = 0x0
+ esp + 8 = 0x80485a0
+ esp + 12 = 0x2d
+ esp + 16 = 0x80485f4
+ esp + 20 = 0xbffffa48
+ esp + 24 = 0xd
+ esp + 28 = 0xc00a0833
+ esp + 32 = 0x833
+ esp + 36 = 0xd
+ esp + 40 = 0x804dcdd saved eip
+ esp + 44 = 0x82b saved cs
+ esp + 48 = 0x213392 saved eflags
+ esp + 52 = 0xbffffa2c saved esp
+ esp + 56 = 0x833 saved ss
+ esp + 60 = 0x1000000
+ */
+
+ /* restore the entry instruction for the system call */
+ pdb_linux_clr_bkpt(regs->eip - 1, pdb_system_call_enter_instr);
+
+ /* save the address of eflags that was saved on the stack */
+ pdb_system_call_eflags_addr = (regs->esp +
+ pdb_linux_syscall_eflags_offset);
+
+ /* muck with the return instruction so that we trap back into the
+ debugger when re-entering user space */
+ pdb_system_call_next_addr = *(unsigned long *)(regs->esp +
+ pdb_linux_syscall_eip_offset);
+ pdb_linux_get_values (&pdb_system_call_leave_instr, 1,
+ pdb_system_call_next_addr,
+ pdb_ctx->process, pdb_ctx->ptbr);
+ pdb_linux_set_values ("cc", 1, pdb_system_call_next_addr,
+ pdb_ctx->process, pdb_ctx->ptbr);
+}
diff --git a/xen/arch/i386/pdb-stub.c b/xen/arch/i386/pdb-stub.c
index 5b42e9a746..63320561dc 100644
--- a/xen/arch/i386/pdb-stub.c
+++ b/xen/arch/i386/pdb-stub.c
@@ -47,26 +47,13 @@ static int pdb_in_buffer_ptr;
static unsigned char pdb_in_checksum;
static unsigned char pdb_xmit_checksum;
-/* function pointers in the near future... */
-unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid);
-void pdb_linux_get_values(char *buffer, int length, unsigned long address,
- int pid, unsigned long cr3);
-void pdb_linux_set_values(char *buffer, int length, unsigned long address,
- int pid, unsigned long cr3);
-
-struct pdb_context
-{
- int valid;
- int domain;
- int process;
- unsigned long ptbr; /* cached page table base register */
-};
struct pdb_context pdb_ctx;
-
int pdb_continue_thread = 0;
int pdb_general_thread = 0;
void pdb_put_packet (unsigned char *buffer, int ack);
+void pdb_bkpt_check (u_char *buffer, int length,
+ unsigned long cr3, unsigned long addr);
int pdb_initialized = 0;
int pdb_page_fault_possible = 0;
@@ -75,6 +62,12 @@ int pdb_page_fault = 0;
static int pdb_serhnd = -1;
static int pdb_stepping = 0;
+int pdb_system_call = 0;
+unsigned char pdb_system_call_enter_instr = 0; /* original enter instr */
+unsigned char pdb_system_call_leave_instr = 0; /* original next instr */
+unsigned long pdb_system_call_next_addr = 0; /* instr after int 0x80 */
+unsigned long pdb_system_call_eflags_addr = 0; /* saved eflags on stack */
+
static inline void pdb_put_char(unsigned char c)
{
serial_putc(pdb_serhnd, c);
@@ -406,15 +399,49 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3,
break;
case 'S': /* step with signal */
case 's': /* step */
+ {
+ if ( pdb_system_call_eflags_addr != 0 )
+ {
+ unsigned long eflags;
+ char eflags_buf[sizeof(eflags)*2]; /* STUPID STUPID STUPID */
+
+ pdb_linux_get_values((u_char*)&eflags, sizeof(eflags),
+ pdb_system_call_eflags_addr,
+ pdb_ctx.process, pdb_ctx.ptbr);
+ eflags |= X86_EFLAGS_TF;
+ mem2hex ((u_char *)&eflags, eflags_buf, sizeof(eflags));
+ pdb_linux_set_values(eflags_buf, sizeof(eflags),
+ pdb_system_call_eflags_addr,
+ pdb_ctx.process, pdb_ctx.ptbr);
+ }
+
regs->eflags |= X86_EFLAGS_TF;
pdb_stepping = 1;
return 1;
/* not reached */
+ }
case 'C': /* continue with signal */
case 'c': /* continue */
+ {
+ if ( pdb_system_call_eflags_addr != 0 )
+ {
+ unsigned long eflags;
+ char eflags_buf[sizeof(eflags)*2]; /* STUPID STUPID STUPID */
+
+ pdb_linux_get_values((u_char*)&eflags, sizeof(eflags),
+ pdb_system_call_eflags_addr,
+ pdb_ctx.process, pdb_ctx.ptbr);
+ eflags &= ~X86_EFLAGS_TF;
+ mem2hex ((u_char *)&eflags, eflags_buf, sizeof(eflags));
+ pdb_linux_set_values(eflags_buf, sizeof(eflags),
+ pdb_system_call_eflags_addr,
+ pdb_ctx.process, pdb_ctx.ptbr);
+ }
+
regs->eflags &= ~X86_EFLAGS_TF;
return 1; /* jump out before replying to gdb */
/* not reached */
+ }
case 'd':
remote_debug = !(remote_debug); /* toggle debug flag */
break;
@@ -424,54 +451,11 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3,
case 'g': /* return the value of the CPU registers */
{
pdb_x86_to_gdb_regs (pdb_out_buffer, regs);
-
- /*
- printk (" reg: %s", pdb_out_buffer);
- printk ("\n");
- printk (" eax: 0x%08lx\n", regs->eax);
- printk (" ecx: 0x%08lx\n", regs->ecx);
- printk (" edx: 0x%08lx\n", regs->edx);
- printk (" ebx: 0x%08lx\n", regs->ebx);
- printk (" esp: 0x%08lx\n", regs->esp);
- printk (" ebp: 0x%08lx\n", regs->ebp);
- printk (" esi: 0x%08lx\n", regs->esi);
- printk (" edi: 0x%08lx\n", regs->edi);
- printk (" eip: 0x%08lx\n", regs->eip);
- printk (" efl: 0x%08lx\n", regs->eflags);
- printk (" xcs: 0x%08x\n", regs->xcs);
- printk (" xss: 0x%08x\n", regs->xss);
- printk (" xds: 0x%08x\n", regs->xds);
- printk (" xes: 0x%08x\n", regs->xes);
- printk (" xfs: 0x%08x\n", regs->xfs);
- printk (" xgs: 0x%08x\n", regs->xgs);
- */
-
break;
}
case 'G': /* set the value of the CPU registers - return OK */
{
pdb_gdb_to_x86_regs (regs, ptr);
-
- /*
- printk (" ptr: %s \n\n", ptr);
- printk (" eax: 0x%08lx\n", regs->eax);
- printk (" ecx: 0x%08lx\n", regs->ecx);
- printk (" edx: 0x%08lx\n", regs->edx);
- printk (" ebx: 0x%08lx\n", regs->ebx);
- printk (" esp: 0x%08lx\n", regs->esp);
- printk (" ebp: 0x%08lx\n", regs->ebp);
- printk (" esi: 0x%08lx\n", regs->esi);
- printk (" edi: 0x%08lx\n", regs->edi);
- printk (" eip: 0x%08lx\n", regs->eip);
- printk (" efl: 0x%08lx\n", regs->eflags);
- printk (" xcs: 0x%08x\n", regs->xcs);
- printk (" xss: 0x%08x\n", regs->xss);
- printk (" xds: 0x%08x\n", regs->xds);
- printk (" xes: 0x%08x\n", regs->xes);
- printk (" xfs: 0x%08x\n", regs->xfs);
- printk (" xgs: 0x%08x\n", regs->xgs);
- */
-
break;
}
case 'H':
@@ -572,17 +556,20 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3,
if (addr >= PAGE_OFFSET)
{
hex2mem (ptr, (char *)addr, length);
+ pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr);
}
else if (pdb_ctx.process != -1)
{
pdb_linux_set_values(ptr, length, addr,
pdb_ctx.process,
pdb_ctx.ptbr);
+ pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr);
}
else
{
pdb_set_values (ptr, length,
pdb_ctx.ptbr, addr);
+ pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr);
}
pdb_page_fault_possible = 0;
if (pdb_page_fault)
@@ -936,7 +923,6 @@ int pdb_set_values(u_char *buffer, int length,
unsigned long cr3, unsigned long addr)
{
int count = pdb_change_values(buffer, length, cr3, addr, __PDB_SET_VAL);
- pdb_bkpt_check(buffer, length, cr3, addr);
return count;
}
@@ -1176,16 +1162,35 @@ int pdb_handle_exception(int exceptionVector,
__asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : );
+ /* If the exception is an int3 from user space then pdb is only
+ interested if it re-wrote an instruction set the breakpoint.
+ This occurs when leaving a system call from a domain.
+ */
+ if ( exceptionVector == 3 &&
+ (xen_regs->xcs & 3) == 3 &&
+ xen_regs->eip != pdb_system_call_next_addr + 1)
+ {
+ TRC(printf("pdb: user bkpt (0x%x) at 0x%x:0x%lx:0x%lx\n",
+ exceptionVector, xen_regs->xcs & 3, cr3, xen_regs->eip));
+ return 1;
+ }
+
/*
- * If PDB didn't set the breakpoint, is not single stepping, and the user
- * didn't press the magic debug key, then we don't handle the exception.
+ * If PDB didn't set the breakpoint, is not single stepping,
+ * is not entering a system call in a domain,
+ * the user didn't press the magic debug key,
+ * then we don't handle the exception.
*/
bkpt = pdb_bkpt_search(cr3, xen_regs->eip - 1);
if ( (bkpt == NULL) &&
- !pdb_stepping && (exceptionVector != KEYPRESS_EXCEPTION) &&
+ !pdb_stepping &&
+ !pdb_system_call &&
+ xen_regs->eip != pdb_system_call_next_addr + 1 &&
+ (exceptionVector != KEYPRESS_EXCEPTION) &&
xen_regs->eip < 0xc0000000) /* xenolinux for now! */
{
- TRC(printf("pdb: user bkpt at 0x%lx:0x%lx\n", cr3, xen_regs->eip));
+ TRC(printf("pdb: user bkpt (0x%x) at 0x%lx:0x%lx\n",
+ exceptionVector, cr3, xen_regs->eip));
return 1;
}
@@ -1199,12 +1204,54 @@ int pdb_handle_exception(int exceptionVector,
pdb_stepping = 0;
}
+ if ( pdb_system_call )
+ {
+ pdb_system_call = 0;
+
+ pdb_linux_syscall_exit_bkpt (xen_regs, &pdb_ctx);
+
+ /* we don't have a saved breakpoint so we need to rewind eip */
+ xen_regs->eip--;
+
+ /* if ther user doesn't care about breaking when entering a
+ system call then we'll just ignore the exception */
+ if ( (pdb_ctx.system_call & 0x01) == 0 )
+ {
+ return 0;
+ }
+ }
+
if ( exceptionVector == BREAKPT_EXCEPTION && bkpt != NULL)
{
/* Executed Int3: replace breakpoint byte with real program byte. */
xen_regs->eip--;
}
+ /* returning to user space after a system call */
+ if ( xen_regs->eip == pdb_system_call_next_addr + 1)
+ {
+ u_char instr[2]; /* REALLY REALLY REALLY STUPID */
+
+ mem2hex (&pdb_system_call_leave_instr, instr, sizeof(instr));
+
+ pdb_linux_set_values (instr, 1, pdb_system_call_next_addr,
+ pdb_ctx.process, pdb_ctx.ptbr);
+
+ pdb_system_call_next_addr = 0;
+ pdb_system_call_leave_instr = 0;
+
+ /* manually rewind eip */
+ xen_regs->eip--;
+
+ /* if the user doesn't care about breaking when returning
+ to user space after a system call then we'll just ignore
+ the exception */
+ if ( (pdb_ctx.system_call & 0x02) == 0 )
+ {
+ return 0;
+ }
+ }
+
/* Generate a signal for GDB. */
switch ( exceptionVector )
{
@@ -1267,6 +1314,7 @@ void initialize_pdb()
pdb_ctx.valid = 1;
pdb_ctx.domain = -1;
pdb_ctx.process = -1;
+ pdb_ctx.system_call = 0;
pdb_ctx.ptbr = 0;
printk("pdb: pervasive debugger (%s) www.cl.cam.ac.uk/netos/pdb\n",
diff --git a/xen/arch/i386/traps.c b/xen/arch/i386/traps.c
index f362faa05f..b38c2921e3 100644
--- a/xen/arch/i386/traps.c
+++ b/xen/arch/i386/traps.c
@@ -258,10 +258,10 @@ asmlinkage void do_int3(struct pt_regs *regs, long error_code)
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
trap_info_t *ti;
+ if ( pdb_handle_exception(3, regs) == 0 )
+ return;
if ( (regs->xcs & 3) != 3 )
{
- if ( pdb_handle_exception(3, regs) == 0 )
- return;
if ( unlikely((regs->xcs & 3) == 0) )
{
show_registers(regs);
@@ -436,6 +436,15 @@ asmlinkage void do_general_protection(struct pt_regs *regs, long error_code)
ti = current->thread.traps + (error_code>>3);
if ( TI_GET_DPL(ti) >= (regs->xcs & 3) )
{
+ unsigned long cr3;
+
+ __asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : );
+ if (pdb_initialized && pdb_ctx.system_call != 0 &&
+ cr3 == pdb_ctx.ptbr)
+ {
+ pdb_linux_syscall_enter_bkpt(regs, error_code, ti);
+ }
+
gtb->flags = GTBF_TRAP_NOCODE;
regs->eip += 2;
goto finish_propagation;
diff --git a/xen/include/asm-i386/pdb.h b/xen/include/asm-i386/pdb.h
index 68efcbccaf..2ed6a9a318 100644
--- a/xen/include/asm-i386/pdb.h
+++ b/xen/include/asm-i386/pdb.h
@@ -14,6 +14,7 @@
#include <asm/ptrace.h>
#include <xen/list.h>
+#include <hypervisor-ifs/dom0_ops.h>
#include <hypervisor-ifs/hypervisor-if.h> /* for domain id */
extern int pdb_initialized;
@@ -37,6 +38,17 @@ extern int pdb_handle_exception(int exceptionVector,
extern int pdb_serial_input(u_char c, struct pt_regs *regs);
extern void pdb_do_debug(dom0_op_t *op);
+/* PDB Context. */
+struct pdb_context
+{
+ int valid;
+ int domain;
+ int process;
+ int system_call; /* 0x01 break on enter, 0x02 break on exit */
+ unsigned long ptbr;
+};
+extern struct pdb_context pdb_ctx;
+
/* Breakpoints. */
struct pdb_breakpoint
{
@@ -56,4 +68,21 @@ extern char *mem2hex (char *, char *, int);
extern char *hex2mem (char *, char *, int);
extern int hexToInt (char **ptr, int *intValue);
+/* Temporary Linux specific definitions */
+extern int pdb_system_call;
+extern unsigned char pdb_system_call_enter_instr; /* original enter instr */
+extern unsigned char pdb_system_call_leave_instr; /* original next instr */
+extern unsigned long pdb_system_call_next_addr; /* instr after int 0x80 */
+extern unsigned long pdb_system_call_eflags_addr; /* saved eflags on stack */
+
+unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid);
+void pdb_linux_get_values(char *buffer, int length, unsigned long address,
+ int pid, unsigned long cr3);
+void pdb_linux_set_values(char *buffer, int length, unsigned long address,
+ int pid, unsigned long cr3);
+void pdb_linux_syscall_enter_bkpt (struct pt_regs *regs, long error_code,
+ trap_info_t *ti);
+void pdb_linux_syscall_exit_bkpt (struct pt_regs *regs,
+ struct pdb_context *pdb_ctx);
+
#endif /* __PDB_H__ */
diff --git a/xen/include/asm-i386/processor.h b/xen/include/asm-i386/processor.h
index aa5048c6ec..49055c2584 100644
--- a/xen/include/asm-i386/processor.h
+++ b/xen/include/asm-i386/processor.h
@@ -12,6 +12,7 @@
#include <asm/cpufeature.h>
#include <asm/desc.h>
#include <asm/flushtlb.h>
+#include <asm/pdb.h>
#include <xen/config.h>
#include <xen/spinlock.h>
#include <hypervisor-ifs/hypervisor-if.h>
@@ -406,8 +407,9 @@ extern struct desc_struct *idt_tables[];
0, 8))
#define SET_FAST_TRAP(_p) \
- (memcpy(idt_tables[smp_processor_id()] + (_p)->fast_trap_idx, \
- &((_p)->fast_trap_desc), 8))
+ (pdb_initialized ? (void *) 0 : \
+ (memcpy(idt_tables[smp_processor_id()] + (_p)->fast_trap_idx, \
+ &((_p)->fast_trap_desc), 8)))
long set_fast_trap(struct task_struct *p, int idx);