From fca1ffc8bcb427ac08a768180e0eea3aee17feb4 Mon Sep 17 00:00:00 2001 From: Alastair Bridgewater Date: Sun, 6 Jun 2010 19:41:10 +0000 Subject: [PATCH] 1.0.39.5: Initial attempt to make breakpoints work on PPC. * 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 | 80 ++++++++++++++++++++++++++++++++++++++++--- src/runtime/ppc-arch.h | 3 ++ src/runtime/ppc-bsd-os.c | 14 ++++++++ src/runtime/ppc-darwin-os.c | 14 ++++++++ src/runtime/ppc-linux-os.c | 28 +++++++++++++++ version.lisp-expr | 2 +- 6 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/runtime/ppc-arch.c b/src/runtime/ppc-arch.c index 7fb7b6c..4c8da5f 100644 --- a/src/runtime/ppc-arch.c +++ b/src/runtime/ppc-arch.c @@ -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; diff --git a/src/runtime/ppc-arch.h b/src/runtime/ppc-arch.h index be871d8..573f0ea 100644 --- a/src/runtime/ppc-arch.h +++ b/src/runtime/ppc-arch.h @@ -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 */ diff --git a/src/runtime/ppc-bsd-os.c b/src/runtime/ppc-bsd-os.c index a9f0ce5..4209b85 100644 --- a/src/runtime/ppc-bsd-os.c +++ b/src/runtime/ppc-bsd-os.c @@ -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. * diff --git a/src/runtime/ppc-darwin-os.c b/src/runtime/ppc-darwin-os.c index 461ae9f..46c7b73 100644 --- a/src/runtime/ppc-darwin-os.c +++ b/src/runtime/ppc-darwin-os.c @@ -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); diff --git a/src/runtime/ppc-linux-os.c b/src/runtime/ppc-linux-os.c index eac1593..a2e72b4 100644 --- a/src/runtime/ppc-linux-os.c +++ b/src/runtime/ppc-linux-os.c @@ -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) { diff --git a/version.lisp-expr b/version.lisp-expr index 35d7360..74e2e5a 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -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" -- 1.7.10.4