diff options
author | ach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk> | 2004-05-14 15:03:43 +0000 |
---|---|---|
committer | ach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk> | 2004-05-14 15:03:43 +0000 |
commit | dff38cfe330891949573efc2dee9d6d5b9ddde7d (patch) | |
tree | 66f23ffe992e01daa2b2fd02387ed1f8f956abdd /xen | |
parent | 360b982a1d98e77e5a4e20dccbdd7a5afc15fa50 (diff) | |
download | xen-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.c | 100 | ||||
-rw-r--r-- | xen/arch/i386/pdb-stub.c | 174 | ||||
-rw-r--r-- | xen/arch/i386/traps.c | 13 | ||||
-rw-r--r-- | xen/include/asm-i386/pdb.h | 29 | ||||
-rw-r--r-- | xen/include/asm-i386/processor.h | 6 |
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); |