/* 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 #include "lisp.h" #include "globals.h" #include "validate.h" #include "os.h" #include "internals.h" #include "arch.h" #include "lispregs.h" #include "signal.h" #include "alloc.h" #include "interrupt.h" #include "interr.h" #include "breakpoint.h" #define BREAKPOINT_INST 0xcc /* INT3 */ /* * 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) /* * Use the /dev/cpu/self/cpuid interface on Solaris. We could use the * same method below, but the Sun C compiler miscompiles the inline * assembly. */ #include #include #include #include #include #include void cpuid(int level, unsigned int* a, unsigned int* b, unsigned int* c, unsigned int* d) { int device; uint32_t regs[4]; static const char devname[] = "/dev/cpu/self/cpuid"; *a = *b = *c = *d = 0; if ((device = open(devname, O_RDONLY)) == -1) { perror(devname); goto exit; } if (pread(device, regs, sizeof(regs), 1) != sizeof(regs)) { perror(devname); goto exit; } *a = regs[0]; *b = regs[1]; *c = regs[2]; *d = regs[3]; exit: (void) close(device); return; } #else #define __cpuid(level, a, b, c, d) \ __asm__ ("xchgl\t%%ebx, %1\n\t" \ "cpuid\n\t" \ "xchgl\t%%ebx, %1\n\t" \ : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ : "0" (level)) void cpuid(int level, unsigned int* a, unsigned int* b, unsigned int* c, unsigned int* d) { unsigned int eax, ebx, ecx, edx; __cpuid(level, eax, ebx, ecx, edx); *a = eax; *b = ebx; *c = ecx; *d = edx; } #endif int arch_support_sse2(void) { unsigned int eax, ebx, ecx, edx; cpuid(1, &eax, &ebx, &ecx, &edx); /* Return non-zero if SSE2 is supported */ return edx & 0x4000000; } char * arch_init(fpu_mode_t mode) { int have_sse2; have_sse2 = arch_support_sse2() && os_support_sse2(); if (!have_sse2) { fprintf(stderr, "CMUCL requires a SSE2 support; exiting\n"); abort(); } switch (mode) { case AUTO: case X87: fprintf(stderr, "fpu mode AUTO or X87 is not longer supported.\n"); /* Fall through and return the sse2 core */ case SSE2: return "lisp-sse2.core"; break; default: abort(); } } /* * Skip the UD1 instruction, and any data bytes associated with the * trap. */ void arch_skip_instruction(os_context_t * context) { int vlen, code; 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; switch (code) { case trap_Error: case trap_Cerror: /* Lisp error arg vector length */ vlen = *pc++; SC_PC(context) = (unsigned long) pc; /* Skip lisp error arg data bytes */ 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; case trap_PendingInterrupt: case trap_Halt: /* Only needed to skip the Code. */ break; default: fprintf(stderr, "[arch_skip_inst invalid code %d\n]\n", code); break; } DPRINTF(debug_handlers, (stderr, "[arch_skip_inst resuming at %lx>]\n", SC_PC(context))); } unsigned char * arch_internal_error_arguments(os_context_t * context) { return (unsigned char *) (SC_PC(context) + 1); } boolean arch_pseudo_atomic_atomic(os_context_t * context) { return SymbolValue(PSEUDO_ATOMIC_ATOMIC); } void arch_set_pseudo_atomic_interrupted(os_context_t * context) { SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, make_fixnum(1)); } /* * 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 char *) pc; *(unsigned char *) pc = BREAKPOINT_INST; DPRINTF(debug_handlers, (stderr, "arch_install_breakpoint at %p, old code = 0x%lx\n", pc, result)); return result; } void arch_remove_breakpoint(void *pc, unsigned long orig_inst) { 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; } /* * 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 /* * 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 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. */ *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 SC_EFLAGS(context) |= 0x100; #else /* * Install helper instructions for the single step: * nop; nop; nop; pushf; or [esp],0x100; popf. * * The or instruction enables the trap flag which enables * single-stepping. So when the popf instruction is run, we start * single-stepping and stop on the next instruction. */ DPRINTF(0, (stderr, "Installing helper instructions\n")); 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 SC_EFLAGS /* * pc - 9 points to the pushf instruction that we installed for * the helper. */ DPRINTF(0, (stderr, " Setting pc to pushf instruction at %p\n", (void*) ((char*) pc - 9))); SC_PC(context) = (int)((char *) pc - 9); #endif } /* * Handles the ud1 instruction from lisp that is used to signal * errors. In particular, this does not handle the breakpoint traps. */ void sigill_handler(HANDLER_ARGS) { unsigned int trap; os_context_t* os_context = (os_context_t *) context; 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 * the current FPU modes from the saved context. */ RESTORE_FPU(os_context); /* * 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. */ DPRINTF(debug_handlers, (stderr, "pc %x\n", *(unsigned short *)SC_PC(context))); /* * 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(debug_handlers, (stderr, "\n")); arch_skip_instruction(os_context); interrupt_handle_pending(os_context); break; case trap_Halt: { 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); restore_fpu_state(fpu_state); arch_skip_instruction(os_context); break; } case trap_Error: case trap_Cerror: DPRINTF(debug_handlers, (stderr, "\n", CODE(code))); interrupt_internal_error(signal, code, os_context, CODE(code) == trap_Cerror); break; case trap_Breakpoint: lose("Unexpected breakpoint trap in sigill-hander.\n"); break; case trap_FunctionEndBreakpoint: 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; #endif #ifdef trap_DynamicSpaceOverflowError case trap_DynamicSpaceOverflowError: interrupt_handle_space_overflow(SymbolFunction (DYNAMIC_SPACE_OVERFLOW_ERROR_HIT), os_context); break; #endif default: 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, sigill_handler); interrupt_install_low_level_handler(SIGTRAP, sigtrap_handler); } extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs); /* These next four functions are an interface to the * Lisp call-in facility. Since this is C we can know * nothing about the calling environment. The control * stack might be the C stack if called from the monitor * or the Lisp stack if called as a result of an interrupt * or maybe even a separate stack. The args are most likely * on that stack but could be in registers depending on * what the compiler likes. So I try to package up the * args into a portable vector and let the assembly language * call-in function figure it out. */ lispobj funcall0(lispobj function) { lispobj *args = NULL; return call_into_lisp(function, args, 0); } lispobj funcall1(lispobj function, lispobj arg0) { lispobj args[1]; args[0] = arg0; return call_into_lisp(function, args, 1); } lispobj funcall2(lispobj function, lispobj arg0, lispobj arg1) { lispobj args[2]; args[0] = arg0; args[1] = arg1; return call_into_lisp(function, args, 2); } lispobj funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2) { lispobj args[3]; args[0] = arg0; args[1] = arg1; args[2] = arg2; return call_into_lisp(function, args, 3); } #ifdef LINKAGE_TABLE #ifndef LinkageEntrySize #define LinkageEntrySize 8 #endif void arch_make_linkage_entry(long linkage_entry, void *target_addr, long type) { char *reloc_addr = (char *) (FOREIGN_LINKAGE_SPACE_START + linkage_entry * LinkageEntrySize); if (type == 1) { /* code reference */ /* Make JMP to function entry. */ /* JMP offset is calculated from next instruction. */ long offset = (char *) target_addr - (reloc_addr + 5); int i; *reloc_addr++ = 0xe9; /* opcode for JMP rel32 */ for (i = 0; i < 4; i++) { *reloc_addr++ = offset & 0xff; offset >>= 8; } /* write a nop for good measure. */ *reloc_addr = 0x90; } else if (type == 2) { *(unsigned long *) reloc_addr = (unsigned long) target_addr; } } /* Make a call to the first function in the linkage table, which is resolve_linkage_tramp. */ void arch_make_lazy_linkage(long linkage_entry) { char *reloc_addr = (char *) (FOREIGN_LINKAGE_SPACE_START + linkage_entry * LinkageEntrySize); long offset = (char *) (FOREIGN_LINKAGE_SPACE_START) - (reloc_addr + 5); int i; *reloc_addr++ = 0xe8; /* opcode for CALL rel32 */ for (i = 0; i < 4; i++) { *reloc_addr++ = offset & 0xff; offset >>= 8; } /* write a nop for good measure. */ *reloc_addr = 0x90; } /* Get linkage entry. The initial instruction in the linkage entry is a CALL; the return address we're passed points to the next instruction. */ long arch_linkage_entry(unsigned long retaddr) { return ((retaddr - 5) - FOREIGN_LINKAGE_SPACE_START) / LinkageEntrySize; } #endif /* LINKAGE_TABLE */