--- BasiliskII/src/Unix/sigsegv.cpp 2008/01/01 09:40:33 1.71 +++ BasiliskII/src/Unix/sigsegv.cpp 2009/04/13 23:13:20 1.89 @@ -49,6 +49,18 @@ using std::list; #define RETSIGTYPE void #endif +// Size of an unsigned integer large enough to hold all bits of a pointer +// NOTE: this can be different than SIGSEGV_REGISTER_TYPE. In +// particular, on ILP32 systems with a 64-bit kernel (HP-UX/ia64?) +#if defined(HAVE_WIN32_VM) +// Windows is either ILP32 or LLP64 +#include +typedef UINT_PTR sigsegv_uintptr_t; +#else +// Other systems are sane enough to follow ILP32 or LP64 models +typedef unsigned long sigsegv_uintptr_t; +#endif + // Type of the system signal handler typedef RETSIGTYPE (*signal_handler)(int); @@ -70,7 +82,7 @@ static bool sigsegv_do_install_handler(i enum transfer_type_t { SIGSEGV_TRANSFER_UNKNOWN = 0, SIGSEGV_TRANSFER_LOAD = 1, - SIGSEGV_TRANSFER_STORE = 2, + SIGSEGV_TRANSFER_STORE = 2 }; // Transfer size @@ -79,7 +91,7 @@ enum transfer_size_t { SIZE_BYTE, SIZE_WORD, // 2 bytes SIZE_LONG, // 4 bytes - SIZE_QUAD, // 8 bytes + SIZE_QUAD // 8 bytes }; #if (defined(powerpc) || defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)) @@ -232,6 +244,8 @@ static void powerpc_decode_instruction(i // Generic extended signal handler #if defined(__FreeBSD__) #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGBUS) +#elif defined(__hpux) +#define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) FAULT_HANDLER(SIGBUS) #else #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV) #endif @@ -264,14 +278,14 @@ static void powerpc_decode_instruction(i #include #define SIGSEGV_CONTEXT_REGS (((ucontext_t *)scp)->uc_mcontext.gregs) #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS[EIP] -#define SIGSEGV_REGISTER_FILE (unsigned long *)SIGSEGV_CONTEXT_REGS +#define SIGSEGV_REGISTER_FILE (SIGSEGV_REGISTER_TYPE *)SIGSEGV_CONTEXT_REGS #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) #if (defined(i386) || defined(__i386__)) #define SIGSEGV_FAULT_INSTRUCTION (((struct sigcontext *)scp)->sc_eip) -#define SIGSEGV_REGISTER_FILE ((unsigned long *)&(((struct sigcontext *)scp)->sc_edi)) /* EDI is the first GPR (even below EIP) in sigcontext */ +#define SIGSEGV_REGISTER_FILE ((SIGSEGV_REGISTER_TYPE *)&(((struct sigcontext *)scp)->sc_edi)) /* EDI is the first GPR (even below EIP) in sigcontext */ #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #endif @@ -280,7 +294,7 @@ static void powerpc_decode_instruction(i #include #define SIGSEGV_CONTEXT_REGS (((ucontext_t *)scp)->uc_mcontext.__gregs) #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS[_REG_EIP] -#define SIGSEGV_REGISTER_FILE (unsigned long *)SIGSEGV_CONTEXT_REGS +#define SIGSEGV_REGISTER_FILE (SIGSEGV_REGISTER_TYPE *)SIGSEGV_CONTEXT_REGS #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #if (defined(powerpc) || defined(__powerpc__)) @@ -296,18 +310,21 @@ static void powerpc_decode_instruction(i #include #define SIGSEGV_CONTEXT_REGS (((ucontext_t *)scp)->uc_mcontext.gregs) #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS[14] /* should use REG_EIP instead */ -#define SIGSEGV_REGISTER_FILE (unsigned long *)SIGSEGV_CONTEXT_REGS +#define SIGSEGV_REGISTER_FILE (SIGSEGV_REGISTER_TYPE *)SIGSEGV_CONTEXT_REGS #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #if (defined(x86_64) || defined(__x86_64__)) #include #define SIGSEGV_CONTEXT_REGS (((ucontext_t *)scp)->uc_mcontext.gregs) #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS[16] /* should use REG_RIP instead */ -#define SIGSEGV_REGISTER_FILE (unsigned long *)SIGSEGV_CONTEXT_REGS +#define SIGSEGV_REGISTER_FILE (SIGSEGV_REGISTER_TYPE *)SIGSEGV_CONTEXT_REGS #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #if (defined(ia64) || defined(__ia64__)) -#define SIGSEGV_FAULT_INSTRUCTION (((struct sigcontext *)scp)->sc_ip & ~0x3ULL) /* slot number is in bits 0 and 1 */ +#define SIGSEGV_CONTEXT_REGS ((struct sigcontext *)scp) +#define SIGSEGV_FAULT_INSTRUCTION (SIGSEGV_CONTEXT_REGS->sc_ip & ~0x3ULL) /* slot number is in bits 0 and 1 */ +#define SIGSEGV_REGISTER_FILE SIGSEGV_CONTEXT_REGS +#define SIGSEGV_SKIP_INSTRUCTION ia64_skip_instruction #endif #if (defined(powerpc) || defined(__powerpc__)) #include @@ -335,6 +352,36 @@ static void powerpc_decode_instruction(i #define SIGSEGV_SKIP_INSTRUCTION mips_skip_instruction #endif #endif +#if (defined(__hpux) || defined(__hpux__)) +#if (defined(__hppa) || defined(__hppa__)) +#define SIGSEGV_CONTEXT_REGS (&((ucontext_t *)scp)->uc_mcontext) +#define SIGSEGV_FAULT_INSTRUCTION_32 (SIGSEGV_CONTEXT_REGS->ss_narrow.ss_pcoq_head & ~3ul) +#define SIGSEGV_FAULT_INSTRUCTION_64 (SIGSEGV_CONTEXT_REGS->ss_wide.ss_64.ss_pcoq_head & ~3ull) +#if defined(__LP64__) +#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_FAULT_INSTRUCTION_64 +#else +#define SIGSEGV_FAULT_INSTRUCTION ((SIGSEGV_CONTEXT_REGS->ss_flags & SS_WIDEREGS) ? \ + (uint32_t)SIGSEGV_FAULT_INSTRUCTION_64 : \ + SIGSEGV_FAULT_INSTRUCTION_32) +#endif +#endif +#if (defined(__ia64) || defined(__ia64__)) +#include +#define SIGSEGV_CONTEXT_REGS ((ucontext_t *)scp) +#define SIGSEGV_FAULT_INSTRUCTION get_fault_instruction(SIGSEGV_CONTEXT_REGS) +#define SIGSEGV_REGISTER_FILE SIGSEGV_CONTEXT_REGS +#define SIGSEGV_SKIP_INSTRUCTION ia64_skip_instruction + +#include +static inline sigsegv_address_t get_fault_instruction(const ucontext_t *ucp) +{ + uint64_t ip; + if (__uc_get_ip(ucp, &ip) != 0) + return SIGSEGV_INVALID_ADDRESS; + return (sigsegv_address_t)(ip & ~3ULL); +} +#endif +#endif #endif #if HAVE_SIGCONTEXT_SUBTERFUGE @@ -348,7 +395,7 @@ static void powerpc_decode_instruction(i #define SIGSEGV_FAULT_HANDLER_ARGS &scs #define SIGSEGV_FAULT_ADDRESS scp->cr2 #define SIGSEGV_FAULT_INSTRUCTION scp->eip -#define SIGSEGV_REGISTER_FILE (unsigned long *)scp +#define SIGSEGV_REGISTER_FILE (SIGSEGV_REGISTER_TYPE *)scp #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #if (defined(sparc) || defined(__sparc__)) @@ -469,7 +516,7 @@ static sigsegv_address_t get_fault_addre #define SIGSEGV_FAULT_HANDLER_ARGS sig, code, scp, addr #define SIGSEGV_FAULT_ADDRESS addr #define SIGSEGV_FAULT_INSTRUCTION scp->sc_eip -#define SIGSEGV_REGISTER_FILE ((unsigned long *)&scp->sc_edi) +#define SIGSEGV_REGISTER_FILE ((SIGSEGV_REGISTER_TYPE *)&scp->sc_edi) #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif #if (defined(alpha) || defined(__alpha__)) @@ -504,10 +551,10 @@ static sigsegv_address_t get_fault_addre #ifndef HAVE_MACH_EXCEPTIONS #if defined(__APPLE__) && defined(__MACH__) #if (defined(ppc) || defined(__ppc__)) -#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp +#define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct __darwin_sigcontext *scp #define SIGSEGV_FAULT_HANDLER_ARGS sig, code, scp #define SIGSEGV_FAULT_ADDRESS get_fault_address(scp) -#define SIGSEGV_FAULT_INSTRUCTION scp->sc_ir +#define SIGSEGV_FAULT_INSTRUCTION scp->MACH_FIELD_NAME(sc_ir) #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGBUS) #define SIGSEGV_REGISTER_FILE (unsigned int *)&scp->sc_ir, &((unsigned int *) scp->sc_regs)[2] #define SIGSEGV_SKIP_INSTRUCTION powerpc_skip_instruction @@ -515,11 +562,11 @@ static sigsegv_address_t get_fault_addre // Use decoding scheme from SheepShaver static sigsegv_address_t get_fault_address(struct sigcontext *scp) { - unsigned int nip = (unsigned int) scp->sc_ir; - unsigned int * gpr = &((unsigned int *) scp->sc_regs)[2]; + unsigned int nip = (unsigned int) scp->MACH_FIELD_NAME(sc_ir); + unsigned int * gpr = &((unsigned int *) scp->MACH_FIELD_NAME(sc_regs))[2]; instruction_t instr; - powerpc_decode_instruction(&instr, nip, gpr); + powerpc_decode_instruction(&instr, nip, (long unsigned int*)gpr); return (sigsegv_address_t)instr.addr; } #endif @@ -536,10 +583,20 @@ static sigsegv_address_t get_fault_addre #define SIGSEGV_FAULT_HANDLER_ARGS ExceptionInfo #define SIGSEGV_FAULT_ADDRESS ExceptionInfo->ExceptionRecord->ExceptionInformation[1] #define SIGSEGV_CONTEXT_REGS ExceptionInfo->ContextRecord +#if defined(_M_IX86) #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS->Eip -#define SIGSEGV_REGISTER_FILE ((unsigned long *)&SIGSEGV_CONTEXT_REGS->Edi) +#define SIGSEGV_REGISTER_FILE ((SIGSEGV_REGISTER_TYPE *)&SIGSEGV_CONTEXT_REGS->Edi) +#define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction +#endif +#if defined(_M_X64) +#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS->Rip +#define SIGSEGV_REGISTER_FILE ((SIGSEGV_REGISTER_TYPE *)&SIGSEGV_CONTEXT_REGS->Rax) #define SIGSEGV_SKIP_INSTRUCTION ix86_skip_instruction #endif +#if defined(_M_IA64) +#define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_CONTEXT_REGS->StIIP +#endif +#endif #if HAVE_MACH_EXCEPTIONS @@ -561,16 +618,43 @@ extern "C" { #include #include -extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *); -extern kern_return_t catch_exception_raise(mach_port_t, mach_port_t, - mach_port_t, exception_type_t, exception_data_t, mach_msg_type_number_t); -extern kern_return_t exception_raise(mach_port_t, mach_port_t, mach_port_t, - exception_type_t, exception_data_t, mach_msg_type_number_t); -extern kern_return_t exception_raise_state(mach_port_t, exception_type_t, - exception_data_t, mach_msg_type_number_t, thread_state_flavor_t *, +#ifndef HAVE_MACH64_VM + +// Undefine this to prevent a preprocessor warning when compiling on a +// 32-bit machine with Mac OS X 10.5. +#undef MACH_EXCEPTION_CODES + +#define MACH_EXCEPTION_CODES 0 +#define mach_exception_data_t exception_data_t +#define mach_exception_data_type_t exception_data_type_t +#define mach_exc_server exc_server +#define catch_mach_exception_raise catch_exception_raise +#define mach_exception_raise exception_raise +#define mach_exception_raise_state exception_raise_state +#define mach_exception_raise_state_identity exception_raise_state_identity +#endif + +extern boolean_t mach_exc_server(mach_msg_header_t *, mach_msg_header_t *); +extern kern_return_t catch_mach_exception_raise(mach_port_t, mach_port_t, + mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t); +extern kern_return_t catch_mach_exception_raise_state(mach_port_t exception_port, + exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t code_count, + int *flavor, + thread_state_t old_state, mach_msg_type_number_t old_state_count, + thread_state_t new_state, mach_msg_type_number_t *new_state_count); +extern kern_return_t catch_mach_exception_raise_state_identity(mach_port_t exception_port, + mach_port_t thread_port, mach_port_t task_port, exception_type_t exception, + mach_exception_data_t code, mach_msg_type_number_t code_count, + int *flavor, + thread_state_t old_state, mach_msg_type_number_t old_state_count, + thread_state_t new_state, mach_msg_type_number_t *new_state_count); +extern kern_return_t mach_exception_raise(mach_port_t, mach_port_t, mach_port_t, + exception_type_t, mach_exception_data_t, mach_msg_type_number_t); +extern kern_return_t mach_exception_raise_state(mach_port_t, exception_type_t, + mach_exception_data_t, mach_msg_type_number_t, thread_state_flavor_t *, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t *); -extern kern_return_t exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, - exception_type_t, exception_data_t, mach_msg_type_number_t, thread_state_flavor_t *, +extern kern_return_t mach_exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, + exception_type_t, mach_exception_data_t, mach_msg_type_number_t, thread_state_flavor_t *, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t *); } @@ -601,58 +685,9 @@ if (ret != KERN_SUCCESS) { \ exit (1); \ } -#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 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 __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__ -#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_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 */ +#ifndef MACH_FIELD_NAME +#define MACH_FIELD_NAME(X) X #endif -#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_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. @@ -683,7 +718,7 @@ handleExceptions(void *priv) _exceptionPort, 0, MACH_PORT_NULL); MACH_CHECK_ERROR(mach_msg, krc); - if (!exc_server(msg, reply)) { + if (!mach_exc_server(msg, reply)) { fprintf(stderr, "exc_server hated the message\n"); exit(1); } @@ -705,9 +740,13 @@ handleExceptions(void *priv) * Instruction skipping */ +#ifndef SIGSEGV_REGISTER_TYPE +#define SIGSEGV_REGISTER_TYPE sigsegv_uintptr_t +#endif + #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION // Decode and skip X86 instruction -#if (defined(i386) || defined(__i386__)) || defined(__x86_64__) +#if (defined(i386) || defined(__i386__)) || (defined(__x86_64__) || defined(_M_X64)) #if defined(__linux__) enum { #if (defined(i386) || defined(__i386__)) @@ -858,7 +897,7 @@ enum { #endif #if defined(_WIN32) enum { -#if (defined(i386) || defined(__i386__)) +#if defined(_M_IX86) X86_REG_EIP = 7, X86_REG_EAX = 5, X86_REG_ECX = 4, @@ -869,6 +908,25 @@ enum { X86_REG_ESI = 1, X86_REG_EDI = 0 #endif +#if defined(_M_X64) + X86_REG_EAX = 0, + X86_REG_ECX = 1, + X86_REG_EDX = 2, + X86_REG_EBX = 3, + X86_REG_ESP = 4, + X86_REG_EBP = 5, + X86_REG_ESI = 6, + X86_REG_EDI = 7, + 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_EIP = 16 +#endif }; #endif // FIXME: this is partly redundant with the instruction decoding phase @@ -905,7 +963,7 @@ static inline int ix86_step_over_modrm(u return offset; } -static bool ix86_skip_instruction(unsigned long * regs) +static bool ix86_skip_instruction(SIGSEGV_REGISTER_TYPE * regs) { unsigned char * eip = (unsigned char *)regs[X86_REG_EIP]; @@ -941,7 +999,7 @@ static bool ix86_skip_instruction(unsign } // REX prefix -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(_M_X64) struct rex_t { unsigned char W; unsigned char R; @@ -993,7 +1051,7 @@ static bool ix86_skip_instruction(unsign goto do_transfer_load; } break; -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(_M_X64) case 0x63: // MOVSXD r64, r/m32 if (has_rex && rex.W) { transfer_size = SIZE_LONG; @@ -1064,7 +1122,7 @@ static bool ix86_skip_instruction(unsign return false; } -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(_M_X64) if (rex.R) reg += 8; #endif @@ -1073,7 +1131,7 @@ static bool ix86_skip_instruction(unsign 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, -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(_M_X64) X86_REG_R8, X86_REG_R9, X86_REG_R10, X86_REG_R11, X86_REG_R12, X86_REG_R13, X86_REG_R14, X86_REG_R15, #endif @@ -1160,6 +1218,789 @@ static bool ix86_skip_instruction(unsign } #endif +// Decode and skip IA-64 instruction +#if defined(__ia64) || defined(__ia64__) +typedef uint64_t ia64_bundle_t[2]; +#if defined(__linux__) +// We can directly patch the slot number +#define IA64_CAN_PATCH_IP_SLOT 1 +// Helper macros to access the machine context +#define IA64_CONTEXT_TYPE struct sigcontext * +#define IA64_CONTEXT scp +#define IA64_GET_IP() (IA64_CONTEXT->sc_ip) +#define IA64_SET_IP(V) (IA64_CONTEXT->sc_ip = (V)) +#define IA64_GET_PR(P) ((IA64_CONTEXT->sc_pr >> (P)) & 1) +#define IA64_GET_NAT(I) ((IA64_CONTEXT->sc_nat >> (I)) & 1) +#define IA64_GET_GR(R) (IA64_CONTEXT->sc_gr[(R)]) +#define _IA64_SET_GR(R,V) (IA64_CONTEXT->sc_gr[(R)] = (V)) +#define _IA64_SET_NAT(I,V) (IA64_CONTEXT->sc_nat = (IA64_CONTEXT->sc_nat & ~(1ull << (I))) | (((uint64_t)!!(V)) << (I))) +#define IA64_SET_GR(R,V,N) (_IA64_SET_GR(R,V), _IA64_SET_NAT(R,N)) + +// Load bundle (in little-endian) +static inline void ia64_load_bundle(ia64_bundle_t bundle, uint64_t raw_ip) +{ + uint64_t *ip = (uint64_t *)(raw_ip & ~3ull); + bundle[0] = ip[0]; + bundle[1] = ip[1]; +} +#endif +#if defined(__hpux) || defined(__hpux__) +// We can directly patch the slot number +#define IA64_CAN_PATCH_IP_SLOT 1 +// Helper macros to access the machine context +#define IA64_CONTEXT_TYPE ucontext_t * +#define IA64_CONTEXT ucp +#define IA64_GET_IP() ia64_get_ip(IA64_CONTEXT) +#define IA64_SET_IP(V) ia64_set_ip(IA64_CONTEXT, V) +#define IA64_GET_PR(P) ia64_get_pr(IA64_CONTEXT, P) +#define IA64_GET_NAT(I) ia64_get_nat(IA64_CONTEXT, I) +#define IA64_GET_GR(R) ia64_get_gr(IA64_CONTEXT, R) +#define IA64_SET_GR(R,V,N) ia64_set_gr(IA64_CONTEXT, R, V, N) +#define UC_ACCESS(FUNC,ARGS) do { if (__uc_##FUNC ARGS != 0) abort(); } while (0) + +static inline uint64_t ia64_get_ip(IA64_CONTEXT_TYPE IA64_CONTEXT) + { uint64_t v; UC_ACCESS(get_ip,(IA64_CONTEXT, &v)); return v; } +static inline void ia64_set_ip(IA64_CONTEXT_TYPE IA64_CONTEXT, uint64_t v) + { UC_ACCESS(set_ip,(IA64_CONTEXT, v)); } +static inline unsigned int ia64_get_pr(IA64_CONTEXT_TYPE IA64_CONTEXT, int pr) + { uint64_t v; UC_ACCESS(get_prs,(IA64_CONTEXT, &v)); return (v >> pr) & 1; } +static inline unsigned int ia64_get_nat(IA64_CONTEXT_TYPE IA64_CONTEXT, int r) + { uint64_t v; unsigned int nat; UC_ACCESS(get_grs,(IA64_CONTEXT, r, 1, &v, &nat)); return (nat >> r) & 1; } +static inline uint64_t ia64_get_gr(IA64_CONTEXT_TYPE IA64_CONTEXT, int r) + { uint64_t v; unsigned int nat; UC_ACCESS(get_grs,(IA64_CONTEXT, r, 1, &v, &nat)); return v; } + +static void ia64_set_gr(IA64_CONTEXT_TYPE IA64_CONTEXT, int r, uint64_t v, unsigned int nat) +{ + if (r == 0) + return; + if (r > 0 && r < 32) + UC_ACCESS(set_grs,(IA64_CONTEXT, r, 1, &v, (!!nat) << r)); + else { + uint64_t bsp, bspstore; + UC_ACCESS(get_ar_bsp,(IA64_CONTEXT, &bsp)); + UC_ACCESS(get_ar_bspstore,(IA64_CONTEXT, &bspstore)); + abort(); /* XXX: use libunwind, this is not fun... */ + } +} + +// Byte-swapping +#if defined(__GNUC__) +#define BSWAP64(V) ({ uint64_t r; __asm__ __volatile__("mux1 %0=%1,@rev;;" : "=r" (r) : "r" (V)); r; }) +#elif defined (__HP_aCC) +#define BSWAP64(V) _Asm_mux1(_MBTYPE_REV, V) +#else +#error "Define byte-swap instruction" +#endif + +// Load bundle (in little-endian) +static inline void ia64_load_bundle(ia64_bundle_t bundle, uint64_t raw_ip) +{ + uint64_t *ip = (uint64_t *)(raw_ip & ~3ull); + bundle[0] = BSWAP64(ip[0]); + bundle[1] = BSWAP64(ip[1]); +} +#endif + +// Instruction operations +enum { + IA64_INST_UNKNOWN = 0, + IA64_INST_LD1, // ld1 op0=[op1] + IA64_INST_LD1_UPDATE, // ld1 op0=[op1],op2 + IA64_INST_LD2, // ld2 op0=[op1] + IA64_INST_LD2_UPDATE, // ld2 op0=[op1],op2 + IA64_INST_LD4, // ld4 op0=[op1] + IA64_INST_LD4_UPDATE, // ld4 op0=[op1],op2 + IA64_INST_LD8, // ld8 op0=[op1] + IA64_INST_LD8_UPDATE, // ld8 op0=[op1],op2 + IA64_INST_ST1, // st1 [op0]=op1 + IA64_INST_ST1_UPDATE, // st1 [op0]=op1,op2 + IA64_INST_ST2, // st2 [op0]=op1 + IA64_INST_ST2_UPDATE, // st2 [op0]=op1,op2 + IA64_INST_ST4, // st4 [op0]=op1 + IA64_INST_ST4_UPDATE, // st4 [op0]=op1,op2 + IA64_INST_ST8, // st8 [op0]=op1 + IA64_INST_ST8_UPDATE, // st8 [op0]=op1,op2 + IA64_INST_ADD, // add op0=op1,op2,op3 + IA64_INST_SUB, // sub op0=op1,op2,op3 + IA64_INST_SHLADD, // shladd op0=op1,op3,op2 + IA64_INST_AND, // and op0=op1,op2 + IA64_INST_ANDCM, // andcm op0=op1,op2 + IA64_INST_OR, // or op0=op1,op2 + IA64_INST_XOR, // xor op0=op1,op2 + IA64_INST_SXT1, // sxt1 op0=op1 + IA64_INST_SXT2, // sxt2 op0=op1 + IA64_INST_SXT4, // sxt4 op0=op1 + IA64_INST_ZXT1, // zxt1 op0=op1 + IA64_INST_ZXT2, // zxt2 op0=op1 + IA64_INST_ZXT4, // zxt4 op0=op1 + IA64_INST_NOP // nop op0 +}; + +const int IA64_N_OPERANDS = 4; + +// Decoded operand type +struct ia64_operand_t { + uint8_t commit; // commit result of operation to register file? + uint8_t valid; // XXX: not really used, can be removed (debug) + int8_t index; // index of GPR, or -1 if immediate value + uint8_t nat; // NaT state before operation + uint64_t value; // register contents or immediate value +}; + +// Decoded instruction type +struct ia64_instruction_t { + uint8_t mnemo; // operation to perform + uint8_t pred; // predicate register to check + uint8_t no_memory; // used to emulated main fault instruction + uint64_t inst; // the raw instruction bits (41-bit wide) + ia64_operand_t operands[IA64_N_OPERANDS]; +}; + +// Get immediate sign-bit +static inline int ia64_inst_get_sbit(uint64_t inst) +{ + return (inst >> 36) & 1; +} + +// Get 8-bit immediate value (A3, A8, I27, M30) +static inline uint64_t ia64_inst_get_imm8(uint64_t inst) +{ + uint64_t value = (inst >> 13) & 0x7full; + if (ia64_inst_get_sbit(inst)) + value |= ~0x7full; + return value; +} + +// Get 9-bit immediate value (M3) +static inline uint64_t ia64_inst_get_imm9b(uint64_t inst) +{ + uint64_t value = (((inst >> 27) & 1) << 7) | ((inst >> 13) & 0x7f); + if (ia64_inst_get_sbit(inst)) + value |= ~0xffull; + return value; +} + +// Get 9-bit immediate value (M5) +static inline uint64_t ia64_inst_get_imm9a(uint64_t inst) +{ + uint64_t value = (((inst >> 27) & 1) << 7) | ((inst >> 6) & 0x7f); + if (ia64_inst_get_sbit(inst)) + value |= ~0xffull; + return value; +} + +// Get 14-bit immediate value (A4) +static inline uint64_t ia64_inst_get_imm14(uint64_t inst) +{ + uint64_t value = (((inst >> 27) & 0x3f) << 7) | (inst & 0x7f); + if (ia64_inst_get_sbit(inst)) + value |= ~0x1ffull; + return value; +} + +// Get 22-bit immediate value (A5) +static inline uint64_t ia64_inst_get_imm22(uint64_t inst) +{ + uint64_t value = ((((inst >> 22) & 0x1f) << 16) | + (((inst >> 27) & 0x1ff) << 7) | + (inst & 0x7f)); + if (ia64_inst_get_sbit(inst)) + value |= ~0x1fffffull; + return value; +} + +// Get 21-bit immediate value (I19) +static inline uint64_t ia64_inst_get_imm21(uint64_t inst) +{ + return (((inst >> 36) & 1) << 20) | ((inst >> 6) & 0xfffff); +} + +// Get 2-bit count value (A2) +static inline int ia64_inst_get_count2(uint64_t inst) +{ + return (inst >> 27) & 0x3; +} + +// Get bundle template +static inline unsigned int ia64_get_template(uint64_t ip) +{ + ia64_bundle_t bundle; + ia64_load_bundle(bundle, ip); + return bundle[0] & 0x1f; +} + +// Get specified instruction in bundle +static uint64_t ia64_get_instruction(uint64_t ip, int slot) +{ + uint64_t inst; + ia64_bundle_t bundle; + ia64_load_bundle(bundle, ip); +#if DEBUG + printf("Bundle: %016llx%016llx\n", bundle[1], bundle[0]); +#endif + + switch (slot) { + case 0: + inst = (bundle[0] >> 5) & 0x1ffffffffffull; + break; + case 1: + inst = ((bundle[1] & 0x7fffffull) << 18) | ((bundle[0] >> 46) & 0x3ffffull); + break; + case 2: + inst = (bundle[1] >> 23) & 0x1ffffffffffull; + break; + case 3: + fprintf(stderr, "ERROR: ia64_get_instruction(), invalid slot number %d\n", slot); + abort(); + break; + } + +#if DEBUG + printf(" Instruction %d: 0x%016llx\n", slot, inst); +#endif + return inst; +} + +// Decode group 0 instructions +static bool ia64_decode_instruction_0(ia64_instruction_t *inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + const int r1 = (inst->inst >> 6) & 0x7f; + const int r3 = (inst->inst >> 20) & 0x7f; + + const int x3 = (inst->inst >> 33) & 0x07; + const int x6 = (inst->inst >> 27) & 0x3f; + const int x2 = (inst->inst >> 31) & 0x03; + const int x4 = (inst->inst >> 27) & 0x0f; + + if (x3 == 0) { + switch (x6) { + case 0x01: // nop.i (I19) + inst->mnemo = IA64_INST_NOP; + inst->operands[0].valid = true; + inst->operands[0].index = -1; + inst->operands[0].value = ia64_inst_get_imm21(inst->inst); + return true; + case 0x14: // sxt1 (I29) + case 0x15: // sxt2 (I29) + case 0x16: // sxt4 (I29) + case 0x10: // zxt1 (I29) + case 0x11: // zxt2 (I29) + case 0x12: // zxt4 (I29) + switch (x6) { + case 0x14: inst->mnemo = IA64_INST_SXT1; break; + case 0x15: inst->mnemo = IA64_INST_SXT2; break; + case 0x16: inst->mnemo = IA64_INST_SXT4; break; + case 0x10: inst->mnemo = IA64_INST_ZXT1; break; + case 0x11: inst->mnemo = IA64_INST_ZXT2; break; + case 0x12: inst->mnemo = IA64_INST_ZXT4; break; + default: abort(); + } + inst->operands[0].valid = true; + inst->operands[0].index = r1; + inst->operands[1].valid = true; + inst->operands[1].index = r3; + inst->operands[1].value = IA64_GET_GR(r3); + inst->operands[1].nat = IA64_GET_NAT(r3); + return true; + } + } + return false; +} + +// Decode group 4 instructions (load/store instructions) +static bool ia64_decode_instruction_4(ia64_instruction_t *inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + const int r1 = (inst->inst >> 6) & 0x7f; + const int r2 = (inst->inst >> 13) & 0x7f; + const int r3 = (inst->inst >> 20) & 0x7f; + + const int m = (inst->inst >> 36) & 1; + const int x = (inst->inst >> 27) & 1; + const int x6 = (inst->inst >> 30) & 0x3f; + + switch (x6) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + if (x == 0) { + inst->operands[0].valid = true; + inst->operands[0].index = r1; + inst->operands[1].valid = true; + inst->operands[1].index = r3; + inst->operands[1].value = IA64_GET_GR(r3); + inst->operands[1].nat = IA64_GET_NAT(r3); + if (m == 0) { + switch (x6) { + case 0x00: inst->mnemo = IA64_INST_LD1; break; + case 0x01: inst->mnemo = IA64_INST_LD2; break; + case 0x02: inst->mnemo = IA64_INST_LD4; break; + case 0x03: inst->mnemo = IA64_INST_LD8; break; + } + } + else { + inst->operands[2].valid = true; + inst->operands[2].index = r2; + inst->operands[2].value = IA64_GET_GR(r2); + inst->operands[2].nat = IA64_GET_NAT(r2); + switch (x6) { + case 0x00: inst->mnemo = IA64_INST_LD1_UPDATE; break; + case 0x01: inst->mnemo = IA64_INST_LD2_UPDATE; break; + case 0x02: inst->mnemo = IA64_INST_LD4_UPDATE; break; + case 0x03: inst->mnemo = IA64_INST_LD8_UPDATE; break; + } + } + return true; + } + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + if (m == 0 && x == 0) { + inst->operands[0].valid = true; + inst->operands[0].index = r3; + inst->operands[0].value = IA64_GET_GR(r3); + inst->operands[0].nat = IA64_GET_NAT(r3); + inst->operands[1].valid = true; + inst->operands[1].index = r2; + inst->operands[1].value = IA64_GET_GR(r2); + inst->operands[1].nat = IA64_GET_NAT(r2); + switch (x6) { + case 0x30: inst->mnemo = IA64_INST_ST1; break; + case 0x31: inst->mnemo = IA64_INST_ST2; break; + case 0x32: inst->mnemo = IA64_INST_ST4; break; + case 0x33: inst->mnemo = IA64_INST_ST8; break; + } + return true; + } + break; + } + return false; +} + +// Decode group 5 instructions (load/store instructions) +static bool ia64_decode_instruction_5(ia64_instruction_t *inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + const int r1 = (inst->inst >> 6) & 0x7f; + const int r2 = (inst->inst >> 13) & 0x7f; + const int r3 = (inst->inst >> 20) & 0x7f; + + const int x6 = (inst->inst >> 30) & 0x3f; + + switch (x6) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + inst->operands[0].valid = true; + inst->operands[0].index = r1; + inst->operands[1].valid = true; + inst->operands[1].index = r3; + inst->operands[1].value = IA64_GET_GR(r3); + inst->operands[1].nat = IA64_GET_NAT(r3); + inst->operands[2].valid = true; + inst->operands[2].index = -1; + inst->operands[2].value = ia64_inst_get_imm9b(inst->inst); + inst->operands[2].nat = 0; + switch (x6) { + case 0x00: inst->mnemo = IA64_INST_LD1_UPDATE; break; + case 0x01: inst->mnemo = IA64_INST_LD2_UPDATE; break; + case 0x02: inst->mnemo = IA64_INST_LD4_UPDATE; break; + case 0x03: inst->mnemo = IA64_INST_LD8_UPDATE; break; + } + return true; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + inst->operands[0].valid = true; + inst->operands[0].index = r3; + inst->operands[0].value = IA64_GET_GR(r3); + inst->operands[0].nat = IA64_GET_NAT(r3); + inst->operands[1].valid = true; + inst->operands[1].index = r2; + inst->operands[1].value = IA64_GET_GR(r2); + inst->operands[1].nat = IA64_GET_NAT(r2); + inst->operands[2].valid = true; + inst->operands[2].index = -1; + inst->operands[2].value = ia64_inst_get_imm9a(inst->inst); + inst->operands[2].nat = 0; + switch (x6) { + case 0x30: inst->mnemo = IA64_INST_ST1_UPDATE; break; + case 0x31: inst->mnemo = IA64_INST_ST2_UPDATE; break; + case 0x32: inst->mnemo = IA64_INST_ST4_UPDATE; break; + case 0x33: inst->mnemo = IA64_INST_ST8_UPDATE; break; + } + return true; + } + return false; +} + +// Decode group 8 instructions (ALU integer) +static bool ia64_decode_instruction_8(ia64_instruction_t *inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + const int r1 = (inst->inst >> 6) & 0x7f; + const int r2 = (inst->inst >> 13) & 0x7f; + const int r3 = (inst->inst >> 20) & 0x7f; + + const int x2a = (inst->inst >> 34) & 0x3; + const int x2b = (inst->inst >> 27) & 0x3; + const int x4 = (inst->inst >> 29) & 0xf; + const int ve = (inst->inst >> 33) & 0x1; + + // destination register (r1) is always valid in this group + inst->operands[0].valid = true; + inst->operands[0].index = r1; + + // source register (r3) is always valid in this group + inst->operands[2].valid = true; + inst->operands[2].index = r3; + inst->operands[2].value = IA64_GET_GR(r3); + inst->operands[2].nat = IA64_GET_NAT(r3); + + if (x2a == 0 && ve == 0) { + inst->operands[1].valid = true; + inst->operands[1].index = r2; + inst->operands[1].value = IA64_GET_GR(r2); + inst->operands[1].nat = IA64_GET_NAT(r2); + switch (x4) { + case 0x0: // add (A1) + inst->mnemo = IA64_INST_ADD; + inst->operands[3].valid = true; + inst->operands[3].index = -1; + inst->operands[3].value = x2b == 1; + return true; + case 0x1: // add (A1) + inst->mnemo = IA64_INST_SUB; + inst->operands[3].valid = true; + inst->operands[3].index = -1; + inst->operands[3].value = x2b == 0; + return true; + case 0x4: // shladd (A2) + inst->mnemo = IA64_INST_SHLADD; + inst->operands[3].valid = true; + inst->operands[3].index = -1; + inst->operands[3].value = ia64_inst_get_count2(inst->inst); + return true; + case 0x9: + if (x2b == 1) { + inst->mnemo = IA64_INST_SUB; + inst->operands[1].index = -1; + inst->operands[1].value = ia64_inst_get_imm8(inst->inst); + inst->operands[1].nat = 0; + return true; + } + break; + case 0xb: + inst->operands[1].index = -1; + inst->operands[1].value = ia64_inst_get_imm8(inst->inst); + inst->operands[1].nat = 0; + // fall-through + case 0x3: + switch (x2b) { + case 0: inst->mnemo = IA64_INST_AND; break; + case 1: inst->mnemo = IA64_INST_ANDCM; break; + case 2: inst->mnemo = IA64_INST_OR; break; + case 3: inst->mnemo = IA64_INST_XOR; break; + } + return true; + } + } + return false; +} + +// Decode instruction +static bool ia64_decode_instruction(ia64_instruction_t *inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + const int major = (inst->inst >> 37) & 0xf; + + inst->mnemo = IA64_INST_UNKNOWN; + inst->pred = inst->inst & 0x3f; + memset(&inst->operands[0], 0, sizeof(inst->operands)); + + switch (major) { + case 0x0: return ia64_decode_instruction_0(inst, IA64_CONTEXT); + case 0x4: return ia64_decode_instruction_4(inst, IA64_CONTEXT); + case 0x5: return ia64_decode_instruction_5(inst, IA64_CONTEXT); + case 0x8: return ia64_decode_instruction_8(inst, IA64_CONTEXT); + } + return false; +} + +static bool ia64_emulate_instruction(ia64_instruction_t *inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + // XXX: handle Register NaT Consumption fault? + // XXX: this simple emulator assumes instructions in a bundle + // don't depend on effects of other instructions in the same + // bundle. It probably would be simpler to JIT-generate code to be + // executed natively but probably more costly (inject/extract CPU state) + if (inst->mnemo == IA64_INST_UNKNOWN) + return false; + if (inst->pred && !IA64_GET_PR(inst->pred)) + return true; + + uint8_t nat, nat2; + uint64_t dst, dst2, src1, src2, src3; + + switch (inst->mnemo) { + case IA64_INST_NOP: + break; + case IA64_INST_ADD: + case IA64_INST_SUB: + case IA64_INST_SHLADD: + src3 = inst->operands[3].value; + // fall-through + case IA64_INST_AND: + case IA64_INST_ANDCM: + case IA64_INST_OR: + case IA64_INST_XOR: + src1 = inst->operands[1].value; + src2 = inst->operands[2].value; + switch (inst->mnemo) { + case IA64_INST_ADD: dst = src1 + src2 + src3; break; + case IA64_INST_SUB: dst = src1 - src2 - src3; break; + case IA64_INST_SHLADD: dst = (src1 << src3) + src2; break; + case IA64_INST_AND: dst = src1 & src2; break; + case IA64_INST_ANDCM: dst = src1 &~ src2; break; + case IA64_INST_OR: dst = src1 | src2; break; + case IA64_INST_XOR: dst = src1 ^ src2; break; + } + inst->operands[0].commit = true; + inst->operands[0].value = dst; + inst->operands[0].nat = inst->operands[1].nat | inst->operands[2].nat; + break; + case IA64_INST_SXT1: + case IA64_INST_SXT2: + case IA64_INST_SXT4: + case IA64_INST_ZXT1: + case IA64_INST_ZXT2: + case IA64_INST_ZXT4: + src1 = inst->operands[1].value; + switch (inst->mnemo) { + case IA64_INST_SXT1: dst = (int64_t)(int8_t)src1; break; + case IA64_INST_SXT2: dst = (int64_t)(int16_t)src1; break; + case IA64_INST_SXT4: dst = (int64_t)(int32_t)src1; break; + case IA64_INST_ZXT1: dst = (uint8_t)src1; break; + case IA64_INST_ZXT2: dst = (uint16_t)src1; break; + case IA64_INST_ZXT4: dst = (uint32_t)src1; break; + } + inst->operands[0].commit = true; + inst->operands[0].value = dst; + inst->operands[0].nat = inst->operands[1].nat; + break; + case IA64_INST_LD1_UPDATE: + case IA64_INST_LD2_UPDATE: + case IA64_INST_LD4_UPDATE: + case IA64_INST_LD8_UPDATE: + inst->operands[1].commit = true; + dst2 = inst->operands[1].value + inst->operands[2].value; + nat2 = inst->operands[2].nat ? inst->operands[2].nat : 0; + // fall-through + case IA64_INST_LD1: + case IA64_INST_LD2: + case IA64_INST_LD4: + case IA64_INST_LD8: + src1 = inst->operands[1].value; + if (inst->no_memory) + dst = 0; + else { + switch (inst->mnemo) { + case IA64_INST_LD1: case IA64_INST_LD1_UPDATE: dst = *((uint8_t *)src1); break; + case IA64_INST_LD2: case IA64_INST_LD2_UPDATE: dst = *((uint16_t *)src1); break; + case IA64_INST_LD4: case IA64_INST_LD4_UPDATE: dst = *((uint32_t *)src1); break; + case IA64_INST_LD8: case IA64_INST_LD8_UPDATE: dst = *((uint64_t *)src1); break; + } + } + inst->operands[0].commit = true; + inst->operands[0].value = dst; + inst->operands[0].nat = 0; + inst->operands[1].value = dst2; + inst->operands[1].nat = nat2; + break; + case IA64_INST_ST1_UPDATE: + case IA64_INST_ST2_UPDATE: + case IA64_INST_ST4_UPDATE: + case IA64_INST_ST8_UPDATE: + inst->operands[0].commit = 0; + dst2 = inst->operands[0].value + inst->operands[2].value; + nat2 = inst->operands[2].nat ? inst->operands[2].nat : 0; + // fall-through + case IA64_INST_ST1: + case IA64_INST_ST2: + case IA64_INST_ST4: + case IA64_INST_ST8: + dst = inst->operands[0].value; + src1 = inst->operands[1].value; + if (!inst->no_memory) { + switch (inst->mnemo) { + case IA64_INST_ST1: case IA64_INST_ST1_UPDATE: *((uint8_t *)dst) = src1; break; + case IA64_INST_ST2: case IA64_INST_ST2_UPDATE: *((uint16_t *)dst) = src1; break; + case IA64_INST_ST4: case IA64_INST_ST4_UPDATE: *((uint32_t *)dst) = src1; break; + case IA64_INST_ST8: case IA64_INST_ST8_UPDATE: *((uint64_t *)dst) = src1; break; + } + } + inst->operands[0].value = dst2; + inst->operands[0].nat = nat2; + break; + default: + return false; + } + + for (int i = 0; i < IA64_N_OPERANDS; i++) { + ia64_operand_t const & op = inst->operands[i]; + if (!op.commit) + continue; + if (op.index == -1) + return false; // XXX: internal error + IA64_SET_GR(op.index, op.value, op.nat); + } + return true; +} + +static bool ia64_emulate_instruction(uint64_t raw_inst, IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + ia64_instruction_t inst; + memset(&inst, 0, sizeof(inst)); + inst.inst = raw_inst; + if (!ia64_decode_instruction(&inst, IA64_CONTEXT)) + return false; + return ia64_emulate_instruction(&inst, IA64_CONTEXT); +} + +static bool ia64_skip_instruction(IA64_CONTEXT_TYPE IA64_CONTEXT) +{ + uint64_t ip = IA64_GET_IP(); +#if DEBUG + printf("IP: 0x%016llx\n", ip); +#if 0 + printf(" Template 0x%02x\n", ia64_get_template(ip)); + ia64_get_instruction(ip, 0); + ia64_get_instruction(ip, 1); + ia64_get_instruction(ip, 2); +#endif +#endif + + // Select which decode switch to use + ia64_instruction_t inst; + inst.inst = ia64_get_instruction(ip, ip & 3); + if (!ia64_decode_instruction(&inst, IA64_CONTEXT)) { + fprintf(stderr, "ERROR: ia64_skip_instruction(): could not decode instruction\n"); + return false; + } + + transfer_type_t transfer_type = SIGSEGV_TRANSFER_UNKNOWN; + transfer_size_t transfer_size = SIZE_UNKNOWN; + + switch (inst.mnemo) { + case IA64_INST_LD1: + case IA64_INST_LD2: + case IA64_INST_LD4: + case IA64_INST_LD8: + case IA64_INST_LD1_UPDATE: + case IA64_INST_LD2_UPDATE: + case IA64_INST_LD4_UPDATE: + case IA64_INST_LD8_UPDATE: + transfer_type = SIGSEGV_TRANSFER_LOAD; + break; + case IA64_INST_ST1: + case IA64_INST_ST2: + case IA64_INST_ST4: + case IA64_INST_ST8: + case IA64_INST_ST1_UPDATE: + case IA64_INST_ST2_UPDATE: + case IA64_INST_ST4_UPDATE: + case IA64_INST_ST8_UPDATE: + transfer_type = SIGSEGV_TRANSFER_STORE; + break; + } + + if (transfer_type == SIGSEGV_TRANSFER_UNKNOWN) { + // Unknown machine code, let it crash. Then patch the decoder + fprintf(stderr, "ERROR: ia64_skip_instruction(): not a load/store instruction\n"); + return false; + } + + switch (inst.mnemo) { + case IA64_INST_LD1: + case IA64_INST_LD1_UPDATE: + case IA64_INST_ST1: + case IA64_INST_ST1_UPDATE: + transfer_size = SIZE_BYTE; + break; + case IA64_INST_LD2: + case IA64_INST_LD2_UPDATE: + case IA64_INST_ST2: + case IA64_INST_ST2_UPDATE: + transfer_size = SIZE_WORD; + break; + case IA64_INST_LD4: + case IA64_INST_LD4_UPDATE: + case IA64_INST_ST4: + case IA64_INST_ST4_UPDATE: + transfer_size = SIZE_LONG; + break; + case IA64_INST_LD8: + case IA64_INST_LD8_UPDATE: + case IA64_INST_ST8: + case IA64_INST_ST8_UPDATE: + transfer_size = SIZE_QUAD; + break; + } + + if (transfer_size == SIZE_UNKNOWN) { + // Unknown machine code, let it crash. Then patch the decoder + fprintf(stderr, "ERROR: ia64_skip_instruction(): unknown transfer size\n"); + return false; + } + + inst.no_memory = true; + if (!ia64_emulate_instruction(&inst, IA64_CONTEXT)) { + fprintf(stderr, "ERROR: ia64_skip_instruction(): could not emulate fault instruction\n"); + return false; + } + + int slot = ip & 3; + bool emulate_next = false; + switch (slot) { + case 0: + switch (ia64_get_template(ip)) { + case 0x2: // MI;I + case 0x3: // MI;I; + emulate_next = true; + slot = 2; + break; + case 0xa: // M;MI + case 0xb: // M;MI; + emulate_next = true; + slot = 1; + break; + } + break; + } + if (emulate_next && !IA64_CAN_PATCH_IP_SLOT) { + while (slot < 3) { + if (!ia64_emulate_instruction(ia64_get_instruction(ip, slot), IA64_CONTEXT)) { + fprintf(stderr, "ERROR: ia64_skip_instruction(): could not emulate instruction\n"); + return false; + } + ++slot; + } + } + +#if IA64_CAN_PATCH_IP_SLOT + if ((slot = ip & 3) < 2) + IA64_SET_IP((ip & ~3ull) + (slot + 1)); + else +#endif + IA64_SET_IP((ip & ~3ull) + 16); +#if DEBUG + printf("IP: 0x%016llx\n", IA64_GET_IP()); +#endif + return true; +} +#endif + // Decode and skip PPC instruction #if (defined(powerpc) || defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)) static bool powerpc_skip_instruction(unsigned long * nip_p, unsigned long * regs) @@ -1662,100 +2503,99 @@ 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) +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, + 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); + (natural_t *)&SIP->exc_state, + &SIP->exc_state_count); MACH_CHECK_ERROR(thread_get_state, krc); - sip->has_exc_state = true; + SIP->has_exc_state = true; } -static void mach_get_thread_state(sigsegv_info_t *sip) +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, + 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); + (natural_t *)&SIP->thr_state, + &SIP->thr_state_count); MACH_CHECK_ERROR(thread_get_state, krc); - sip->has_thr_state = true; + SIP->has_thr_state = true; } -static void mach_set_thread_state(sigsegv_info_t *sip) +static void mach_set_thread_state(sigsegv_info_t *SIP) { - kern_return_t krc = thread_set_state(sip->thread, + kern_return_t krc = thread_set_state(SIP->thread, SIGSEGV_THREAD_STATE_FLAVOR, - (natural_t *)&sip->thr_state, - sip->thr_state_count); + (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) +sigsegv_address_t sigsegv_get_fault_address(sigsegv_info_t *SIP) { #ifdef HAVE_MACH_EXCEPTIONS +#ifdef EMULATED_PPC static int use_fast_path = -1; - if (use_fast_path != 1 && !sip->has_exc_state) { - mach_get_exception_state(sip); + 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; + if (use_fast_path < 0) { + const char *machfault = getenv("SIGSEGV_MACH_FAULT"); + if (machfault) { + if (strcmp(machfault, "fast") == 0) + use_fast_path = 1; + else if (strcmp(machfault, "slow") == 0) + use_fast_path = 0; + } + if (use_fast_path < 0) + use_fast_path = addr == SIP->addr; + } + SIP->addr = addr; } #endif - return sip->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) +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); +#ifdef EMULATED_PPC + if (!SIP->has_thr_state) { + mach_get_thread_state(SIP); - sip->pc = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION; + SIP->pc = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION; } #endif - return sip->pc; +#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; + 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 - si.thread = thread; - si.has_exc_state = false; - si.has_thr_state = false; + SI.thread = thread; + SI.has_exc_state = false; + SI.has_thr_state = false; #endif - sigsegv_info_t * const sip = &si; + sigsegv_info_t * const SIP = &SI; // Call user's handler and reinstall the global handler, if required - switch (SIGSEGV_FAULT_HANDLER_INVOKE(sip)) { + switch (SIGSEGV_FAULT_HANDLER_INVOKE(SIP)) { case SIGSEGV_RETURN_SUCCESS: return true; @@ -1764,8 +2604,8 @@ static bool handle_badaccess(SIGSEGV_FAU // Call the instruction skipper with the register file // available #ifdef HAVE_MACH_EXCEPTIONS - if (!sip->has_thr_state) - mach_get_thread_state(sip); + if (!SIP->has_thr_state) + mach_get_thread_state(SIP); #endif if (SIGSEGV_SKIP_INSTRUCTION(SIGSEGV_REGISTER_FILE)) { #ifdef HAVE_MACH_EXCEPTIONS @@ -1773,7 +2613,7 @@ static bool handle_badaccess(SIGSEGV_FAU // is modified off of the stack, in Mach we // need to actually call thread_set_state to // have the register values updated. - mach_set_thread_state(sip); + mach_set_thread_state(SIP); #endif return true; } @@ -1782,7 +2622,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(sip); + sigsegv_state_dumper(SIP); break; } @@ -1815,7 +2655,7 @@ static inline kern_return_t forward_exception(mach_port_t thread_port, mach_port_t task_port, exception_type_t exception_type, - exception_data_t exception_data, + mach_exception_data_t exception_data, mach_msg_type_number_t data_count, ExceptionPorts *oldExceptionPorts) { @@ -1862,26 +2702,26 @@ forward_exception(mach_port_t thread_por switch (behavior) { case EXCEPTION_DEFAULT: // fprintf(stderr, "forwarding to exception_raise\n"); - kret = exception_raise(port, thread_port, task_port, exception_type, - exception_data, data_count); - MACH_CHECK_ERROR (exception_raise, kret); + kret = mach_exception_raise(port, thread_port, task_port, exception_type, + exception_data, data_count); + MACH_CHECK_ERROR (mach_exception_raise, kret); break; case EXCEPTION_STATE: // fprintf(stderr, "forwarding to exception_raise_state\n"); - kret = exception_raise_state(port, exception_type, exception_data, - data_count, &flavor, - (natural_t *)&thread_state, thread_state_count, - (natural_t *)&thread_state, &thread_state_count); - MACH_CHECK_ERROR (exception_raise_state, kret); + kret = mach_exception_raise_state(port, exception_type, exception_data, + data_count, &flavor, + (natural_t *)&thread_state, thread_state_count, + (natural_t *)&thread_state, &thread_state_count); + MACH_CHECK_ERROR (mach_exception_raise_state, kret); break; case EXCEPTION_STATE_IDENTITY: // fprintf(stderr, "forwarding to exception_raise_state_identity\n"); - kret = exception_raise_state_identity(port, thread_port, task_port, - exception_type, exception_data, - data_count, &flavor, - (natural_t *)&thread_state, thread_state_count, - (natural_t *)&thread_state, &thread_state_count); - MACH_CHECK_ERROR (exception_raise_state_identity, kret); + kret = mach_exception_raise_state_identity(port, thread_port, task_port, + exception_type, exception_data, + data_count, &flavor, + (natural_t *)&thread_state, thread_state_count, + (natural_t *)&thread_state, &thread_state_count); + MACH_CHECK_ERROR (mach_exception_raise_state_identity, kret); break; default: fprintf(stderr, "forward_exception got unknown behavior\n"); @@ -1918,12 +2758,12 @@ forward_exception(mach_port_t thread_por * linkage because that is what exc_server expects. */ kern_return_t -catch_exception_raise(mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count) +catch_mach_exception_raise(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t code_count) { kern_return_t krc; @@ -1944,6 +2784,50 @@ catch_exception_raise(mach_port_t except return krc; } + +/* XXX: borrowed from launchd and gdb */ +kern_return_t +catch_mach_exception_raise_state(mach_port_t exception_port, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t code_count, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t *new_state_count) +{ + memcpy(new_state, old_state, old_state_count * sizeof(old_state[0])); + *new_state_count = old_state_count; + return KERN_SUCCESS; +} + +/* XXX: borrowed from launchd and gdb */ +kern_return_t +catch_mach_exception_raise_state_identity(mach_port_t exception_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t code_count, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t *new_state_count) +{ + kern_return_t kret; + + memcpy(new_state, old_state, old_state_count * sizeof(old_state[0])); + *new_state_count = old_state_count; + + kret = mach_port_deallocate(mach_task_self(), task_port); + MACH_CHECK_ERROR(mach_port_deallocate, kret); + kret = mach_port_deallocate(mach_task_self(), thread_port); + MACH_CHECK_ERROR(mach_port_deallocate, kret); + + return KERN_SUCCESS; +} #endif #ifdef HAVE_SIGSEGV_RECOVERY @@ -2068,7 +2952,7 @@ static bool sigsegv_do_install_handler(s // addressing modes) used in PPC instructions, you will need the // GPR state anyway. krc = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, _exceptionPort, - EXCEPTION_DEFAULT, SIGSEGV_THREAD_STATE_FLAVOR); + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, SIGSEGV_THREAD_STATE_FLAVOR); if (krc != KERN_SUCCESS) { mach_error("thread_set_exception_ports", krc); return false; @@ -2096,7 +2980,7 @@ static LONG WINAPI main_exception_filter { if (sigsegv_fault_handler != NULL && ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION - && ExceptionInfo->ExceptionRecord->NumberParameters == 2 + && ExceptionInfo->ExceptionRecord->NumberParameters >= 2 && handle_badaccess(ExceptionInfo)) return EXCEPTION_CONTINUE_EXECUTION; @@ -2248,7 +3132,7 @@ void sigsegv_set_dump_state(sigsegv_stat const int REF_INDEX = 123; const int REF_VALUE = 45; -static int page_size; +static sigsegv_uintptr_t page_size; static volatile char * page = 0; static volatile int handler_called = 0; @@ -2286,7 +3170,7 @@ static sigsegv_return_t sigsegv_test_han (instruction_address >= (sigsegv_address_t)e_region))) exit(11); #endif - if (vm_protect((char *)((unsigned long)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0) + if (vm_protect((char *)((sigsegv_uintptr_t)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0) exit(12); return SIGSEGV_RETURN_SUCCESS; } @@ -2299,7 +3183,7 @@ static sigsegv_return_t sigsegv_insn_han #if DEBUG printf("sigsegv_insn_handler(%p, %p)\n", fault_address, instruction_address); #endif - if (((unsigned long)fault_address - (unsigned long)page) < page_size) { + if (((sigsegv_uintptr_t)fault_address - (sigsegv_uintptr_t)page) < page_size) { #ifdef __GNUC__ // Make sure reported fault instruction address falls into // expected code range @@ -2317,7 +3201,7 @@ static sigsegv_return_t sigsegv_insn_han // More sophisticated tests for instruction skipper static bool arch_insn_skipper_tests() { -#if (defined(i386) || defined(__i386__)) || defined(__x86_64__) +#if (defined(i386) || defined(__i386__)) || (defined(__x86_64__) || defined(_M_X64)) static const unsigned char code[] = { 0x8a, 0x00, // mov (%eax),%al 0x8a, 0x2c, 0x18, // mov (%eax,%ebx,1),%ch @@ -2331,7 +3215,7 @@ static bool arch_insn_skipper_tests() 0x8b, 0x0c, 0x18, // mov (%eax,%ebx,1),%ecx 0x89, 0x00, // mov %eax,(%eax) 0x89, 0x0c, 0x18, // mov %ecx,(%eax,%ebx,1) -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(_M_X64) 0x44, 0x8a, 0x00, // mov (%rax),%r8b 0x44, 0x8a, 0x20, // mov (%rax),%r12b 0x42, 0x8a, 0x3c, 0x10, // mov (%rax,%r10,1),%dil @@ -2360,10 +3244,10 @@ static bool arch_insn_skipper_tests() 0 // end }; const int N_REGS = 20; - unsigned long regs[N_REGS]; + SIGSEGV_REGISTER_TYPE regs[N_REGS]; for (int i = 0; i < N_REGS; i++) regs[i] = i; - const unsigned long start_code = (unsigned long)&code; + const sigsegv_uintptr_t start_code = (sigsegv_uintptr_t)&code; regs[X86_REG_EIP] = start_code; while ((regs[X86_REG_EIP] - start_code) < (sizeof(code) - 1) && ix86_skip_instruction(regs)) @@ -2389,18 +3273,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; - BARRIER(); - 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; @@ -2431,18 +3327,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); - BARRIER(); - 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