From: Alastair Bridgewater Date: Mon, 1 Mar 2010 13:09:00 +0000 (+0000) Subject: 1.0.36.9: UD2-BREAKPOINTS feature for x86oid systems X-Git-Url: http://repo.macrolet.net/gitweb/?a=commitdiff_plain;h=11b5ac86a98f058fe0375b0a707c6ef9e24590c9;p=sbcl.git 1.0.36.9: UD2-BREAKPOINTS feature for x86oid systems * Add new feature UD2-BREAKPOINTS, enabled by default only on x86oid darwin targets. * Use said feature instead of DARWIN for breakpoint trap selection. * Make breakpoints work when using UD2-BREAKPOINTS (tested on x86 and x86-64 linux). * This patch brought to you by lp#309067, which remains valid for three reasons: First, the test case is still disabled. Second, this only fixes for x86oids, not for PPC. And third, I didn't actually test this on a darwin system. --- diff --git a/NEWS b/NEWS index 5484d8b..741dbcc 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ changes relative to sbcl-1.0.36: * bug fix: bogus style warnings from certain (SETF SLOT-VALUE) and WITH-SLOTS usages during compilation. * bug fix: SB-C::CLASS-INFO now prints correctly (lp#514762) + * enhancement: Can now build with ud2 instead of int3 as trap instruction on + all x86oid platforms (except win32) with :UD2-BREAKPOINTS target feature. + * bug fix: Breakpoints now work when using ud2 instead of int3 as trap + instruction (tested on x86oid linux with ud2-breakpoints). changes in sbcl-1.0.36 relative to sbcl-1.0.35: * new feature: SB-EXT:TYPEXPAND-1, SB-EXT:TYPEXPAND, and diff --git a/base-target-features.lisp-expr b/base-target-features.lisp-expr index 60161d4..7bd7caf 100644 --- a/base-target-features.lisp-expr +++ b/base-target-features.lisp-expr @@ -237,6 +237,11 @@ ;; local storage. ; :restore-tls-segment-register-from-tls + ;; On some x86oid operating systems (darwin) SIGTRAP is not reliably + ;; delivered for the INT3 instruction, so we use the UD2 instruction + ;; which generates SIGILL instead. + ; :ud2-breakpoints + ;; Support for detection of unportable code (when applied to the ;; COMMON-LISP package, or SBCL-internal pacakges) or bad-neighbourly ;; code (when applied to user-level packages), relating to material diff --git a/make-config.sh b/make-config.sh index 5f250ec..9cbfa0d 100644 --- a/make-config.sh +++ b/make-config.sh @@ -240,10 +240,10 @@ case "$sbcl_os" in printf ' :bsd' >> $ltf printf ' :darwin' >> $ltf if [ $sbcl_arch = "x86" ]; then - printf ' :mach-exception-handler :sb-lutex :restore-fs-segment-register-from-tls' >> $ltf + printf ' :mach-exception-handler :sb-lutex :restore-fs-segment-register-from-tls :ud2-breakpoints' >> $ltf fi if [ $sbcl_arch = "x86-64" ]; then - printf ' :mach-exception-handler :sb-lutex' >> $ltf + printf ' :mach-exception-handler :sb-lutex :ud2-breakpoints' >> $ltf fi link_or_copy $sbcl_arch-darwin-os.h target-arch-os.h link_or_copy bsd-os.h target-os.h diff --git a/src/compiler/x86-64/insts.lisp b/src/compiler/x86-64/insts.lisp index 28ac794..b141f85 100644 --- a/src/compiler/x86-64/insts.lisp +++ b/src/compiler/x86-64/insts.lisp @@ -2900,13 +2900,13 @@ (defun break-control (chunk inst stream dstate) (declare (ignore inst)) (flet ((nt (x) (if stream (sb!disassem:note x dstate)))) - ;; FIXME: Make sure that BYTE-IMM-CODE is defined. The genesis - ;; map has it undefined; and it should be easier to look in the target - ;; Lisp (with (DESCRIBE 'BYTE-IMM-CODE)) than to definitively deduce - ;; from first principles whether it's defined in some way that genesis - ;; can't grok. - (case #!-darwin (byte-imm-code chunk dstate) - #!+darwin (word-imm-code chunk dstate) + ;; XXX: {BYTE,WORD}-IMM-CODE below is a macro defined by the + ;; DEFINE-INSTRUCTION-FORMAT for {BYTE,WORD}-IMM above. Due to + ;; the spectacular design for DEFINE-INSTRUCTION-FORMAT (involving + ;; a call to EVAL in order to define the macros at compile-time + ;; only) they do not even show up as symbols in the target core. + (case #!-ud2-breakpoints (byte-imm-code chunk dstate) + #!+ud2-breakpoints (word-imm-code chunk dstate) (#.error-trap (nt "error trap") (sb!disassem:handle-break-args #'snarf-error-junk stream dstate)) @@ -2928,17 +2928,17 @@ (define-instruction break (segment code) (:declare (type (unsigned-byte 8) code)) - #!-darwin (:printer byte-imm ((op #b11001100)) '(:name :tab code) - :control #'break-control) - #!+darwin (:printer word-imm ((op #b0000101100001111)) '(:name :tab code) - :control #'break-control) + #!-ud2-breakpoints (:printer byte-imm ((op #b11001100)) '(:name :tab code) + :control #'break-control) + #!+ud2-breakpoints (:printer word-imm ((op #b0000101100001111)) '(:name :tab code) + :control #'break-control) (:emitter - #!-darwin (emit-byte segment #b11001100) + #!-ud2-breakpoints (emit-byte segment #b11001100) ;; On darwin, trap handling via SIGTRAP is unreliable, therefore we ;; throw a sigill with 0x0b0f instead and check for this in the ;; SIGILL handler and pass it on to the sigtrap handler if ;; appropriate - #!+darwin (emit-word segment #b0000101100001111) + #!+ud2-breakpoints (emit-word segment #b0000101100001111) (emit-byte segment code))) (define-instruction int (segment number) diff --git a/src/compiler/x86-64/macros.lisp b/src/compiler/x86-64/macros.lisp index e591b24..675700f 100644 --- a/src/compiler/x86-64/macros.lisp +++ b/src/compiler/x86-64/macros.lisp @@ -236,7 +236,7 @@ ;;;; error code (defun emit-error-break (vop kind code values) (assemble () - #!-darwin + #!-ud2-breakpoints (inst int 3) ; i386 breakpoint instruction ;; On Darwin, we need to use #x0b0f instead of int3 in order ;; to generate a SIGILL instead of a SIGTRAP as darwin/x86 @@ -244,7 +244,7 @@ ;; handlers. Hopefully this will be fixed by Apple at a ;; later date. Do the same on x86-64 as we do on x86 until this gets ;; sorted out. - #!+darwin + #!+ud2-breakpoints (inst word #x0b0f) ;; The return PC points here; note the location for the debugger. (when vop diff --git a/src/compiler/x86/insts.lisp b/src/compiler/x86/insts.lisp index 4edb1a4..c7e3005 100644 --- a/src/compiler/x86/insts.lisp +++ b/src/compiler/x86/insts.lisp @@ -2249,8 +2249,8 @@ ;; Lisp (with (DESCRIBE 'BYTE-IMM-CODE)) than to definitively deduce ;; from first principles whether it's defined in some way that genesis ;; can't grok. - (case #!-darwin (byte-imm-code chunk dstate) - #!+darwin (word-imm-code chunk dstate) + (case #!-ud2-breakpoints (byte-imm-code chunk dstate) + #!+ud2-breakpoints (word-imm-code chunk dstate) (#.error-trap (nt "error trap") (sb!disassem:handle-break-args #'snarf-error-junk stream dstate)) @@ -2268,17 +2268,17 @@ (define-instruction break (segment code) (:declare (type (unsigned-byte 8) code)) - #!-darwin (:printer byte-imm ((op #b11001100)) '(:name :tab code) - :control #'break-control) - #!+darwin (:printer word-imm ((op #b0000101100001111)) '(:name :tab code) - :control #'break-control) + #!-ud2-breakpoints (:printer byte-imm ((op #b11001100)) '(:name :tab code) + :control #'break-control) + #!+ud2-breakpoints (:printer word-imm ((op #b0000101100001111)) '(:name :tab code) + :control #'break-control) (:emitter - #!-darwin (emit-byte segment #b11001100) + #!-ud2-breakpoints (emit-byte segment #b11001100) ;; On darwin, trap handling via SIGTRAP is unreliable, therefore we ;; throw a sigill with 0x0b0f instead and check for this in the ;; SIGILL handler and pass it on to the sigtrap handler if ;; appropriate - #!+darwin (emit-word segment #b0000101100001111) + #!+ud2-breakpoints (emit-word segment #b0000101100001111) (emit-byte segment code))) (define-instruction int (segment number) diff --git a/src/compiler/x86/macros.lisp b/src/compiler/x86/macros.lisp index af6f52d..18251b5 100644 --- a/src/compiler/x86/macros.lisp +++ b/src/compiler/x86/macros.lisp @@ -290,7 +290,7 @@ ;;;; error code (defun emit-error-break (vop kind code values) (assemble () - #!-darwin + #!-ud2-breakpoints (inst int 3) ; i386 breakpoint instruction ;; CLH 20060314 ;; On Darwin, we need to use #x0b0f instead of int3 in order @@ -298,7 +298,7 @@ ;; doesn't seem to be reliably firing SIGTRAP ;; handlers. Hopefully this will be fixed by Apple at a ;; later date. - #!+darwin + #!+ud2-breakpoints (inst word #x0b0f) ;; The return PC points here; note the location for the debugger. (when vop diff --git a/src/runtime/x86-64-arch.c b/src/runtime/x86-64-arch.c index f3f414b..0612ce9 100644 --- a/src/runtime/x86-64-arch.c +++ b/src/runtime/x86-64-arch.c @@ -31,6 +31,13 @@ #include "genesis/symbol.h" #define BREAKPOINT_INST 0xcc /* INT3 */ +#define UD2_INST 0x0b0f /* UD2 */ + +#ifndef LISP_FEATURE_UD2_BREAKPOINTS +#define BREAKPOINT_WIDTH 1 +#else +#define BREAKPOINT_WIDTH 2 +#endif unsigned long fast_random_state = 1; @@ -155,8 +162,14 @@ arch_install_breakpoint(void *pc) { unsigned int result = *(unsigned int*)pc; +#ifndef LISP_FEATURE_UD2_BREAKPOINTS *(char*)pc = BREAKPOINT_INST; /* x86 INT3 */ *((char*)pc+1) = trap_Breakpoint; /* Lisp trap code */ +#else + *(char*)pc = UD2_INST & 0xff; + *((char*)pc+1) = UD2_INST >> 8; + *((char*)pc+2) = trap_Breakpoint; +#endif return result; } @@ -166,6 +179,9 @@ arch_remove_breakpoint(void *pc, unsigned int orig_inst) { *((char *)pc) = orig_inst & 0xff; *((char *)pc + 1) = (orig_inst & 0xff00) >> 8; +#if BREAKPOINT_WIDTH > 1 + *((char *)pc + 2) = (orig_inst & 0xff0000) >> 16; +#endif } /* When single stepping, single_stepping holds the original instruction @@ -183,8 +199,7 @@ arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst) unsigned int *pc = (unsigned int*)(*os_context_pc_addr(context)); /* Put the original instruction back. */ - *((char *)pc) = orig_inst & 0xff; - *((char *)pc + 1) = (orig_inst & 0xff00) >> 8; + arch_remove_breakpoint(pc, orig_inst); #ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG /* Install helper instructions for the single step: @@ -209,14 +224,14 @@ arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst) void arch_handle_breakpoint(os_context_t *context) { - --*os_context_pc_addr(context); + *os_context_pc_addr(context) -= BREAKPOINT_WIDTH; handle_breakpoint(context); } void arch_handle_fun_end_breakpoint(os_context_t *context) { - --*os_context_pc_addr(context); + *os_context_pc_addr(context) -= BREAKPOINT_WIDTH; *os_context_pc_addr(context) = (unsigned long)handle_fun_end_breakpoint(context); } @@ -236,8 +251,7 @@ sigtrap_handler(int signal, siginfo_t *info, os_context_t *context) { unsigned int trap; - if (single_stepping && (signal==SIGTRAP)) - { + if (single_stepping) { #ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG /* Un-install single step helper instructions. */ *(single_stepping-3) = single_step_save1; @@ -247,12 +261,13 @@ sigtrap_handler(int signal, siginfo_t *info, os_context_t *context) *context_eflags_addr(context) ^= 0x100; #endif /* Re-install the breakpoint if possible. */ - if ((char *)*os_context_pc_addr(context) == - (char *)single_stepping + 1) { + if (((char *)*os_context_pc_addr(context) > + (char *)single_stepping) && + ((char *)*os_context_pc_addr(context) <= + (char *)single_stepping + BREAKPOINT_WIDTH)) { fprintf(stderr, "warning: couldn't reinstall breakpoint\n"); } else { - *((char *)single_stepping) = BREAKPOINT_INST; /* x86 INT3 */ - *((char *)single_stepping+1) = trap_Breakpoint; + arch_install_breakpoint(single_stepping); } single_stepping = NULL; @@ -278,8 +293,8 @@ sigill_handler(int signal, siginfo_t *siginfo, os_context_t *context) { /* Triggering SIGTRAP using int3 is unreliable on OS X/x86, so * we need to use illegal instructions for traps. */ -#if defined(LISP_FEATURE_DARWIN) && !defined(LISP_FEATURE_MACH_EXCEPTION_HANDLER) - if (*((unsigned short *)*os_context_pc_addr(context)) == 0x0b0f) { +#if defined(LISP_FEATURE_UD2_BREAKPOINTS) && !defined(LISP_FEATURE_MACH_EXCEPTION_HANDLER) + if (*((unsigned short *)*os_context_pc_addr(context)) == UD2_INST) { *os_context_pc_addr(context) += 2; return sigtrap_handler(signal, siginfo, context); } diff --git a/src/runtime/x86-64-assem.S b/src/runtime/x86-64-assem.S index ac42010..388c3bf 100644 --- a/src/runtime/x86-64-assem.S +++ b/src/runtime/x86-64-assem.S @@ -67,7 +67,7 @@ * for this instruction in the SIGILL handler and if we see it, we * advance the EIP by two bytes to skip over ud2 instruction and * call sigtrap_handler. */ -#if defined(LISP_FEATURE_DARWIN) +#if defined(LISP_FEATURE_UD2_BREAKPOINTS) #define TRAP ud2 #else #define TRAP int3 diff --git a/src/runtime/x86-arch.c b/src/runtime/x86-arch.c index b9b5250..b000e0f 100644 --- a/src/runtime/x86-arch.c +++ b/src/runtime/x86-arch.c @@ -30,6 +30,13 @@ #include "genesis/symbol.h" #define BREAKPOINT_INST 0xcc /* INT3 */ +#define UD2_INST 0x0b0f /* UD2 */ + +#ifndef LISP_FEATURE_UD2_BREAKPOINTS +#define BREAKPOINT_WIDTH 1 +#else +#define BREAKPOINT_WIDTH 2 +#endif unsigned long fast_random_state = 1; @@ -158,8 +165,14 @@ arch_install_breakpoint(void *pc) { unsigned int result = *(unsigned int*)pc; +#ifndef LISP_FEATURE_UD2_BREAKPOINTS *(char*)pc = BREAKPOINT_INST; /* x86 INT3 */ *((char*)pc+1) = trap_Breakpoint; /* Lisp trap code */ +#else + *(char*)pc = UD2_INST & 0xff; + *((char*)pc+1) = UD2_INST >> 8; + *((char*)pc+2) = trap_Breakpoint; +#endif return result; } @@ -169,6 +182,9 @@ arch_remove_breakpoint(void *pc, unsigned int orig_inst) { *((char *)pc) = orig_inst & 0xff; *((char *)pc + 1) = (orig_inst & 0xff00) >> 8; +#if BREAKPOINT_WIDTH > 1 + *((char *)pc + 2) = (orig_inst & 0xff0000) >> 16; +#endif } /* When single stepping, single_stepping holds the original instruction @@ -186,8 +202,7 @@ arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst) unsigned int *pc = (unsigned int*)(*os_context_pc_addr(context)); /* Put the original instruction back. */ - *((char *)pc) = orig_inst & 0xff; - *((char *)pc + 1) = (orig_inst & 0xff00) >> 8; + arch_remove_breakpoint(pc, orig_inst); #ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG /* Install helper instructions for the single step: @@ -222,11 +237,13 @@ restore_breakpoint_from_single_step(os_context_t * context) *context_eflags_addr(context) &= ~0x100; #endif /* Re-install the breakpoint if possible. */ - if (*os_context_pc_addr(context) == (int)single_stepping + 1) { + if (((char *)*os_context_pc_addr(context) > + (char *)single_stepping) && + ((char *)*os_context_pc_addr(context) <= + (char *)single_stepping + BREAKPOINT_WIDTH)) { fprintf(stderr, "warning: couldn't reinstall breakpoint\n"); } else { - *((char *)single_stepping) = BREAKPOINT_INST; /* x86 INT3 */ - *((char *)single_stepping+1) = trap_Breakpoint; + arch_install_breakpoint(single_stepping); } single_stepping = NULL; @@ -236,14 +253,14 @@ restore_breakpoint_from_single_step(os_context_t * context) void arch_handle_breakpoint(os_context_t *context) { - --*os_context_pc_addr(context); + *os_context_pc_addr(context) -= BREAKPOINT_WIDTH; handle_breakpoint(context); } void arch_handle_fun_end_breakpoint(os_context_t *context) { - --*os_context_pc_addr(context); + *os_context_pc_addr(context) -= BREAKPOINT_WIDTH; *os_context_pc_addr(context) = (int)handle_fun_end_breakpoint(context); } @@ -263,7 +280,7 @@ sigtrap_handler(int signal, siginfo_t *info, os_context_t *context) { unsigned int trap; - if (single_stepping && (signal==SIGTRAP)) { + if (single_stepping) { restore_breakpoint_from_single_step(context); return; } @@ -297,8 +314,8 @@ sigill_handler(int signal, siginfo_t *siginfo, os_context_t *context) { /* Triggering SIGTRAP using int3 is unreliable on OS X/x86, so * we need to use illegal instructions for traps. */ -#if defined(LISP_FEATURE_DARWIN) && !defined(LISP_FEATURE_MACH_EXCEPTION_HANDLER) - if (*((unsigned short *)*os_context_pc_addr(context)) == 0x0b0f) { +#if defined(LISP_FEATURE_UD2_BREAKPOINTS) && !defined(LISP_FEATURE_MACH_EXCEPTION_HANDLER) + if (*((unsigned short *)*os_context_pc_addr(context)) == UD2_INST) { *os_context_pc_addr(context) += 2; return sigtrap_handler(signal, siginfo, context); } diff --git a/src/runtime/x86-assem.S b/src/runtime/x86-assem.S index 37fcafa..8e0db32 100644 --- a/src/runtime/x86-assem.S +++ b/src/runtime/x86-assem.S @@ -79,7 +79,7 @@ * for this instruction in the SIGILL handler and if we see it, we * advance the EIP by two bytes to skip over ud2 instruction and * call sigtrap_handler. */ -#if defined(LISP_FEATURE_DARWIN) +#if defined(LISP_FEATURE_UD2_BREAKPOINTS) #define END() #define TRAP ud2 #else diff --git a/version.lisp-expr b/version.lisp-expr index a8629a1..54ad299 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.36.8" +"1.0.36.9"