1.0.39.5: Initial attempt to make breakpoints work on PPC.
authorAlastair Bridgewater <lisphacker@users.sourceforge.net>
Sun, 6 Jun 2010 19:41:10 +0000 (19:41 +0000)
committerAlastair Bridgewater <lisphacker@users.sourceforge.net>
Sun, 6 Jun 2010 19:41:10 +0000 (19:41 +0000)
  * The actual breakpoint trap instruction was wrong, acting as a
NOP instead of trapping.  Fixed.

  * A bit of cleanup and fixes surrounding setting up breakpoints
and dealing with after-breakpoints.

  * An initial implementation of the clever part of
arch_do_displaced_inst().

  * Added a couple of new arch-os-specific functions to find the
parts of the context required for arch_do_displaced_inst().  Stubbed
out for darwin and bsd, probably broken on GLIBC231_STYLE_UCONTEXT
linux systems.

  * Everything beyond fixing the breakpoint trap instruction not
actually tested, because it turned out that the entire breakpoint
system is broken on all non-x86oid targets, due to something in
src/code/debug-int.lisp.

src/runtime/ppc-arch.c
src/runtime/ppc-arch.h
src/runtime/ppc-bsd-os.c
src/runtime/ppc-darwin-os.c
src/runtime/ppc-linux-os.c
version.lisp-expr

index 7fb7b6c..4c8da5f 100644 (file)
@@ -51,6 +51,9 @@
 #endif
 #endif
 
+/* Magic encoding for the instruction used for traps. */
+#define TRAP_INSTRUCTION(trap) ((3<<26) | (6 << 21) | (trap))
+
 void arch_init() {
 }
 
@@ -117,7 +120,7 @@ arch_install_breakpoint(void *pc)
 {
     unsigned int *ptr = (unsigned int *)pc;
     unsigned int result = *ptr;
-    *ptr = (3<<26) | (5 << 21) | trap_Breakpoint;
+    *ptr = TRAP_INSTRUCTION(trap_Breakpoint);
     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
     return result;
 }
@@ -149,12 +152,33 @@ arch_remove_breakpoint(void *pc, unsigned int orig_inst)
 static unsigned int *skipped_break_addr, displaced_after_inst;
 static sigset_t orig_sigmask;
 
+static boolean
+should_branch(os_context_t *context, unsigned int orig_inst)
+{
+    /* orig_inst is a conditional branch instruction.  We need to
+     * know if the branch will be taken if executed in context. */
+    int ctr = *os_context_ctr_addr(context);
+    int cr = *os_context_cr_addr(context);
+    int bo_field = (orig_inst >> 21) & 0x1f;
+    int bi_field = (orig_inst >> 16) & 0x1f;
+    int ctr_ok;
+
+    if (!(bo_field & 4)) ctr--; /* Decrement CTR if necessary. */
+
+    ctr_ok = (bo_field & 4) || ((ctr == 0) == ((bo_field & 2) == 2));
+    return ctr_ok && ((bo_field & 0x10) ||
+                      !(((cr >> (31-bi_field)) ^ (bo_field >> 3)) & 1));
+}
+
 void
 arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
 {
     /* not sure how we ensure that we get the breakpoint reinstalled
      * after doing this -dan */
     unsigned int *pc = (unsigned int *)(*os_context_pc_addr(context));
+    unsigned int *next_pc;
+    int op = orig_inst >> 26;
+    int sub_op = (orig_inst & 0x7fe) >> 1;  /* XL-form sub-opcode */
 
     orig_sigmask = *os_context_sigmask_addr(context);
     sigaddset_blockable(os_context_sigmask_addr(context));
@@ -163,9 +187,53 @@ arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
     skipped_break_addr = pc;
 
-    /* FIXME: we should apparently be installing the after-breakpoint
-     * here, but would need to find the next instruction address for
-     * it first. alpha-arch.c shows how to do it. --NS 2007-04-02 */
+    /* Figure out where we will end up after running the displaced
+     * instruction by defaulting to the next instruction in the stream
+     * and then checking for branch instructions.  FIXME: This will
+     * probably screw up if it attempts to step a trap instruction. */
+    next_pc = pc + 1;
+
+    if (op == 18) {
+        /* Branch  I-form */
+        unsigned int displacement = orig_inst & 0x03fffffc;
+        /* Sign extend */
+        if (displacement & 0x02000000) {
+            displacement |= 0xc0000000;
+        }
+        if (orig_inst & 2) { /* Absolute Address */
+            next_pc = (unsigned int *)displacement;
+        } else {
+            next_pc = (unsigned int *)(((unsigned int)pc) + displacement);
+        }
+    } else if ((op == 16)
+               && should_branch(context, orig_inst)) {
+        /* Branch Conditional  B-form */
+        unsigned int displacement = orig_inst & 0x0000fffc;
+        /* Sign extend */
+        if (displacement & 0x00008000) {
+            displacement |= 0xffff0000;
+        }
+        if (orig_inst & 2) { /* Absolute Address */
+            next_pc = (unsigned int *)displacement;
+        } else {
+            next_pc = (unsigned int *)(((unsigned int)pc) + displacement);
+        }
+    } else if ((op == 19) && (sub_op == 16)
+               && should_branch(context, orig_inst)) {
+        /* Branch Conditional to Link Register  XL-form */
+        next_pc = (unsigned int *)
+            ((*os_context_lr_addr(context)) & ~3);
+    } else if ((op == 19) && (sub_op == 528)
+               && should_branch(context, orig_inst)) {
+        /* Branch Conditional to Count Register  XL-form */
+        next_pc = (unsigned int *)
+            ((*os_context_ctr_addr(context)) & ~3);
+    }
+
+    /* Set the "after" breakpoint. */
+    displaced_after_inst = *next_pc;
+    *next_pc = TRAP_INSTRUCTION(trap_AfterBreakpoint);
+    os_flush_icache((os_vm_address_t)next_pc, sizeof(unsigned int));
 }
 
 #ifdef LISP_FEATURE_GENCGC
@@ -411,7 +479,9 @@ arch_handle_fun_end_breakpoint(os_context_t *context)
 void
 arch_handle_after_breakpoint(os_context_t *context)
 {
-    *skipped_break_addr = trap_Breakpoint;
+    *skipped_break_addr = TRAP_INSTRUCTION(trap_Breakpoint);
+    os_flush_icache((os_vm_address_t) skipped_break_addr,
+                    sizeof(unsigned int));
     skipped_break_addr = NULL;
     *(unsigned int *)*os_context_pc_addr(context)
         = displaced_after_inst;
index be871d8..573f0ea 100644 (file)
@@ -18,4 +18,7 @@ release_spinlock(lispobj *word)
 
 extern void ppc_flush_icache(os_vm_address_t address, os_vm_size_t length);
 
+os_context_register_t *os_context_ctr_addr(os_context_t *context);
+os_context_register_t *os_context_cr_addr(os_context_t *context);
+
 #endif /* _PPC_ARCH_H */
index a9f0ce5..4209b85 100644 (file)
@@ -46,6 +46,20 @@ os_context_lr_addr(os_context_t *context)
 #endif
 }
 
+os_context_register_t *
+os_context_ctr_addr(os_context_t *context)
+{
+    /* FIXME: Figure out how to make this happen. */
+    lose("was asked for context Counter (CTR) register, but don't know how");
+}
+
+os_context_register_t *
+os_context_cr_addr(os_context_t *context)
+{
+    /* FIXME: Figure out how to make this happen. */
+    lose("was asked for context Condition (CR) register, but don't know how");
+}
+
 /* FIXME: If this can be a no-op on BSD/x86, then it
  * deserves a more precise name.
  *
index 461ae9f..46c7b73 100644 (file)
@@ -122,6 +122,20 @@ os_context_lr_addr(os_context_t *context)
 }
 
 os_context_register_t *
+os_context_ctr_addr(os_context_t *context)
+{
+    /* FIXME: Figure out how to make this happen. */
+    lose("was asked for context Counter (CTR) register, but don't know how");
+}
+
+os_context_register_t *
+os_context_cr_addr(os_context_t *context)
+{
+    /* FIXME: Figure out how to make this happen. */
+    lose("was asked for context Condition (CR) register, but don't know how");
+}
+
+os_context_register_t *
 os_context_pc_addr(os_context_t *context)
 {
   return &context->uc_mcontext->PPC_DARWIN_REGIFY(ss).PPC_DARWIN_REGIFY(srr0);
index eac1593..a2e72b4 100644 (file)
@@ -94,6 +94,34 @@ os_context_lr_addr(os_context_t *context)
 #endif
 }
 
+os_context_register_t *
+os_context_ctr_addr(os_context_t *context)
+{
+    /* Like os_context_fp_control() and os_context_lr_addr(), this
+     * uses an index beyond the declared end of the array in order to
+     * find the correct register value in the context. */
+#if defined(GLIBC231_STYLE_UCONTEXT)
+    /* FIXME: This probably should be ->ctr instead of ->gpr[PT_CTR]. */
+    return &((context->uc_mcontext.regs)->gpr[PT_CTR]);
+#elif defined(GLIBC232_STYLE_UCONTEXT)
+    return &((context->uc_mcontext.uc_regs)->gregs[PT_CTR]);
+#endif
+}
+
+os_context_register_t *
+os_context_cr_addr(os_context_t *context)
+{
+    /* Like os_context_fp_control() and os_context_lr_addr(), this
+     * uses an index beyond the declared end of the array in order to
+     * find the correct register value in the context. */
+#if defined(GLIBC231_STYLE_UCONTEXT)
+    /* FIXME: This probably should be ->ccr instead of ->gpr[PT_CCR]. */
+    return &((context->uc_mcontext.regs)->gpr[PT_CCR]);
+#elif defined(GLIBC232_STYLE_UCONTEXT)
+    return &((context->uc_mcontext.uc_regs)->gregs[PT_CCR]);
+#endif
+}
+
 sigset_t *
 os_context_sigmask_addr(os_context_t *context)
 {
index 35d7360..74e2e5a 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.39.4"
+"1.0.39.5"