/* This code was written as part of the CMU Common Lisp project at Carnegie Mellon University, and has been placed in the public domain. */ #include #include #include "lisp.h" #include "os.h" #include "internals.h" #include "interrupt.h" #include "arch.h" #include "lispregs.h" #include "globals.h" #include "alloc.h" #include "breakpoint.h" #if defined GENCGC #include "gencgc.h" #endif /* * See MAKE-BOGUS-LRA in code/debug-int.lisp for these values. * * Ideally, internals.h should have the correct values. We leave * these defaults here for now. */ #ifndef REAL_LRA_SLOT #define REAL_LRA_SLOT 0 #endif #ifndef KNOWN_RETURN_P_SLOT #ifndef i386 #define KNOWN_RETURN_P_SLOT 1 #else #define KNOWN_RETURN_P_SLOT 2 #endif #endif #ifndef BOGUS_LRA_CONSTANTS #ifndef i386 #define BOGUS_LRA_CONSTANTS 2 #else #define BOGUS_LRA_CONSTANTS 3 #endif #endif static void * compute_pc(lispobj code_obj, int pc_offset) { struct code *code; code = (struct code *) PTR(code_obj); return (void *) ((char *) code + HeaderValue(code->header) * sizeof(lispobj) + pc_offset); } unsigned long breakpoint_install(lispobj code_obj, int pc_offset) { return arch_install_breakpoint(compute_pc(code_obj, pc_offset)); } void breakpoint_remove(lispobj code_obj, int pc_offset, unsigned long orig_inst) { arch_remove_breakpoint(compute_pc(code_obj, pc_offset), orig_inst); } void breakpoint_do_displaced_inst(os_context_t * scp, unsigned long orig_inst) { #if !defined(hpux) && !defined(irix) && !defined(i386) undo_fake_foreign_function_call(scp); #endif arch_do_displaced_inst(scp, orig_inst); } #if !defined(i386) static lispobj find_code(os_context_t * scp) { #ifdef reg_CODE lispobj code = SC_REG(scp, reg_CODE), header; if (LowtagOf(code) != type_OtherPointer) return NIL; header = *(lispobj *) (code - type_OtherPointer); if (TypeOf(header) == type_CodeHeader) return code; else return code - HeaderValue(header) * sizeof(lispobj); #else return NIL; #endif } #endif #if defined(i386) static lispobj find_code(os_context_t * scp) { lispobj *codeptr = component_ptr_from_pc((lispobj *) SC_PC(scp)); if (codeptr == NULL) return NIL; else return (lispobj) codeptr | type_OtherPointer; } #endif #if (defined(DARWIN) && defined(__ppc__)) || (defined(sparc)) /* * During a function-end-breakpoint, the pc is sometimes less than the * code address, which bypasses the function end stuff. Then the * offset is zero for a function-end-breakpoint, and we can't find the * breakpoint data, causing an error during tracing. But we know this * is a function-end breakpoint, because function_end is set to true. * * (This condition of pc < code address seems to occur only if a GC * happens during tracing. I guess the function-end code object * sometimes gets moved to a lower address than the corresponding * code.) * * Hence this replacement looks at the function end flag first * to see if it is a function-end breakpoint and does the function-end * stuff anyway. If not, we do the normal stuff. */ static int compute_offset(os_context_t * scp, lispobj code, boolean function_end) { if (code == NIL) return 0; if (function_end) { /* * We're in a function end breakpoint. Compute the * offset from the (known) breakpoint location and the * beginning of the breakpoint guts. (See *-assem.S.) * * Then make the offset negative so the caller knows * that the offset is not from the code object. */ extern char function_end_breakpoint_trap; extern char function_end_breakpoint_guts; int offset; offset = &function_end_breakpoint_trap - &function_end_breakpoint_guts; #if 0 fprintf(stderr, "compute_offset\n"); fprintf(stderr, " function end offset = %d\n", offset); #endif return make_fixnum(-offset); } else { unsigned long code_start; struct code *codeptr = (struct code *) PTR(code); unsigned long pc = SC_PC(scp); code_start = (unsigned long) codeptr + HeaderValue(codeptr->header) * sizeof(lispobj); #if 0 fprintf(stderr, "compute_offset\n"); fprintf(stderr, " pc = %d\n", pc); fprintf(stderr, " code_start = %d\n", code_start); fprintf(stderr, " function_end = %d\n", function_end); #endif if (pc < code_start) { return 0; } else { int offset = pc - code_start; #if 0 fprintf(stderr, " offset = %d\n", offset); fprintf(stderr, " codeptr->code_size = %d\n", codeptr->code_size); fprintf(stderr, " function_end = %d\n", function_end); #endif if (offset >= codeptr->code_size) { return 0; } else { return make_fixnum(offset); } } } } #else 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 { unsigned long code_start; struct code *codeptr = (struct code *) PTR(code); #ifdef parisc unsigned long pc = SC_PC(scp) & ~3; #else unsigned long pc = SC_PC(scp); #endif 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 { return make_fixnum(offset); } } } } #endif #ifndef i386 void handle_breakpoint(int signal, int subcode, os_context_t * scp) { lispobj code; fake_foreign_function_call(scp); code = find_code(scp); #if 0 fprintf(stderr, "handle_breakpoint\n"); fprintf(stderr, " offset = %d\n", compute_offset(scp, code, 0)); #endif funcall3(SymbolFunction(HANDLE_BREAKPOINT), compute_offset(scp, code, 0), code, alloc_sap(scp)); undo_fake_foreign_function_call(scp); } #else void handle_breakpoint(int signal, int subcode, os_context_t * scp) { lispobj code, scp_sap = alloc_sap(scp); fake_foreign_function_call(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. */ sigprocmask(SIG_SETMASK, &scp->uc_sigmask, NULL); funcall3(SymbolFunction(HANDLE_BREAKPOINT), compute_offset(scp, code, 0), code, scp_sap); undo_fake_foreign_function_call(scp); } #endif #ifndef i386 void * handle_function_end_breakpoint(int signal, int subcode, os_context_t * scp) { lispobj code, lra; struct code *codeptr; int offset; int known_return_p; fake_foreign_function_call(scp); code = find_code(scp); codeptr = (struct code *) PTR(code); offset = compute_offset(scp, code, 1); #if 0 printf("handle_function_end:\n"); printf(" code = 0x%08x\n", code); printf(" codeptr = %p\n", codeptr); printf(" offset = %d\n", fixnum_value(offset)); fflush(stdout); #endif if (offset < 0) { /* * We were in the function end breakpoint. Which means we are * in a bogus LRA, so compute where the code-component of this * bogus lra object starts. Adjust code, and codeptr * appropriately so the breakpoint handler can do the right * thing. */ unsigned int pc; pc = SC_PC(scp); offset = -offset; /* * Some magic here. pc points to the trap instruction. The * offset gives us where the function_end_breakpoint_guts * begins. But we need to back up some more to get to the * code-component object. See MAKE-BOGUS-LRA in * debug-int.lisp */ code = pc - fixnum_value(offset); code -= sizeof(struct code) + BOGUS_LRA_CONSTANTS * sizeof(lispobj); code += type_OtherPointer; codeptr = (struct code *) PTR(code); #if 0 printf(" pc = 0x%08x\n", pc); printf(" code = 0x%08x\n", code); printf(" codeptr = %p\n", codeptr); fflush(stdout); #endif } lra = codeptr->constants[REAL_LRA_SLOT]; known_return_p = codeptr->constants[KNOWN_RETURN_P_SLOT] != NIL; { lispobj *args = current_control_stack_pointer; /* * Because HANDLE_BREAKPOINT can GC, the LRA could move, and * we need to know where it went so we can return to the * correct place. We do this by saving the LRA on the Lisp * stack. If GC moves the LRA, the stack entry will get * updated appropriately too. */ current_control_stack_pointer += 1; args[0] = lra; funcall3(SymbolFunction(HANDLE_BREAKPOINT), offset, code, alloc_sap(scp)); /* * Breakpoint handling done. Get the (possibly changed) LRA * value off the stack so we know where to return to. */ lra = args[0]; current_control_stack_pointer -= 1; #ifdef reg_CODE /* * With the known-return convention, we definitely do NOT want * to mangle the CODE register because it isn't pointing to * the bogus LRA but to the actual routine. */ if (!known_return_p) { SC_REG(scp, reg_CODE) = lra; } #endif } undo_fake_foreign_function_call(scp); return (void *) (lra - type_OtherPointer + sizeof(lispobj)); } #else void * handle_function_end_breakpoint(int signal, int subcode, os_context_t * scp) { lispobj code, scp_sap = alloc_sap(scp); struct code *codeptr; fake_foreign_function_call(scp); code = find_code(scp); codeptr = (struct code *) PTR(code); /* * Don't disallow recursive breakpoint traps. Otherwise, we can't * use debugger breakpoints anywhere in here. */ sigprocmask(SIG_SETMASK, &scp->uc_sigmask, NULL); funcall3(SymbolFunction(HANDLE_BREAKPOINT), compute_offset(scp, code, 1), code, scp_sap); undo_fake_foreign_function_call(scp); return compute_pc(codeptr->constants[REAL_LRA_SLOT], fixnum_value(codeptr->constants[REAL_LRA_SLOT + 1])); } #endif