--- BasiliskII/src/Unix/sigsegv.cpp 2006/01/22 00:05:05 1.57 +++ BasiliskII/src/Unix/sigsegv.cpp 2008/01/06 16:19:27 1.74 @@ -10,7 +10,7 @@ * tjw@omnigroup.com Sun, 4 Jun 2000 * www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html * - * Basilisk II (C) 1997-2005 Christian Bauer + * Basilisk II (C) 1997-2008 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,19 +66,23 @@ static bool sigsegv_do_install_handler(i * Instruction decoding aids */ +// Transfer type +enum transfer_type_t { + SIGSEGV_TRANSFER_UNKNOWN = 0, + SIGSEGV_TRANSFER_LOAD = 1, + SIGSEGV_TRANSFER_STORE = 2 +}; + // Transfer size enum transfer_size_t { SIZE_UNKNOWN, SIZE_BYTE, SIZE_WORD, // 2 bytes SIZE_LONG, // 4 bytes - SIZE_QUAD, // 8 bytes + SIZE_QUAD // 8 bytes }; -// Transfer type -typedef sigsegv_transfer_type_t transfer_type_t; - -#if (defined(powerpc) || defined(__powerpc__) || defined(__ppc__)) +#if (defined(powerpc) || defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)) // Addressing mode enum addressing_mode_t { MODE_UNKNOWN, @@ -240,7 +244,7 @@ static void powerpc_decode_instruction(i #define SIGSEGV_CONTEXT_REGS (((ucontext_t *)scp)->uc_mcontext.gregs) #define SIGSEGV_FAULT_INSTRUCTION (unsigned long)SIGSEGV_CONTEXT_REGS[CTX_EPC] #if (defined(mips) || defined(__mips)) -#define SIGSEGV_REGISTER_FILE SIGSEGV_CONTEXT_REGS +#define SIGSEGV_REGISTER_FILE &SIGSEGV_CONTEXT_REGS[CTX_EPC], &SIGSEGV_CONTEXT_REGS[CTX_R0] #define SIGSEGV_SKIP_INSTRUCTION mips_skip_instruction #endif #endif @@ -323,6 +327,13 @@ static void powerpc_decode_instruction(i #define SIGSEGV_REGISTER_FILE (&SIGSEGV_CONTEXT_REGS.arm_r0) #define SIGSEGV_SKIP_INSTRUCTION arm_skip_instruction #endif +#if (defined(mips) || defined(__mips__)) +#include +#define SIGSEGV_CONTEXT_REGS (((struct ucontext *)scp)->uc_mcontext) +#define SIGSEGV_FAULT_INSTRUCTION (SIGSEGV_CONTEXT_REGS.pc) +#define SIGSEGV_REGISTER_FILE &SIGSEGV_CONTEXT_REGS.pc, &SIGSEGV_CONTEXT_REGS.gregs[0] +#define SIGSEGV_SKIP_INSTRUCTION mips_skip_instruction +#endif #endif #endif @@ -591,32 +602,57 @@ if (ret != KERN_SUCCESS) { \ } #ifdef __ppc__ +#define SIGSEGV_EXCEPTION_STATE_TYPE ppc_exception_state_t +#define SIGSEGV_EXCEPTION_STATE_FLAVOR PPC_EXCEPTION_STATE +#define SIGSEGV_EXCEPTION_STATE_COUNT PPC_EXCEPTION_STATE_COUNT +#define SIGSEGV_FAULT_ADDRESS SIP->exc_state.dar #define SIGSEGV_THREAD_STATE_TYPE ppc_thread_state_t #define SIGSEGV_THREAD_STATE_FLAVOR PPC_THREAD_STATE #define SIGSEGV_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT -#define SIGSEGV_FAULT_INSTRUCTION state->srr0 +#define SIGSEGV_FAULT_INSTRUCTION SIP->thr_state.srr0 #define SIGSEGV_SKIP_INSTRUCTION powerpc_skip_instruction -#define SIGSEGV_REGISTER_FILE (unsigned long *)&state->srr0, (unsigned long *)&state->r0 +#define SIGSEGV_REGISTER_FILE (unsigned long *)&SIP->thr_state.srr0, (unsigned long *)&SIP->thr_state.r0 +#endif +#ifdef __ppc64__ +#define SIGSEGV_EXCEPTION_STATE_TYPE ppc_exception_state64_t +#define SIGSEGV_EXCEPTION_STATE_FLAVOR PPC_EXCEPTION_STATE64 +#define SIGSEGV_EXCEPTION_STATE_COUNT PPC_EXCEPTION_STATE64_COUNT +#define SIGSEGV_FAULT_ADDRESS SIP->exc_state.dar +#define SIGSEGV_THREAD_STATE_TYPE ppc_thread_state64_t +#define SIGSEGV_THREAD_STATE_FLAVOR PPC_THREAD_STATE64 +#define SIGSEGV_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT +#define SIGSEGV_FAULT_INSTRUCTION SIP->thr_state.srr0 +#define SIGSEGV_SKIP_INSTRUCTION powerpc_skip_instruction +#define SIGSEGV_REGISTER_FILE (unsigned long *)&SIP->thr_state.srr0, (unsigned long *)&SIP->thr_state.r0 #endif #ifdef __i386__ -#ifdef i386_SAVED_STATE -#define SIGSEGV_THREAD_STATE_TYPE struct i386_saved_state -#define SIGSEGV_THREAD_STATE_FLAVOR i386_SAVED_STATE -#define SIGSEGV_THREAD_STATE_COUNT i386_SAVED_STATE_COUNT -#define SIGSEGV_REGISTER_FILE ((unsigned long *)&state->edi) /* EDI is the first GPR we consider */ -#else +#define SIGSEGV_EXCEPTION_STATE_TYPE struct i386_exception_state +#define SIGSEGV_EXCEPTION_STATE_FLAVOR i386_EXCEPTION_STATE +#define SIGSEGV_EXCEPTION_STATE_COUNT i386_EXCEPTION_STATE_COUNT +#define SIGSEGV_FAULT_ADDRESS SIP->exc_state.faultvaddr #define SIGSEGV_THREAD_STATE_TYPE struct i386_thread_state #define SIGSEGV_THREAD_STATE_FLAVOR i386_THREAD_STATE #define SIGSEGV_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT -#define SIGSEGV_REGISTER_FILE ((unsigned long *)&state->eax) /* EAX is the first GPR we consider */ +#define SIGSEGV_FAULT_INSTRUCTION SIP->thr_state.eip +#define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction +#define SIGSEGV_REGISTER_FILE ((unsigned long *)&SIP->thr_state.eax) /* EAX is the first GPR we consider */ #endif -#define SIGSEGV_FAULT_INSTRUCTION state->eip +#ifdef __x86_64__ +#define SIGSEGV_EXCEPTION_STATE_TYPE struct x86_exception_state64 +#define SIGSEGV_EXCEPTION_STATE_FLAVOR x86_EXCEPTION_STATE64 +#define SIGSEGV_EXCEPTION_STATE_COUNT x86_EXCEPTION_STATE64_COUNT +#define SIGSEGV_FAULT_ADDRESS SIP->exc_state.faultvaddr +#define SIGSEGV_THREAD_STATE_TYPE struct x86_thread_state64 +#define SIGSEGV_THREAD_STATE_FLAVOR x86_THREAD_STATE64 +#define SIGSEGV_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT +#define SIGSEGV_FAULT_INSTRUCTION SIP->thr_state.rip #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction +#define SIGSEGV_REGISTER_FILE ((unsigned long *)&SIP->thr_state.rax) /* RAX is the first GPR we consider */ #endif -#define SIGSEGV_FAULT_ADDRESS code[1] -#define SIGSEGV_FAULT_HANDLER_INVOKE(ADDR, IP) ((code[0] == KERN_PROTECTION_FAILURE) ? sigsegv_fault_handler(ADDR, IP) : SIGSEGV_RETURN_FAILURE) -#define SIGSEGV_FAULT_HANDLER_ARGLIST mach_port_t thread, exception_data_t code, SIGSEGV_THREAD_STATE_TYPE *state -#define SIGSEGV_FAULT_HANDLER_ARGS thread, code, &state +#define SIGSEGV_FAULT_ADDRESS_FAST code[1] +#define SIGSEGV_FAULT_INSTRUCTION_FAST SIGSEGV_INVALID_ADDRESS +#define SIGSEGV_FAULT_HANDLER_ARGLIST mach_port_t thread, exception_data_t code +#define SIGSEGV_FAULT_HANDLER_ARGS thread, code // Since there can only be one exception thread running at any time // this is not a problem. @@ -774,6 +810,7 @@ enum { #endif #if defined(__APPLE__) && defined(__MACH__) enum { +#if (defined(i386) || defined(__i386__)) #ifdef i386_SAVED_STATE // same as FreeBSD (in Open Darwin 8.0.1) X86_REG_EIP = 10, @@ -790,13 +827,33 @@ enum { X86_REG_EIP = 10, X86_REG_EAX = 0, X86_REG_ECX = 2, - X86_REG_EDX = 4, + X86_REG_EDX = 3, X86_REG_EBX = 1, X86_REG_ESP = 7, X86_REG_EBP = 6, X86_REG_ESI = 5, X86_REG_EDI = 4 #endif +#endif +#if defined(__x86_64__) + X86_REG_R8 = 8, + X86_REG_R9 = 9, + X86_REG_R10 = 10, + X86_REG_R11 = 11, + X86_REG_R12 = 12, + X86_REG_R13 = 13, + X86_REG_R14 = 14, + X86_REG_R15 = 15, + X86_REG_EDI = 4, + X86_REG_ESI = 5, + X86_REG_EBP = 6, + X86_REG_EBX = 1, + X86_REG_EDX = 3, + X86_REG_EAX = 0, + X86_REG_ECX = 2, + X86_REG_ESP = 7, + X86_REG_EIP = 16 +#endif }; #endif #if defined(_WIN32) @@ -859,8 +916,14 @@ static bool ix86_skip_instruction(unsign return false; #endif + enum instruction_type_t { + i_MOV, + i_ADD + }; + transfer_type_t transfer_type = SIGSEGV_TRANSFER_UNKNOWN; transfer_size_t transfer_size = SIZE_LONG; + instruction_type_t instruction_type = i_MOV; int reg = -1; int len = 0; @@ -911,6 +974,7 @@ static bool ix86_skip_instruction(unsign #endif // Decode instruction + int op_len = 1; int target_size = SIZE_UNKNOWN; switch (eip[0]) { case 0x0f: @@ -925,61 +989,71 @@ static bool ix86_skip_instruction(unsign transfer_size = SIZE_WORD; goto do_mov_extend; do_mov_extend: - switch (eip[2] & 0xc0) { - case 0x80: - reg = (eip[2] >> 3) & 7; - transfer_type = SIGSEGV_TRANSFER_LOAD; - break; - case 0x40: - reg = (eip[2] >> 3) & 7; - transfer_type = SIGSEGV_TRANSFER_LOAD; - break; - case 0x00: - reg = (eip[2] >> 3) & 7; - transfer_type = SIGSEGV_TRANSFER_LOAD; - break; - } - len += 3 + ix86_step_over_modrm(eip + 2); - break; - } - break; + op_len = 2; + goto do_transfer_load; + } + break; +#if defined(__x86_64__) + case 0x63: // MOVSXD r64, r/m32 + if (has_rex && rex.W) { + transfer_size = SIZE_LONG; + target_size = SIZE_QUAD; + } + else if (transfer_size != SIZE_WORD) { + transfer_size = SIZE_LONG; + target_size = SIZE_QUAD; + } + goto do_transfer_load; +#endif + case 0x02: // ADD r8, r/m8 + transfer_size = SIZE_BYTE; + case 0x03: // ADD r32, r/m32 + instruction_type = i_ADD; + goto do_transfer_load; case 0x8a: // MOV r8, r/m8 transfer_size = SIZE_BYTE; case 0x8b: // MOV r32, r/m32 (or 16-bit operation) - switch (eip[1] & 0xc0) { + do_transfer_load: + switch (eip[op_len] & 0xc0) { case 0x80: - reg = (eip[1] >> 3) & 7; + reg = (eip[op_len] >> 3) & 7; transfer_type = SIGSEGV_TRANSFER_LOAD; break; case 0x40: - reg = (eip[1] >> 3) & 7; + reg = (eip[op_len] >> 3) & 7; transfer_type = SIGSEGV_TRANSFER_LOAD; break; case 0x00: - reg = (eip[1] >> 3) & 7; + reg = (eip[op_len] >> 3) & 7; transfer_type = SIGSEGV_TRANSFER_LOAD; break; } - len += 2 + ix86_step_over_modrm(eip + 1); + len += 1 + op_len + ix86_step_over_modrm(eip + op_len); break; + case 0x00: // ADD r/m8, r8 + transfer_size = SIZE_BYTE; + case 0x01: // ADD r/m32, r32 + instruction_type = i_ADD; + goto do_transfer_store; case 0x88: // MOV r/m8, r8 transfer_size = SIZE_BYTE; case 0x89: // MOV r/m32, r32 (or 16-bit operation) - switch (eip[1] & 0xc0) { + do_transfer_store: + switch (eip[op_len] & 0xc0) { case 0x80: - reg = (eip[1] >> 3) & 7; + reg = (eip[op_len] >> 3) & 7; transfer_type = SIGSEGV_TRANSFER_STORE; break; case 0x40: - reg = (eip[1] >> 3) & 7; + reg = (eip[op_len] >> 3) & 7; transfer_type = SIGSEGV_TRANSFER_STORE; break; case 0x00: - reg = (eip[1] >> 3) & 7; + reg = (eip[op_len] >> 3) & 7; transfer_type = SIGSEGV_TRANSFER_STORE; break; } - len += 2 + ix86_step_over_modrm(eip + 1); + len += 1 + op_len + ix86_step_over_modrm(eip + op_len); break; } if (target_size == SIZE_UNKNOWN) @@ -995,7 +1069,7 @@ static bool ix86_skip_instruction(unsign reg += 8; #endif - if (transfer_type == SIGSEGV_TRANSFER_LOAD && reg != -1) { + if (instruction_type == i_MOV && transfer_type == SIGSEGV_TRANSFER_LOAD && reg != -1) { static const int x86_reg_map[] = { X86_REG_EAX, X86_REG_ECX, X86_REG_EDX, X86_REG_EBX, X86_REG_ESP, X86_REG_EBP, X86_REG_ESI, X86_REG_EDI, @@ -1031,7 +1105,7 @@ static bool ix86_skip_instruction(unsign } #if DEBUG - printf("%08x: %s %s access", regs[X86_REG_EIP], + printf("%p: %s %s access", (void *)regs[X86_REG_EIP], transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_WORD ? "word" : transfer_size == SIZE_LONG ? "long" : @@ -1087,7 +1161,7 @@ static bool ix86_skip_instruction(unsign #endif // Decode and skip PPC instruction -#if (defined(powerpc) || defined(__powerpc__) || defined(__ppc__)) +#if (defined(powerpc) || defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)) static bool powerpc_skip_instruction(unsigned long * nip_p, unsigned long * regs) { instruction_t instr; @@ -1123,14 +1197,9 @@ static bool powerpc_skip_instruction(uns // Decode and skip MIPS instruction #if (defined(mips) || defined(__mips)) -enum { -#if (defined(sgi) || defined(__sgi)) - MIPS_REG_EPC = 35, -#endif -}; -static bool mips_skip_instruction(greg_t * regs) +static bool mips_skip_instruction(greg_t * pc_p, greg_t * regs) { - unsigned int * epc = (unsigned int *)(unsigned long)regs[MIPS_REG_EPC]; + unsigned int * epc = (unsigned int *)(unsigned long)*pc_p; if (epc == 0) return false; @@ -1279,7 +1348,7 @@ static bool mips_skip_instruction(greg_t mips_gpr_names[reg]); #endif - regs[MIPS_REG_EPC] += 4; + *pc_p += 4; return true; } #endif @@ -1291,6 +1360,7 @@ enum { SPARC_REG_G1 = REG_G1, SPARC_REG_O0 = REG_O0, SPARC_REG_PC = REG_PC, + SPARC_REG_nPC = REG_nPC #endif }; static bool sparc_skip_instruction(unsigned long * regs, gwindows_t * gwins, struct rwindow * rwin) @@ -1354,7 +1424,7 @@ static bool sparc_skip_instruction(unsig break; case 7: // Store Doubleword transfer_type = SIGSEGV_TRANSFER_STORE; - transfer_size = SIZE_WORD; + transfer_size = SIZE_LONG; register_pair = true; break; } @@ -1364,19 +1434,36 @@ static bool sparc_skip_instruction(unsig return false; } - // Zero target register in case of a load operation const int reg = (opcode >> 25) & 0x1f; + +#if DEBUG + static const char * reg_names[] = { + "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", + "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", + "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7" + }; + printf("%s %s register %s\n", + transfer_size == SIZE_BYTE ? "byte" : + transfer_size == SIZE_WORD ? "word" : + transfer_size == SIZE_LONG ? "long" : + transfer_size == SIZE_QUAD ? "quad" : "unknown", + transfer_type == SIGSEGV_TRANSFER_LOAD ? "load to" : "store from", + reg_names[reg]); +#endif + + // Zero target register in case of a load operation if (transfer_type == SIGSEGV_TRANSFER_LOAD && reg != 0) { // FIXME: code to handle local & input registers is not tested - if (reg >= 1 && reg <= 7) { + if (reg >= 1 && reg < 8) { // global registers regs[reg - 1 + SPARC_REG_G1] = 0; } - else if (reg >= 8 && reg <= 15) { + else if (reg >= 8 && reg < 16) { // output registers regs[reg - 8 + SPARC_REG_O0] = 0; } - else if (reg >= 16 && reg <= 23) { + else if (reg >= 16 && reg < 24) { // local registers (in register windows) if (gwins) gwins->wbuf->rw_local[reg - 16] = 0; @@ -1392,23 +1479,8 @@ static bool sparc_skip_instruction(unsig } } -#if DEBUG - static const char * reg_names[] = { - "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", - "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7", - "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", - "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7" - }; - printf("%s %s register %s\n", - transfer_size == SIZE_BYTE ? "byte" : - transfer_size == SIZE_WORD ? "word" : - transfer_size == SIZE_LONG ? "long" : - transfer_size == SIZE_QUAD ? "quad" : "unknown", - transfer_type == SIGSEGV_TRANSFER_LOAD ? "load to" : "store from", - reg_names[reg]); -#endif - regs[SPARC_REG_PC] += 4; + regs[SPARC_REG_nPC] += 4; return true; } #endif @@ -1564,14 +1636,20 @@ static bool arm_skip_instruction(unsigne // Fallbacks +#ifndef SIGSEGV_FAULT_ADDRESS_FAST +#define SIGSEGV_FAULT_ADDRESS_FAST SIGSEGV_FAULT_ADDRESS +#endif +#ifndef SIGSEGV_FAULT_INSTRUCTION_FAST +#define SIGSEGV_FAULT_INSTRUCTION_FAST SIGSEGV_FAULT_INSTRUCTION +#endif #ifndef SIGSEGV_FAULT_INSTRUCTION -#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_INVALID_PC +#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_INVALID_ADDRESS #endif #ifndef SIGSEGV_FAULT_HANDLER_ARGLIST_1 #define SIGSEGV_FAULT_HANDLER_ARGLIST_1 SIGSEGV_FAULT_HANDLER_ARGLIST #endif #ifndef SIGSEGV_FAULT_HANDLER_INVOKE -#define SIGSEGV_FAULT_HANDLER_INVOKE(ADDR, IP) sigsegv_fault_handler(ADDR, IP) +#define SIGSEGV_FAULT_HANDLER_INVOKE(P) sigsegv_fault_handler(P) #endif // SIGSEGV recovery supported ? @@ -1584,25 +1662,100 @@ static bool arm_skip_instruction(unsigne * SIGSEGV global handler */ +struct sigsegv_info_t { + sigsegv_address_t addr; + sigsegv_address_t pc; +#ifdef HAVE_MACH_EXCEPTIONS + mach_port_t thread; + bool has_exc_state; + SIGSEGV_EXCEPTION_STATE_TYPE exc_state; + mach_msg_type_number_t exc_state_count; + bool has_thr_state; + SIGSEGV_THREAD_STATE_TYPE thr_state; + mach_msg_type_number_t thr_state_count; +#endif +}; + +#ifdef HAVE_MACH_EXCEPTIONS +static void mach_get_exception_state(sigsegv_info_t *SIP) +{ + SIP->exc_state_count = SIGSEGV_EXCEPTION_STATE_COUNT; + kern_return_t krc = thread_get_state(SIP->thread, + SIGSEGV_EXCEPTION_STATE_FLAVOR, + (natural_t *)&SIP->exc_state, + &SIP->exc_state_count); + MACH_CHECK_ERROR(thread_get_state, krc); + SIP->has_exc_state = true; +} + +static void mach_get_thread_state(sigsegv_info_t *SIP) +{ + SIP->thr_state_count = SIGSEGV_THREAD_STATE_COUNT; + kern_return_t krc = thread_get_state(SIP->thread, + SIGSEGV_THREAD_STATE_FLAVOR, + (natural_t *)&SIP->thr_state, + &SIP->thr_state_count); + MACH_CHECK_ERROR(thread_get_state, krc); + SIP->has_thr_state = true; +} + +static void mach_set_thread_state(sigsegv_info_t *SIP) +{ + kern_return_t krc = thread_set_state(SIP->thread, + SIGSEGV_THREAD_STATE_FLAVOR, + (natural_t *)&SIP->thr_state, + SIP->thr_state_count); + MACH_CHECK_ERROR(thread_set_state, krc); +} +#endif + +// Return the address of the invalid memory reference +sigsegv_address_t sigsegv_get_fault_address(sigsegv_info_t *SIP) +{ +#ifdef HAVE_MACH_EXCEPTIONS + static int use_fast_path = -1; + if (use_fast_path != 1 && !SIP->has_exc_state) { + mach_get_exception_state(SIP); + + sigsegv_address_t addr = (sigsegv_address_t)SIGSEGV_FAULT_ADDRESS; + if (use_fast_path < 0) + use_fast_path = addr == SIP->addr; + SIP->addr = addr; + } +#endif + return SIP->addr; +} + +// Return the address of the instruction that caused the fault, or +// SIGSEGV_INVALID_ADDRESS if we could not retrieve this information +sigsegv_address_t sigsegv_get_fault_instruction_address(sigsegv_info_t *SIP) +{ +#ifdef HAVE_MACH_EXCEPTIONS + if (!SIP->has_thr_state) { + mach_get_thread_state(SIP); + + SIP->pc = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION; + } +#endif + return SIP->pc; +} + // This function handles the badaccess to memory. // It is called from the signal handler or the exception handler. static bool handle_badaccess(SIGSEGV_FAULT_HANDLER_ARGLIST_1) { + sigsegv_info_t SI; + SI.addr = (sigsegv_address_t)SIGSEGV_FAULT_ADDRESS_FAST; + SI.pc = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION_FAST; #ifdef HAVE_MACH_EXCEPTIONS - // We must match the initial count when writing back the CPU state registers - kern_return_t krc; - mach_msg_type_number_t count; - - count = SIGSEGV_THREAD_STATE_COUNT; - krc = thread_get_state(thread, SIGSEGV_THREAD_STATE_FLAVOR, (thread_state_t)state, &count); - MACH_CHECK_ERROR (thread_get_state, krc); + SI.thread = thread; + SI.has_exc_state = false; + SI.has_thr_state = false; #endif + sigsegv_info_t * const SIP = &SI; - sigsegv_address_t fault_address = (sigsegv_address_t)SIGSEGV_FAULT_ADDRESS; - sigsegv_address_t fault_instruction = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION; - // Call user's handler and reinstall the global handler, if required - switch (SIGSEGV_FAULT_HANDLER_INVOKE(fault_address, fault_instruction)) { + switch (SIGSEGV_FAULT_HANDLER_INVOKE(SIP)) { case SIGSEGV_RETURN_SUCCESS: return true; @@ -1610,16 +1763,17 @@ static bool handle_badaccess(SIGSEGV_FAU case SIGSEGV_RETURN_SKIP_INSTRUCTION: // Call the instruction skipper with the register file // available +#ifdef HAVE_MACH_EXCEPTIONS + if (!SIP->has_thr_state) + mach_get_thread_state(SIP); +#endif if (SIGSEGV_SKIP_INSTRUCTION(SIGSEGV_REGISTER_FILE)) { #ifdef HAVE_MACH_EXCEPTIONS // Unlike UNIX signals where the thread state // is modified off of the stack, in Mach we // need to actually call thread_set_state to // have the register values updated. - krc = thread_set_state(thread, - SIGSEGV_THREAD_STATE_FLAVOR, (thread_state_t)state, - count); - MACH_CHECK_ERROR (thread_set_state, krc); + mach_set_thread_state(SIP); #endif return true; } @@ -1628,7 +1782,7 @@ static bool handle_badaccess(SIGSEGV_FAU case SIGSEGV_RETURN_FAILURE: // We can't do anything with the fault_address, dump state? if (sigsegv_state_dumper != 0) - sigsegv_state_dumper(fault_address, fault_instruction); + sigsegv_state_dumper(SIP); break; } @@ -1689,13 +1843,18 @@ forward_exception(mach_port_t thread_por behavior = oldExceptionPorts->behaviors[portIndex]; flavor = oldExceptionPorts->flavors[portIndex]; + if (!VALID_THREAD_STATE_FLAVOR(flavor)) { + fprintf(stderr, "Invalid thread_state flavor = %d. Not forwarding\n", flavor); + return KERN_FAILURE; + } + /* fprintf(stderr, "forwarding exception, port = 0x%x, behaviour = %d, flavor = %d\n", port, behavior, flavor); */ if (behavior != EXCEPTION_DEFAULT) { thread_state_count = THREAD_STATE_MAX; - kret = thread_get_state (thread_port, flavor, thread_state, + kret = thread_get_state (thread_port, flavor, (natural_t *)&thread_state, &thread_state_count); MACH_CHECK_ERROR (thread_get_state, kret); } @@ -1711,8 +1870,8 @@ forward_exception(mach_port_t thread_por // fprintf(stderr, "forwarding to exception_raise_state\n"); kret = exception_raise_state(port, exception_type, exception_data, data_count, &flavor, - thread_state, thread_state_count, - thread_state, &thread_state_count); + (natural_t *)&thread_state, thread_state_count, + (natural_t *)&thread_state, &thread_state_count); MACH_CHECK_ERROR (exception_raise_state, kret); break; case EXCEPTION_STATE_IDENTITY: @@ -1720,22 +1879,23 @@ forward_exception(mach_port_t thread_por kret = exception_raise_state_identity(port, thread_port, task_port, exception_type, exception_data, data_count, &flavor, - thread_state, thread_state_count, - thread_state, &thread_state_count); + (natural_t *)&thread_state, thread_state_count, + (natural_t *)&thread_state, &thread_state_count); MACH_CHECK_ERROR (exception_raise_state_identity, kret); break; default: fprintf(stderr, "forward_exception got unknown behavior\n"); + kret = KERN_FAILURE; break; } if (behavior != EXCEPTION_DEFAULT) { - kret = thread_set_state (thread_port, flavor, thread_state, + kret = thread_set_state (thread_port, flavor, (natural_t *)&thread_state, thread_state_count); MACH_CHECK_ERROR (thread_set_state, kret); } - return KERN_SUCCESS; + return kret; } /* @@ -1763,20 +1923,24 @@ catch_exception_raise(mach_port_t except mach_port_t task, exception_type_t exception, exception_data_t code, - mach_msg_type_number_t codeCount) + mach_msg_type_number_t code_count) { - SIGSEGV_THREAD_STATE_TYPE state; kern_return_t krc; - if ((exception == EXC_BAD_ACCESS) && (codeCount >= 2)) { - if (handle_badaccess(SIGSEGV_FAULT_HANDLER_ARGS)) - return KERN_SUCCESS; + if (exception == EXC_BAD_ACCESS) { + switch (code[0]) { + case KERN_PROTECTION_FAILURE: + case KERN_INVALID_ADDRESS: + if (handle_badaccess(SIGSEGV_FAULT_HANDLER_ARGS)) + return KERN_SUCCESS; + break; + } } // In Mach we do not need to remove the exception handler. // If we forward the exception, eventually some exception handler // will take care of this exception. - krc = forward_exception(thread, task, exception, code, codeCount, &ports); + krc = forward_exception(thread, task, exception, code, code_count, &ports); return krc; } @@ -2088,13 +2252,22 @@ static int page_size; static volatile char * page = 0; static volatile int handler_called = 0; +/* Barriers */ +#ifdef __GNUC__ +#define BARRIER() asm volatile ("" : : : "memory") +#else +#define BARRIER() /* nothing */ +#endif + #ifdef __GNUC__ // Code range where we expect the fault to come from static void *b_region, *e_region; #endif -static sigsegv_return_t sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) +static sigsegv_return_t sigsegv_test_handler(sigsegv_info_t *sip) { + const sigsegv_address_t fault_address = sigsegv_get_fault_address(sip); + const sigsegv_address_t instruction_address = sigsegv_get_fault_instruction_address(sip); #if DEBUG printf("sigsegv_test_handler(%p, %p)\n", fault_address, instruction_address); printf("expected fault at %p\n", page + REF_INDEX); @@ -2108,7 +2281,7 @@ static sigsegv_return_t sigsegv_test_han #ifdef __GNUC__ // Make sure reported fault instruction address falls into // expected code range - if (instruction_address != SIGSEGV_INVALID_PC + if (instruction_address != SIGSEGV_INVALID_ADDRESS && ((instruction_address < (sigsegv_address_t)b_region) || (instruction_address >= (sigsegv_address_t)e_region))) exit(11); @@ -2119,8 +2292,10 @@ static sigsegv_return_t sigsegv_test_han } #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION -static sigsegv_return_t sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address) +static sigsegv_return_t sigsegv_insn_handler(sigsegv_info_t *sip) { + const sigsegv_address_t fault_address = sigsegv_get_fault_address(sip); + const sigsegv_address_t instruction_address = sigsegv_get_fault_instruction_address(sip); #if DEBUG printf("sigsegv_insn_handler(%p, %p)\n", fault_address, instruction_address); #endif @@ -2128,7 +2303,7 @@ static sigsegv_return_t sigsegv_insn_han #ifdef __GNUC__ // Make sure reported fault instruction address falls into // expected code range - if (instruction_address != SIGSEGV_INVALID_PC + if (instruction_address != SIGSEGV_INVALID_ADDRESS && ((instruction_address < (sigsegv_address_t)b_region) || (instruction_address >= (sigsegv_address_t)e_region))) return SIGSEGV_RETURN_FAILURE; @@ -2179,6 +2354,8 @@ static bool arch_insn_skipper_tests() 0x4c, 0x89, 0x18, // mov %r11,(%rax) 0x4a, 0x89, 0x0c, 0x10, // mov %rcx,(%rax,%r10,1) 0x4e, 0x89, 0x1c, 0x10, // mov %r11,(%rax,%r10,1) + 0x63, 0x47, 0x04, // movslq 4(%rdi),%eax + 0x48, 0x63, 0x47, 0x04, // movslq 4(%rdi),%rax #endif 0 // end }; @@ -2212,17 +2389,30 @@ int main(void) if (!sigsegv_install_handler(sigsegv_test_handler)) return 4; - + #ifdef __GNUC__ b_region = &&L_b_region1; e_region = &&L_e_region1; #endif - L_b_region1: - page[REF_INDEX] = REF_VALUE; - if (page[REF_INDEX] != REF_VALUE) - exit(20); - page[REF_INDEX] = REF_VALUE; - L_e_region1: + /* This is a really awful hack but otherwise gcc is smart enough + * (or bug'ous enough?) to optimize the labels and place them + * e.g. at the "main" entry point, which is wrong. + */ + volatile int label_hack = 1; + switch (label_hack) { + case 1: + L_b_region1: + page[REF_INDEX] = REF_VALUE; + if (page[REF_INDEX] != REF_VALUE) + exit(20); + page[REF_INDEX] = REF_VALUE; + BARRIER(); + // fall-through + case 2: + L_e_region1: + BARRIER(); + break; + } if (handler_called != 1) return 5; @@ -2253,17 +2443,24 @@ int main(void) b_region = &&L_b_region2; e_region = &&L_e_region2; #endif - L_b_region2: - TEST_SKIP_INSTRUCTION(unsigned char); - TEST_SKIP_INSTRUCTION(unsigned short); - TEST_SKIP_INSTRUCTION(unsigned int); - TEST_SKIP_INSTRUCTION(unsigned long); - TEST_SKIP_INSTRUCTION(signed char); - TEST_SKIP_INSTRUCTION(signed short); - TEST_SKIP_INSTRUCTION(signed int); - TEST_SKIP_INSTRUCTION(signed long); - L_e_region2: - + switch (label_hack) { + case 1: + L_b_region2: + TEST_SKIP_INSTRUCTION(unsigned char); + TEST_SKIP_INSTRUCTION(unsigned short); + TEST_SKIP_INSTRUCTION(unsigned int); + TEST_SKIP_INSTRUCTION(unsigned long); + TEST_SKIP_INSTRUCTION(signed char); + TEST_SKIP_INSTRUCTION(signed short); + TEST_SKIP_INSTRUCTION(signed int); + TEST_SKIP_INSTRUCTION(signed long); + BARRIER(); + // fall-through + case 2: + L_e_region2: + BARRIER(); + break; + } if (!arch_insn_skipper_tests()) return 20; #endif