[Git][cmucl/cmucl][master] 2 commits: Fix #97: Use UD1 instruction instead of INT3
Raymond Toy (@rtoy)
gitlab at common-lisp.net
Mon May 31 17:13:30 UTC 2021
Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
138199bd by Raymond Toy at 2021-05-31T17:13:13+00:00
Fix #97: Use UD1 instruction instead of INT3
- - - - -
94a45e3f by Raymond Toy at 2021-05-31T17:13:13+00:00
Merge branch 'issue-97-define-ud2-inst' into 'master'
Fix #97: Use UD1 instruction instead of INT3
Closes #97
See merge request cmucl/cmucl!72
- - - - -
9 changed files:
- src/code/x86-vm.lisp
- src/compiler/x86/insts.lisp
- src/compiler/x86/macros.lisp
- src/compiler/x86/system.lisp
- src/lisp/arch.h
- src/lisp/breakpoint.c
- src/lisp/x86-arch.c
- src/lisp/x86-arch.h
- src/lisp/x86-assem.S
Changes:
=====================================
src/code/x86-vm.lisp
=====================================
@@ -237,12 +237,20 @@
(with-alien ((scp (* unix:sigcontext) scp))
(let ((pc (sigcontext-program-counter scp)))
(declare (type system-area-pointer pc))
- ;; using INT3 the pc is .. INT3 <here> code length bytes...
- (let* ((length (sap-ref-8 pc 1))
+ ;; The pc should point to the start of the UD1 instruction. So
+ ;; we have something like:
+ ;;
+ ;; offset contents
+ ;; 0 UD1 (contains the trap code)
+ ;; 3 length
+ ;; 4... bytes
+ (let* ((length (sap-ref-8 pc 3))
(vector (make-array length :element-type '(unsigned-byte 8))))
(declare (type (unsigned-byte 8) length)
(type (simple-array (unsigned-byte 8) (*)) vector))
- (copy-from-system-area pc (* vm:byte-bits 2)
+ ;; Grab bytes after the length byte where the number of bytes is
+ ;; given by value of the length byte.
+ (copy-from-system-area pc (* vm:byte-bits 4)
vector (* vm:word-bits
vm:vector-data-offset)
(* length vm:byte-bits))
=====================================
src/compiler/x86/insts.lisp
=====================================
@@ -1779,7 +1779,7 @@
(bit-test-reg/mem 24
:default-printer '(:name :tab reg/mem ", " reg))
(prefix :field (byte 8 0) :value #b0001111)
- (op :field (byte 3 11))
+ (op :field (byte 8 8))
;;(test :fields (list (byte 2 14) (byte 3 8)))
(reg/mem :fields (list (byte 2 22) (byte 3 16))
:type 'reg/mem)
@@ -1788,22 +1788,22 @@
(imm))
(define-instruction bt (segment src index)
- (:printer bit-test-reg/mem ((op #b100)))
+ (:printer bit-test-reg/mem ((op #b10100011)))
(:emitter
(emit-bit-test-and-mumble segment src index #b100)))
(define-instruction btc (segment src index)
- (:printer bit-test-reg/mem ((op #b111)))
+ (:printer bit-test-reg/mem ((op #b10111011)))
(:emitter
(emit-bit-test-and-mumble segment src index #b111)))
(define-instruction btr (segment src index)
- (:printer bit-test-reg/mem ((op #b110)))
+ (:printer bit-test-reg/mem ((op #b10110011)))
(:emitter
(emit-bit-test-and-mumble segment src index #b110)))
(define-instruction bts (segment src index)
- (:printer bit-test-reg/mem ((op #b101)))
+ (:printer bit-test-reg/mem ((op #b10101011)))
(:emitter
(emit-bit-test-and-mumble segment src index #b101)))
@@ -2061,6 +2061,26 @@
(op :field (byte 8 0))
(code :field (byte 8 8)))
+
+;; The UD1 instruction. The mod bits of the mod r/m byte MUST be #b11
+;; so that the reg/mem field is actually a register. This is a hack
+;; to allow us to print out the reg/mem reg as a 32-bit reg.
+;;
+;; While the instruction looks like an ext-reg-reg/mem format with
+;; fixed width value of 1, it isn't because we need to disassemble the
+;; reg/mem field as a 32-bit reg. ext-reg-reg/mem needs a width prefix
+;; byte to specify that, and we definitely don't want that. Hence,
+;; use a special instruction format for the UD1 instruction.
+(disassem:define-instruction-format
+ (ud1 24 :default-printer '(:name :tab reg ", " reg/mem))
+ (prefix :field (byte 8 0) :value #b00001111)
+ (op :field (byte 8 8) :value #b10111001)
+ ;; The mod bits ensure that the reg/mem field is interpreted as a
+ ;; register, not memory.
+ (reg/mem :field (byte 3 16) :type 'word-reg)
+ (reg :field (byte 3 19) :type 'word-reg)
+ (mod :field (byte 2 22) :value #b11))
+
(defun snarf-error-junk (sap offset &optional length-only)
(let* ((length (system:sap-ref-8 sap offset))
(vector (make-array length :element-type '(unsigned-byte 8))))
@@ -2091,53 +2111,64 @@
(sc-offsets)
(lengths))))))))
-(defmacro break-cases (breaknum &body cases)
- (let ((bn-temp (gensym)))
- (collect ((clauses))
- (dolist (case cases)
- (clauses `((= ,bn-temp ,(car case)) ,@(cdr case))))
- `(let ((,bn-temp ,breaknum))
- (cond ,@(clauses))))))
-
-(defun break-control (chunk inst stream dstate)
+(defun ud1-control (chunk inst stream dstate)
(declare (ignore inst))
(flet ((nt (x) (if stream (disassem:note x dstate))))
- (case (byte-imm-code chunk dstate)
- (#.vm:error-trap
- (nt "Error trap")
- (disassem:handle-break-args #'snarf-error-junk stream dstate))
- (#.vm:cerror-trap
- (nt "Cerror trap")
- (disassem:handle-break-args #'snarf-error-junk stream dstate))
- (#.vm:breakpoint-trap
- (nt "Breakpoint trap"))
- (#.vm:pending-interrupt-trap
- (nt "Pending interrupt trap"))
- (#.vm:halt-trap
- (nt "Halt trap"))
- (#.vm:function-end-breakpoint-trap
- (nt "Function end breakpoint trap"))
- )))
-
-;; This is really the int3 instruction.
-(define-instruction break (segment code)
+ (let ((code (ldb (byte 6 16) chunk)))
+ (ecase code
+ (#.vm:error-trap
+ (nt #.(format nil "Trap ~D: Error trap" vm:error-trap))
+ (disassem:handle-break-args #'snarf-error-junk stream dstate))
+ (#.vm:cerror-trap
+ (nt #.(format nil "Trap ~D: Cerror trap" vm:cerror-trap))
+ (disassem:handle-break-args #'snarf-error-junk stream dstate))
+ (#.vm:pending-interrupt-trap
+ (nt #.(format nil "Trap ~D: Pending interrupt trap" vm:pending-interrupt-trap)))
+ (#.vm:halt-trap
+ (nt #.(format nil "Trap ~D: Halt trap" vm:halt-trap)))
+ (#.vm:function-end-breakpoint-trap
+ (nt #.(format nil "Trap ~D: Function end breakpoint trap"
+ vm:function-end-breakpoint-trap)))))))
+
+;; The ud1 instruction where we smash the code (trap type) into the
+;; low 6 bits of the mod r/m byte. The mod bits are set to #b11 to
+;; make sure the reg/mem part is interpreted to be a register and not
+;; memory.
+(define-instruction ud1 (segment code)
(:declare (type (unsigned-byte 8) code))
- (:printer byte-imm ((op #b11001100)) '(:name :tab code)
- :control #'break-control)
+ (:printer ud1 ((op #b10111001) (reg nil :type 'word-reg))
+ :default
+ :control #'ud1-control)
(:emitter
- (emit-byte segment #b11001100)
- (emit-byte segment code)))
+ ;; We should not be using the breakpoint trap with UD1 anymore.
+ ;; Breakpoint traps are handled in C now, using plain int3.
+ (assert (/= code vm:breakpoint-trap))
+ ;; Emit the bytes of the instruction.
+ (emit-byte segment #x0f)
+ (emit-byte segment #xb9)
+ (emit-mod-reg-r/m-byte segment
+ #b11
+ (ldb (byte 3 3) code)
+ (ldb (byte 3 0) code))))
+
+;; Handles both int and int3. To get int3 you have to say (inst int
+;; 3). But int3 should not be used in Lisp code. This is mainly so
+;; that int3 gets disassembled correctly if a breakpoint has been set
+;; in Lisp code. (But in general the disassembly will be messed up
+;; because the following byte will in general be the second byte of
+;; some instruction, and not the first byte of an instruction.)
(define-instruction int (segment number)
(:declare (type (unsigned-byte 8) number))
(:printer byte-imm ((op #b11001101)))
(:emitter
- (etypecase number
- ((member 3)
- (emit-byte segment #b11001100))
- ((unsigned-byte 8)
- (emit-byte segment #b11001101)
- (emit-byte segment number)))))
+ (emit-byte segment #b11001101)
+ (emit-byte segment number)))
+
+(define-instruction int3 (segment)
+ (:printer byte ((op #b11001100)))
+ (:emitter
+ (emit-byte segment #b11001100)))
(define-instruction into (segment)
(:printer byte ((op #b11001110)))
=====================================
src/compiler/x86/macros.lisp
=====================================
@@ -243,12 +243,10 @@
(defun emit-error-break (vop kind code values)
(let ((vector (gensym))
(length (gensym)))
- `((inst int 3) ; i386 breakpoint instruction
- ;; The return PC points here; note the location for the debugger.
- (let ((vop ,vop))
+ `((let ((vop ,vop))
(when vop
- (note-this-location vop :internal-error)))
- (inst byte ,kind) ; eg trap_Xyyy
+ (note-this-location vop :internal-error)))
+ (inst ud1 ,kind) ; eg trap_Xyyy
(let ((,vector (make-array 8 :element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(write-var-integer (error-number-or-lose ',code) ,vector)
@@ -342,7 +340,7 @@
(- other-pointer-type)))
0)
(inst jmp :eq ,label)
- (inst break pending-interrupt-trap)
+ (inst ud1 pending-interrupt-trap)
(emit-label ,label)))))
=====================================
src/compiler/x86/system.lisp
=====================================
@@ -284,11 +284,11 @@
(:policy :fast-safe)
(:translate unix::do-pending-interrupt)
(:generator 1
- (inst break pending-interrupt-trap)))
+ (inst ud1 pending-interrupt-trap)))
(define-vop (halt)
(:generator 1
- (inst break halt-trap)))
+ (inst ud1 halt-trap)))
(defknown float-wait () (values))
(define-vop (float-wait)
=====================================
src/lisp/arch.h
=====================================
@@ -13,15 +13,39 @@
extern char *arch_init(fpu_mode_t);
+/*
+ * Skip over the trap instructions for an error trap and also skip
+ * over anly following bytes used to encode information for an
+ * error-trap or cerror-trap. The PC in the context is set to address
+ * just past the trap instruction and data bytes (if any).
+ */
extern void arch_skip_instruction(os_context_t * scp);
+
extern boolean arch_pseudo_atomic_atomic(os_context_t * scp);
extern void arch_set_pseudo_atomic_interrupted(os_context_t * scp);
extern os_vm_address_t arch_get_bad_addr(HANDLER_ARGS);
extern unsigned char *arch_internal_error_arguments(os_context_t * scp);
+
+/*
+ * Install an architecture-dependent breakpoint instruction at the
+ * given PC address. This also returns the bytes that were
+ * overwritten by the breakpoint instruction so that the original
+ * instruction can be restored once the breakpoint has been handled.
+ */
extern unsigned long arch_install_breakpoint(void *pc);
+
extern void arch_remove_breakpoint(void *pc, unsigned long orig_inst);
extern void arch_install_interrupt_handlers(void);
+
+/*
+ * This is called when we need to continue after a breakpoint. The
+ * original instruction in |orig_inst| is put back. Then things are
+ * set up so that we can run again and after this instruction is run,
+ * we trap again so that the original breakpoint can be replaced. How
+ * this is done is architecture-dependent.
+ */
extern void arch_do_displaced_inst(os_context_t * scp, unsigned long orig_inst);
+
extern lispobj funcall0(lispobj function);
extern lispobj funcall1(lispobj function, lispobj arg0);
extern lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1);
=====================================
src/lisp/breakpoint.c
=====================================
@@ -192,6 +192,8 @@ compute_offset(os_context_t * scp, lispobj code, boolean function_end)
static int
compute_offset(os_context_t * scp, lispobj code, boolean function_end)
{
+ DPRINTF(debug_handlers, (stderr, "compute_offset: code = 0x%lx\n", code));
+
if (code == NIL)
return 0;
else {
@@ -206,11 +208,20 @@ compute_offset(os_context_t * scp, lispobj code, boolean function_end)
code_start = (unsigned long) codeptr
+ HeaderValue(codeptr->header) * sizeof(lispobj);
+
+ DPRINTF(debug_handlers,
+ (stderr, "compute_offset: pc = 0x%lx, code_start = 0x%lx\n",
+ pc, code_start));
+
if (pc < code_start)
return 0;
else {
int offset = pc - code_start;
+ DPRINTF(debug_handlers,
+ (stderr, "compute_offset: offset %d, size = %ld\n",
+ offset, codeptr->code_size));
+
if (offset >= codeptr->code_size) {
return 0;
} else {
@@ -250,6 +261,10 @@ handle_breakpoint(int signal, int subcode, os_context_t * scp)
code = find_code(scp);
+ DPRINTF(debug_handlers,
+ (stderr, "handle breakpoint: offset %d\n",
+ compute_offset(scp, code, 0)));
+
/*
* Don't disallow recursive breakpoint traps. Otherwise, we can't
* use debugger breakpoints anywhere in here.
=====================================
src/lisp/x86-arch.c
=====================================
@@ -23,7 +23,19 @@
#define BREAKPOINT_INST 0xcc /* INT3 */
-unsigned long fast_random_state = 1;
+/*
+ * The first two bytes of the UD1 instruction. The mod r/m byte isn't
+ * included here.
+ */
+static const unsigned char ud1[] = {0x0f, 0xb9};
+
+
+/*
+ * Set to positive value to enabled debug prints related to the sigill
+ * and sigtrap handlers. Also enables prints related to handling of
+ * breakpoints.
+ */
+unsigned int debug_handlers = 0;
#if defined(SOLARIS)
/*
@@ -129,32 +141,42 @@ arch_init(fpu_mode_t mode)
/*
- * Assuming we get here via an INT3 xxx instruction, the PC now
- * points to the interrupt code (lisp value) so we just move past
- * it. Skip the code, then if the code is an error-trap or
- * Cerror-trap then skip the data bytes that follow.
+ * Skip the UD1 instruction, and any data bytes associated with the
+ * trap.
*/
-
void
arch_skip_instruction(os_context_t * context)
{
int vlen, code;
- DPRINTF(0, (stderr, "[arch_skip_inst at %lx>]\n", SC_PC(context)));
+ DPRINTF(debug_handlers,
+ (stderr, "[arch_skip_inst at %lx>]\n", SC_PC(context)));
+
+ /* Get the address of the beginning of the UD1 instruction */
+ char* pc = (char *) SC_PC(context);
+
+ /*
+ * Skip over the part of the UD1 inst (0x0f, 0xb9) so we can get to the mod r/m byte
+ */
+ pc += sizeof(ud1);
+
+ code = *pc++;
+ SC_PC(context) = (unsigned long) pc;
- /* Get and skip the lisp error code. */
- code = *(char *) SC_PC(context)++;
switch (code) {
case trap_Error:
case trap_Cerror:
/* Lisp error arg vector length */
- vlen = *(char *) SC_PC(context)++;
+ vlen = *pc++;
+ SC_PC(context) = (unsigned long) pc;
+
/* Skip lisp error arg data bytes */
- while (vlen-- > 0)
- SC_PC(context)++;
+ SC_PC(context) = (unsigned long) (pc + vlen);
break;
case trap_Breakpoint:
+ lose("Unexpected breakpoint trap in arch_skip_instruction\n");
+ break;
case trap_FunctionEndBreakpoint:
break;
@@ -168,7 +190,8 @@ arch_skip_instruction(os_context_t * context)
break;
}
- DPRINTF(0, (stderr, "[arch_skip_inst resuming at %lx>]\n", SC_PC(context)));
+ DPRINTF(debug_handlers,
+ (stderr, "[arch_skip_inst resuming at %lx>]\n", SC_PC(context)));
}
unsigned char *
@@ -191,13 +214,19 @@ arch_set_pseudo_atomic_interrupted(os_context_t * context)
+/*
+ * Installs a breakpoint (INT3) at |pc|. We return the byte that was
+ * replaced by the int3 instruction.
+ */
unsigned long
arch_install_breakpoint(void *pc)
{
- unsigned long result = *(unsigned long *) pc;
+ unsigned long result = *(unsigned char *) pc;
+ *(unsigned char *) pc = BREAKPOINT_INST;
- *(char *) pc = BREAKPOINT_INST; /* x86 INT3 */
- *((char *) pc + 1) = trap_Breakpoint; /* Lisp trap code */
+ DPRINTF(debug_handlers,
+ (stderr, "arch_install_breakpoint at %p, old code = 0x%lx\n",
+ pc, result));
return result;
}
@@ -205,8 +234,14 @@ arch_install_breakpoint(void *pc)
void
arch_remove_breakpoint(void *pc, unsigned long orig_inst)
{
- *((char *) pc) = orig_inst & 0xff;
- *((char *) pc + 1) = (orig_inst & 0xff00) >> 8;
+ DPRINTF(debug_handlers,
+ (stderr, "arch_remove_breakpoint: %p orig %lx\n",
+ pc, orig_inst));
+
+ /*
+ * Just restore the byte from orig_inst.
+ */
+ *(unsigned char *) pc = orig_inst & 0xff;
}
@@ -224,20 +259,44 @@ unsigned int single_step_save2;
unsigned int single_step_save3;
#endif
+/*
+ * This is called when we need to continue after a breakpoint. This
+ * works by putting the original byte back into the code, and then
+ * enabling single-step mode to step one instruction. When we return,
+ * the instruction will get run and a sigtrap will get triggered when
+ * the one instruction is done.
+ *
+ * TODO: Be more like other archs where the original inst is put back,
+ * and the next inst is replaced with a afterBreakpoint trap. When we
+ * run, the afterBreakpoint trap is hit at the next instruction and
+ * then we can put back the original breakpoint and replace the
+ * afterBreakpoint trap with the original inst there too.
+ *
+ * For x86, this means computing how many bytes are used in the
+ * current instruction, and then placing an int3 (or maybe ud1) after
+ * it.
+ */
void
arch_do_displaced_inst(os_context_t * context, unsigned long orig_inst)
{
- unsigned int *pc = (unsigned int *) SC_PC(context);
+ unsigned char *pc = (unsigned char *) SC_PC(context);
+ DPRINTF(debug_handlers,
+ (stderr, "arch_do_displaced_inst: pc %p orig_inst %lx\n",
+ pc, orig_inst));
+
/*
* Put the original instruction back.
*/
- *((char *) pc) = orig_inst & 0xff;
- *((char *) pc + 1) = (orig_inst & 0xff00) >> 8;
+ *pc = orig_inst & 0xff;
+ /*
+ * If we have the SC_EFLAGS macro, we can enable single-stepping
+ * by setting the bit. Otherwise, we need a more complicated way
+ * of enabling single-stepping.
+ */
#ifdef SC_EFLAGS
- /* Enable single-stepping */
SC_EFLAGS(context) |= 0x100;
#else
@@ -274,53 +333,31 @@ arch_do_displaced_inst(os_context_t * context, unsigned long orig_inst)
}
+/*
+ * Handles the ud1 instruction from lisp that is used to signal
+ * errors. In particular, this does not handle the breakpoint traps.
+ */
void
-sigtrap_handler(HANDLER_ARGS)
+sigill_handler(HANDLER_ARGS)
{
unsigned int trap;
os_context_t* os_context = (os_context_t *) context;
-#if 0
- fprintf(stderr, "x86sigtrap: %8x %x\n",
- SC_PC(os_os_context), *(unsigned char *) (SC_PC(os_context) - 1));
- fprintf(stderr, "sigtrap(%d %d %x)\n", signal, CODE(code), os_context);
-#endif
- if (single_stepping && (signal == SIGTRAP)) {
-#if 0
- fprintf(stderr, "* Single step trap %p\n", single_stepping);
-#endif
-
-#ifdef SC_EFLAGS
- /* Disable single-stepping */
- SC_EFLAGS(os_context) ^= 0x100;
-#else
- /* Un-install single step helper instructions. */
- *(single_stepping - 3) = single_step_save1;
- *(single_stepping - 2) = single_step_save2;
- *(single_stepping - 1) = single_step_save3;
- DPRINTF(0, (stderr, "Uninstalling helper instructions\n"));
-#endif
-
- /*
- * Re-install the breakpoint if possible.
- */
- if ((int) SC_PC(os_context) == (int) single_stepping + 1)
- fprintf(stderr, "* Breakpoint not re-install\n");
- else {
- char *ptr = (char *) single_stepping;
-
- ptr[0] = BREAKPOINT_INST; /* x86 INT3 */
- ptr[1] = trap_Breakpoint;
- }
-
- single_stepping = NULL;
- return;
- }
+ DPRINTF(debug_handlers,
+ (stderr,"sigill: fp=%lx sp=%lx pc=%lx { %x, %x, %x, %x, %x }\n",
+ SC_REG(context, reg_FP),
+ SC_REG(context, reg_SP),
+ SC_PC(context),
+ *((unsigned char*)SC_PC(context) + 0), /* 0x0F */
+ *((unsigned char*)SC_PC(context) + 1), /* 0x0B */
+ *((unsigned char*)SC_PC(context) + 2),
+ *((unsigned char*)SC_PC(context) + 3),
+ *((unsigned char*)SC_PC(context) + 4)));
+
/* This is just for info in case monitor wants to print an approx */
current_control_stack_pointer = (unsigned long *) SC_SP(os_context);
-
/*
* In many places in the switch below, we eventually throw instead
* of returning from the signal handler. So, just in case, set
@@ -329,86 +366,181 @@ sigtrap_handler(HANDLER_ARGS)
RESTORE_FPU(os_context);
/*
- * On entry %eip points just after the INT3 byte and aims at the
- * 'kind' value (eg trap_Cerror). For error-trap and Cerror-trap a
- * number of bytes will follow, the first is the length of the byte
- * arguments to follow.
+ * On entry %eip points just to the beginning of the UD1
+ * instruction. For error-trap and cerror-trap a number of bytes
+ * will follow, the first is the length of the byte arguments to
+ * follow.
*/
- trap = *(unsigned char *) SC_PC(os_context);
+ DPRINTF(debug_handlers,
+ (stderr, "pc %x\n", *(unsigned short *)SC_PC(context)));
- switch (trap) {
+ /*
+ * If the trapping instruction is UD1, assume it's a Lisp trap
+ * that we handle here. Otherwise, just call interrupt_handle_now
+ * for other cases.
+ */
+ if (memcmp((void *)SC_PC(context), ud1, sizeof(ud1)) == 0) {
+ /*
+ * This must match what the lisp code is doing. The trap
+ * number is placed in the low 6-bits of the 3rd byte of the
+ * instruction.
+ */
+ trap = *(((char *)SC_PC(context)) + 2) & 0x3f;
+
+ DPRINTF(debug_handlers, (stderr, "code = %x\n", trap));
+
+ switch (trap) {
case trap_PendingInterrupt:
- DPRINTF(0, (stderr, "<trap Pending Interrupt.>\n"));
- arch_skip_instruction(os_context);
- interrupt_handle_pending(os_context);
- break;
+ DPRINTF(debug_handlers, (stderr, "<trap Pending Interrupt.>\n"));
+ arch_skip_instruction(os_context);
+ interrupt_handle_pending(os_context);
+ break;
case trap_Halt:
- {
- FPU_STATE(fpu_state);
- save_fpu_state(fpu_state);
+ {
+ FPU_STATE(fpu_state);
+ save_fpu_state(fpu_state);
- fake_foreign_function_call(os_context);
- lose("%%primitive halt called; the party is over.\n");
- undo_fake_foreign_function_call(os_context);
+ fake_foreign_function_call(os_context);
+ lose("%%primitive halt called; the party is over.\n");
+ undo_fake_foreign_function_call(os_context);
- restore_fpu_state(fpu_state);
- arch_skip_instruction(os_context);
- break;
- }
+ restore_fpu_state(fpu_state);
+ arch_skip_instruction(os_context);
+ break;
+ }
case trap_Error:
case trap_Cerror:
- DPRINTF(0, (stderr, "<trap Error %x>\n", CODE(code)));
- interrupt_internal_error(signal, code, os_context, CODE(code) == trap_Cerror);
- break;
+ DPRINTF(debug_handlers, (stderr, "<trap Error %x>\n", CODE(code)));
+ interrupt_internal_error(signal, code, os_context, CODE(code) == trap_Cerror);
+ break;
case trap_Breakpoint:
-#if 0
- fprintf(stderr, "*C break\n");
-#endif
- SC_PC(os_context) -= 1;
-
- handle_breakpoint(signal, CODE(code), os_context);
-#if 0
- fprintf(stderr, "*C break return\n");
-#endif
- break;
+ lose("Unexpected breakpoint trap in sigill-hander.\n");
+ break;
case trap_FunctionEndBreakpoint:
- SC_PC(os_context) -= 1;
- SC_PC(os_context) =
- (int) handle_function_end_breakpoint(signal, CODE(code), os_context);
- break;
+ SC_PC(os_context) =
+ (int) handle_function_end_breakpoint(signal, CODE(code), os_context);
+ break;
#ifdef trap_DynamicSpaceOverflowWarning
case trap_DynamicSpaceOverflowWarning:
- interrupt_handle_space_overflow(SymbolFunction
- (DYNAMIC_SPACE_OVERFLOW_WARNING_HIT),
- os_context);
- break;
+ interrupt_handle_space_overflow(SymbolFunction
+ (DYNAMIC_SPACE_OVERFLOW_WARNING_HIT),
+ os_context);
+ break;
#endif
#ifdef trap_DynamicSpaceOverflowError
case trap_DynamicSpaceOverflowError:
- interrupt_handle_space_overflow(SymbolFunction
- (DYNAMIC_SPACE_OVERFLOW_ERROR_HIT),
- os_context);
- break;
+ interrupt_handle_space_overflow(SymbolFunction
+ (DYNAMIC_SPACE_OVERFLOW_ERROR_HIT),
+ os_context);
+ break;
#endif
default:
- DPRINTF(0,
- (stderr, "[C--trap default %d %d %p]\n", signal, CODE(code),
- os_context));
- interrupt_handle_now(signal, code, os_context);
- break;
+ DPRINTF(debug_handlers,
+ (stderr, "[C--trap default %d %d %p]\n", signal, CODE(code),
+ os_context));
+ interrupt_handle_now(signal, code, os_context);
+ break;
+ }
+ } else {
+ interrupt_handle_now(signal, code, os_context);
+ }
+}
+
+/*
+ * Handles the breakpoint trap (int3) and also single-stepping
+ */
+void
+sigtrap_handler(HANDLER_ARGS)
+{
+ os_context_t* os_context = (os_context_t *) context;
+
+ DPRINTF(debug_handlers,
+ (stderr,"sigtrap: fp=%lx sp=%lx pc=%lx { %x, %x, %x, %x, %x }\n",
+ SC_REG(context, reg_FP),
+ SC_REG(context, reg_SP),
+ SC_PC(context),
+ *((unsigned char*)SC_PC(context) + 0), /* 0x0F */
+ *((unsigned char*)SC_PC(context) + 1), /* 0x0B */
+ *((unsigned char*)SC_PC(context) + 2),
+ *((unsigned char*)SC_PC(context) + 3),
+ *(unsigned char*)(SC_PC(context) + 4)));
+
+ if (single_stepping) {
+ /*
+ * We were single-stepping so we now need to disable
+ * single-stepping. We want to put back the breakpoint (int3)
+ * instruction so that the next time the breakpoint will be
+ * hit again as expected.
+ */
+ DPRINTF(debug_handlers, (stderr, "* Single step trap %p\n", single_stepping));
+
+#ifdef SC_EFLAGS
+ /* Disable single-stepping */
+ SC_EFLAGS(os_context) ^= 0x100;
+#else
+ /* Un-install single step helper instructions. */
+ *(single_stepping - 3) = single_step_save1;
+ *(single_stepping - 2) = single_step_save2;
+ *(single_stepping - 1) = single_step_save3;
+ DPRINTF(0, (stderr, "Uninstalling helper instructions\n"));
+#endif
+
+ /*
+ * Re-install the breakpoint if possible.
+ */
+ DPRINTF(debug_handlers,
+ (stderr, "* Maybe reinstall breakpoint for pc %p with single_stepping %p\n",
+ (void*) SC_PC(os_context), single_stepping));
+
+ /*
+ * Lose if single-stepping didn't move us past where the
+ * breakpoint instruction was inserted.
+ */
+ if ((unsigned long) SC_PC(os_context) <= (unsigned long) single_stepping) {
+ lose("Single-stepping did not advance past the breakpoint at %p\n",
+ single_stepping);
+ }
+
+ /*
+ * Put back the breakpoint since we skipped over it.
+ */
+ char *ptr = (char *) single_stepping;
+ ptr[0] = BREAKPOINT_INST; /* x86 INT3 */
+
+ single_stepping = NULL;
+ return;
}
+
+ /*
+ * We weren't single-stepping, so this we've just hit the breakpoint (int3). Handle it.
+ */
+ DPRINTF(debug_handlers, (stderr, "*C break\n"));
+
+ /*
+ * The int3 instruction causes a trap that leaves us just after
+ * the instruction. Backup one so we're at the beginning. This
+ * is really important so that when we handle the breakpoint, the
+ * offset of the instruction matches where Lisp thinks the
+ * breakpoint was placed.
+ */
+ SC_PC(os_context) -= 1;
+
+ handle_breakpoint(signal, CODE(code), os_context);
+
+ DPRINTF(debug_handlers, (stderr, "*C break return\n"));
}
+
void
arch_install_interrupt_handlers(void)
{
- interrupt_install_low_level_handler(SIGILL, sigtrap_handler);
+ interrupt_install_low_level_handler(SIGILL, sigill_handler);
interrupt_install_low_level_handler(SIGTRAP, sigtrap_handler);
}
=====================================
src/lisp/x86-arch.h
=====================================
@@ -11,6 +11,13 @@
extern int arch_support_sse2(void);
extern boolean os_support_sse2(void);
+/*
+ * Set to non-zero to enable debug prints for debugging the sigill and
+ * sigtrap handlers and for debugging breakpoints.
+ */
+extern unsigned int debug_handlers;
+
+
/*
* Define macro to allocate a local array of the appropriate size
* where the fpu state can be stored.
=====================================
src/lisp/x86-assem.S
=====================================
@@ -18,6 +18,21 @@
#include "internals.h"
#include "lispregs.h"
+/*
+ * Emit the appropriate instruction used for implementing traps.
+ * Currently, this is the UD1 instruction. However, it make it
+ * easy to add the trap code, use a sequence of bytes. The code
+ * is smashed into the mod r/m byte with the mod bits set to
+ * #b11. This MUST be coordinated with the Lisp code and the C
+ * code.
+ *
+ * Also, clang doesn't recognize the ud1 instruction.
+ */
+#define UD1(code) \
+ .byte 0x0f ; \
+ .byte 0xb9 ; \
+ .byte 0xc0 + code
+
/* Minimize conditionalization for different OS naming schemes */
#ifdef DARWIN
#define GNAME(var) _##var
@@ -244,8 +259,7 @@ ENDFUNC(sse_restore)
* The undefined-function trampoline.
*/
FUNCDEF(undefined_tramp)
- INT3
- .byte trap_Error
+ UD1(trap_Error)
/* Number of argument bytes */
.byte 2
.byte UNDEFINED_SYMBOL_ERROR
@@ -286,32 +300,28 @@ multiple_value_return:
.globl GNAME(function_end_breakpoint_trap)
GNAME(function_end_breakpoint_trap):
- INT3
- .byte trap_FunctionEndBreakpoint
+ UD1(trap_FunctionEndBreakpoint)
hlt # Should never return here.
+ENDFUNC(function_end_breakpoint_trap)
.globl GNAME(function_end_breakpoint_end)
GNAME(function_end_breakpoint_end):
-
FUNCDEF(do_pending_interrupt)
- INT3
- .byte trap_PendingInterrupt
+ UD1(trap_PendingInterrupt)
ret
ENDFUNC(do_pending_interrupt)
#ifdef trap_DynamicSpaceOverflowError
FUNCDEF(do_dynamic_space_overflow_error)
- INT3
- .byte trap_DynamicSpaceOverflowError
+ UD1(trap_DynamicSpaceOverflowError)
ret
ENDFUNC(do_dynamic_space_overflow_error)
#endif
#ifdef trap_DynamicSpaceOverflowWarning
FUNCDEF(do_dynamic_space_overflow_warning)
- INT3
- .byte trap_DynamicSpaceOverflowWarning
+ UD1(trap_DynamicSpaceOverflowWarning)
ret
ENDFUNC(do_dynamic_space_overflow_warning)
#endif
@@ -493,8 +503,7 @@ FUNCDEF(undefined_foreign_symbol_trap)
movl 8(%ebp),%eax
/* Now trap to Lisp */
- INT3
- .byte trap_Error
+ UD1(trap_Error)
/* Number of argument bytes */
.byte 2
.byte UNDEFINED_FOREIGN_SYMBOL_ERROR
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/2589cd0c29215133eac897d10f6f880d76bc1b75...94a45e3f8478710d65d84e292f58083ad394d473
--
View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/2589cd0c29215133eac897d10f6f880d76bc1b75...94a45e3f8478710d65d84e292f58083ad394d473
You're receiving this email because of your account on gitlab.common-lisp.net.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/cmucl-cvs/attachments/20210531/05fb1c6c/attachment-0001.html>
More information about the cmucl-cvs
mailing list