bitkeeper revision 1.891.1.22 (40a4dfcfIxTtNtL9Vh3WBZEZINWxqg)
authorach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk>
Fri, 14 May 2004 15:03:43 +0000 (15:03 +0000)
committerach61@labyrinth.cl.cam.ac.uk <ach61@labyrinth.cl.cam.ac.uk>
Fri, 14 May 2004 15:03:43 +0000 (15:03 +0000)
vertical debugging: pdb can break when entering / exiting a system call from user space

.rootkeys
xen/arch/i386/pdb-linux.c [new file with mode: 0644]
xen/arch/i386/pdb-stub.c
xen/arch/i386/traps.c
xen/include/asm-i386/pdb.h
xen/include/asm-i386/processor.h

index a47520e6acdafd58ef820847ab1272995ebceeae..3a60ca432809e06d83ecaabd25eaedcffcb12dda 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/i386/pci-i386.h
 3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/i386/pci-irq.c
 3ddb79bcZ_2FxINljqNSkqa17ISyJw xen/arch/i386/pci-pc.c
+40a4dfced2dnSzbKgJFlD3chKHexjQ xen/arch/i386/pdb-linux.c
 4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/i386/pdb-stub.c
 3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/i386/process.c
 3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/i386/rwlock.c
diff --git a/xen/arch/i386/pdb-linux.c b/xen/arch/i386/pdb-linux.c
new file mode 100644 (file)
index 0000000..fd0fc5e
--- /dev/null
@@ -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);
+}
index 5b42e9a74612277f6f255ea0279eec1504c8c05d..63320561dca1f0ef7196ac11fde0fbde2876d50f 100644 (file)
@@ -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", 
index f362faa05faf21d19229d554ece5216f313ac501..b38c2921e3f9d0ebce25fb329c2fb5c1f6d37092 100644 (file)
@@ -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;
index 68efcbccafded74661d8f02777d5af1fd25d0fea..2ed6a9a3187d38b6ecdbd5e6f997e13be0569b89 100644 (file)
@@ -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__ */
index aa5048c6ec533d8ce2e3ab715ef74b223c5b6469..49055c258425e12df681898d519e850a94da9f08 100644 (file)
@@ -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);