From 3edae40b72dd0359018c89bc244fb5242c8b3a4e Mon Sep 17 00:00:00 2001 From: dtc <dtc> Date: Tue, 25 Nov 1997 15:53:32 +0000 Subject: [PATCH] Breakpoint and single stepping support for the x86 port. This has been tested in the experimental code and works on both FreeBSD and Linux. Plus a few improvements to the interrupt safety for the x86 port. --- lisp/breakpoint.c | 51 +++++++++++++++-- lisp/x86-arch.c | 143 +++++++++++++++++++++++++--------------------- lisp/x86-assem.S | 65 ++++++++++++--------- 3 files changed, 162 insertions(+), 97 deletions(-) diff --git a/lisp/breakpoint.c b/lisp/breakpoint.c index cb9e84a55..06e6db9c0 100644 --- a/lisp/breakpoint.c +++ b/lisp/breakpoint.c @@ -1,6 +1,6 @@ /* - $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/breakpoint.c,v 1.6 1994/10/27 17:13:54 ram Exp $ + $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/breakpoint.c,v 1.7 1997/11/25 15:53:30 dtc Exp $ This code was written as part of the CMU Common Lisp project at Carnegie Mellon University, and has been placed in the public domain. @@ -47,12 +47,13 @@ void breakpoint_remove(lispobj code_obj, int pc_offset, void breakpoint_do_displaced_inst(struct sigcontext *scp, unsigned long orig_inst) { -#if !defined(hpux) && !defined(irix) +#if !defined(hpux) && !defined(irix) && !defined(i386) undo_fake_foreign_function_call(scp); #endif arch_do_displaced_inst(scp, orig_inst); } +#ifndef i386 static lispobj find_code(struct sigcontext *scp) { #ifdef reg_CODE @@ -71,6 +72,18 @@ static lispobj find_code(struct sigcontext *scp) return NIL; #endif } +#endif + +#ifdef i386 +static lispobj find_code(struct sigcontext *scp) +{ + lispobj codeptr = component_ptr_from_pc(SC_PC(scp)); + + if (codeptr==NULL) + return NIL; + return (codeptr+type_OtherPointer); +} +#endif static int compute_offset(struct sigcontext *scp, lispobj code) { @@ -101,15 +114,22 @@ static int compute_offset(struct sigcontext *scp, lispobj code) void handle_breakpoint(int signal, int subcode, struct sigcontext *scp) { - lispobj code; + lispobj code, scp_sap=alloc_sap(scp); fake_foreign_function_call(scp); code = find_code(scp); + +#ifdef i386 + /* Don't disallow recursive breakpoint traps. Otherwise, we can't */ + /* use debugger breakpoints anywhere in here. */ + sigsetmask(scp->sc_mask); +#endif + funcall3(SymbolFunction(HANDLE_BREAKPOINT), compute_offset(scp, code), code, - alloc_sap(scp)); + scp_sap); undo_fake_foreign_function_call(scp); } @@ -117,7 +137,7 @@ void handle_breakpoint(int signal, int subcode, struct sigcontext *scp) void *handle_function_end_breakpoint(int signal, int subcode, struct sigcontext *scp) { - lispobj code, lra; + lispobj code, lra, scp_sap=alloc_sap(scp); struct code *codeptr; fake_foreign_function_call(scp); @@ -125,10 +145,16 @@ void *handle_function_end_breakpoint(int signal, int subcode, code = find_code(scp); codeptr = (struct code *)PTR(code); +#ifdef i386 + /* Don't disallow recursive breakpoint traps. Otherwise, we can't */ + /* use debugger breakpoints anywhere in here. */ + sigsetmask(scp->sc_mask); +#endif + funcall3(SymbolFunction(HANDLE_BREAKPOINT), compute_offset(scp, code), code, - alloc_sap(scp)); + scp_sap); lra = codeptr->constants[REAL_LRA_SLOT]; #ifdef reg_CODE @@ -138,5 +164,18 @@ void *handle_function_end_breakpoint(int signal, int subcode, undo_fake_foreign_function_call(scp); +#ifdef i386 + /* On the x86 the saved lra is a SAP; extract the return + address. */ + if (!Pointerp(lra) || !(LowtagOf(lra)==type_OtherPointer)) + fprintf(stderr,"* Return address not a SAP!\n"); + { + struct sap *sap = (struct sap *)PTR(lra); + if (TypeOf(sap->header)!=type_Sap) + fprintf(stderr,"* Return address not a SAP!\n"); + return (void *)(sap->pointer); + } +#else return (void *)(lra-type_OtherPointer+sizeof(lispobj)); +#endif } diff --git a/lisp/x86-arch.c b/lisp/x86-arch.c index 4762c0a30..4963d773d 100644 --- a/lisp/x86-arch.c +++ b/lisp/x86-arch.c @@ -79,9 +79,6 @@ struct sigcontext *context; /* Skip lisp error arg data bytes */ while(vlen-- > 0) (char*)context->sc_pc++; - /* Align 2 */ - /* (char*)context->sc_pc += 0x3; - context->sc_pc &= ~0x03; */ break; case trap_Breakpoint: /* Not tested */ @@ -94,7 +91,7 @@ struct sigcontext *context; break; default: - DPRINTF(1,(stderr,"[arch_skip_inst invalid code %d\n]\n",code)); + fprintf(stderr,"[arch_skip_inst invalid code %d\n]\n",code); break; } @@ -124,88 +121,117 @@ arch_set_pseudo_atomic_interrupted(struct sigcontext *context) unsigned long arch_install_breakpoint(void *pc) { - char*ptr = (char*)pc; - unsigned long result = *(unsigned long*)ptr; - *ptr++ = BREAKPOINT_INST; /* x86 INT3 */ - *ptr++ = trap_Breakpoint; /* Lisp trap code */ - *ptr++ = 1; /* vector length */ - *ptr++ = 0; /* junk data */ - - os_flush_icache((os_vm_address_t)pc, sizeof(unsigned long)); + unsigned long result = *(unsigned long*)pc; + *(char*)pc = BREAKPOINT_INST; /* x86 INT3 */ + *((char*)pc+1) = trap_Breakpoint; /* Lisp trap code */ + return result; } void arch_remove_breakpoint(void *pc, unsigned long orig_inst) { - unsigned int *ptr=(unsigned int*)pc; - *ptr = orig_inst; - os_flush_icache((os_vm_address_t)pc, sizeof(unsigned long)); + *((char *)pc) = orig_inst & 0xff; + *((char *)pc + 1) = (orig_inst & 0xff00) >> 8; } -static unsigned int *skipped_break_addr, displaced_after_inst, - after_breakpoint; -static int orig_sigmask; - -unsigned int -emulate_branch(struct sigcontext *context,unsigned long orig_inst) -{ - /* This has to be invented for the X86. Maybe figure out how - * to use the trace trap. Shouldn't be hard. Gdb does it. - */ - int next_pc = context->sc_pc; - return next_pc; -} #ifdef __linux__ _syscall1(int,sigreturn,struct sigcontext *,context) #endif +/* When single stepping single_stepping holds the original instruction + pc location. */ +unsigned int *single_stepping=NULL; +#ifndef __linux__ +unsigned int single_step_save1; +unsigned int single_step_save2; +unsigned int single_step_save3; +#endif + void -arch_do_displaced_inst(struct sigcontext *context, - unsigned long orig_inst) +arch_do_displaced_inst(struct sigcontext *context, unsigned long orig_inst) { - /* Ditto the above for X86. - */ unsigned int *pc = (unsigned int*)context->sc_pc; - unsigned int *next_pc; - unsigned int next_inst; - DPRINTF(0,(stderr,"[arch_do_displaced_inst %x %x NOT YET!]\n", - context,orig_inst)); - sigreturn(context); + unsigned int flags = context->sc_efl; + + /* Put the original instruction back. */ + *((char *)pc) = orig_inst & 0xff; + *((char *)pc + 1) = (orig_inst & 0xff00) >> 8; + +#ifdef __linux__ + context->eflags |= 0x100; +#else + /* Install helper instructions for the single step: + pushf; or [esp],0x100; popf. */ + single_step_save1 = *(pc-3); + single_step_save2 = *(pc-2); + single_step_save3 = *(pc-1); + *(pc-3) = 0x9c909090; + *(pc-2) = 0x00240c81; + *(pc-1) = 0x9d000001; +#endif + + single_stepping=(unsigned int*)pc; + +#ifndef __linux__ + (unsigned int*)context->sc_pc = ((char *)pc-9); +#endif } -#define AfterBreakpoint 100 /* pfw - what is this?? */ void sigtrap_handler(HANDLER_ARGS) { + unsigned int trap; + #ifdef __linux__ GET_CONTEXT #endif + /* + fprintf(stderr,"x86sigtrap: %8x %x\n", + context->sc_pc, *(unsigned char *)(context->sc_pc-1)); + fprintf(stderr,"sigtrap(%d %d %x)\n",signal,code,context);*/ -#ifdef __linux__ - __setfpucw(contextstruct.fpstate->cw); + if (single_stepping && (signal==SIGTRAP)) + { + /* fprintf(stderr,"* Single step trap %x\n", single_stepping); */ + +#ifndef __linux__ + /* Un-install single step helper instructions. */ + *(single_stepping-3) = single_step_save1; + *(single_stepping-2) = single_step_save2; + *(single_stepping-1) = single_step_save3; +#else + context->eflags ^= 0x100; #endif + /* Re-install the breakpoint if possible. */ + if ((int)context->sc_pc == (int)single_stepping + 1) + fprintf(stderr,"* Breakpoint not re-install\n"); + else + { + char*ptr = (char*)single_stepping; + *((char *)single_stepping) = BREAKPOINT_INST; /* x86 INT3 */ + *((char *)single_stepping+1) = trap_Breakpoint; + } + + single_stepping=NULL; + return; + } - /*fprintf(stderr,"x86sigtrap: %8x %x\n", context->sc_pc, *(char*)(context->sc_pc-1)); - */ - DPRINTF(0,(stderr,"sigtrap(%d %d %x)\n",signal,code,context)); SAVE_CONTEXT(); + /* this is just for info in case monitor wants to print an approx */ current_control_stack_pointer = (unsigned long*)context->sc_sp; + /* 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. */ - if( *(unsigned char*)(context->sc_pc-1) == BREAKPOINT_INST) - { - if(after_breakpoint) code = AfterBreakpoint; /* ?? */ - else code = (int)*(char*)context->sc_pc; - } - switch (code) + trap = *(unsigned char *)(context->sc_pc); + switch (trap) { case trap_PendingInterrupt: DPRINTF(0,(stderr,"<trap Pending Interrupt.>\n")); @@ -230,29 +256,18 @@ sigtrap_handler(HANDLER_ARGS) #endif break; - case trap_Breakpoint: /* Not tested */ + case trap_Breakpoint: + /* fprintf(stderr,"*C break\n");*/ (char*)context->sc_pc -= 1; handle_breakpoint(signal, code, context); + /* fprintf(stderr,"*C break return\n");*/ break; - case trap_FunctionEndBreakpoint: /* not tested */ + case trap_FunctionEndBreakpoint: (char*)context->sc_pc -= 1; context->sc_pc = (int)handle_function_end_breakpoint(signal, code, context); break; - case AfterBreakpoint: /* not tested */ - DPRINTF(1,(stderr,"[C--AfterBreakpoint NOT TESTED]\n")); - (char*)context->sc_pc -= 1; - *skipped_break_addr = BREAKPOINT_INST; - os_flush_icache((os_vm_address_t)skipped_break_addr, - sizeof(unsigned long)); - skipped_break_addr = NULL; - *(unsigned int *)context->sc_pc = displaced_after_inst; - os_flush_icache((os_vm_address_t)context->sc_pc, sizeof(unsigned long)); - context->sc_mask = orig_sigmask; - after_breakpoint=NULL; - break; - default: DPRINTF(0,(stderr,"[C--trap default %d %d %x]\n",signal,code,context)); #ifdef __linux__ diff --git a/lisp/x86-assem.S b/lisp/x86-assem.S index 34ceea34f..eaed0c8b8 100644 --- a/lisp/x86-assem.S +++ b/lisp/x86-assem.S @@ -1,6 +1,6 @@ ### x86-assem.S -*- Mode: Asm; -*- /** - * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/x86-assem.S,v 1.5 1997/11/04 09:11:23 dtc Exp $ + * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/x86-assem.S,v 1.6 1997/11/25 15:53:32 dtc Exp $ * * Authors: Paul F. Werkowski <pw@snoopy.mv.com> * Douglas T. Crosher @@ -311,10 +311,8 @@ Llra: ret .size GNAME(call_into_lisp), . - GNAME(call_into_lisp) - -/* Fdefn objects for undefined functions have a 'raw' slot - pointing here. The RISC systems seem to have this set up - as an actual function object instead. +/* + * The undefined-function trampoline. */ .text .align align_4byte,0x90 @@ -322,48 +320,61 @@ Llra: .type GNAME(undefined_tramp),@function GNAME(undefined_tramp): int3 - .byte trap_Cerror + .byte trap_Error .byte 2 .byte 23 .byte sc_DescriptorReg # EAX in the Descriptor-reg SC. ret .size GNAME(undefined_tramp), .-GNAME(undefined_tramp) -/* This apparently gets called to hack on something in closures - that can't get done by the compiler. -*/ - .align align_4byte,0x90 +/* + * The closure trampoline. + */ + .text + .align align_4byte,0x90 .global GNAME(closure_tramp) .type GNAME(closure_tramp),@function GNAME(closure_tramp): movl FDEFN_FUNCTION_OFFSET(%eax),%eax jmp CLOSURE_FUNCTION_OFFSET(%eax) - .size GNAME(closure_tramp), .-GNAME(closure_tramp) - .align align_4byte - .global GNAME(function_end_breakpoint_trap) - .global GNAME(function_end_breakpoint_end) +/* + * Function-end breakpoint magic. + */ + .text .global GNAME(function_end_breakpoint_guts) - -GNAME(function_end_breakpoint_trap): - ret - .align align_4byte + .align align_4byte GNAME(function_end_breakpoint_guts): - ret - .align align_4byte -GNAME(function_end_breakpoint_end): - ret + /* Multiple Value return */ + jmp multiple_value_return + /* Single value return */ + + /* The eventual return will now be a multi-value return so + setup for it. Multi-value returns are not in general valid + for one value so convert to 2 values. Perhaps the + convertion should be changed? */ + movl $8,%ecx # Setup ecx=2 for the debugger. + movl $NIL,%edi # Second value + movl %esp,%ebx # ebx has the ofp + subl $8,%esp # needed? + +multiple_value_return: + + .global GNAME(function_end_breakpoint_trap) +GNAME(function_end_breakpoint_trap): + int3 + .byte trap_FunctionEndBreakpoint - .global GNAME(test_point) - .align align_4byte -GNAME(test_point): - ret + /* Never returns here. */ + .global GNAME(function_end_breakpoint_end) +GNAME(function_end_breakpoint_end): + hlt .global GNAME(do_pending_interrupt) .type GNAME(do_pending_interrupt),@function - .align align_4byte,0x90 + .align align_4byte,0x90 GNAME(do_pending_interrupt): int3 .byte trap_PendingInterrupt -- GitLab