diff --git a/src/core/signals.cpp b/src/core/signals.cpp index df3b28b975..e148f2c4c2 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -12,9 +12,81 @@ #include #ifdef ARCH_X86_64 #include +#include #endif #endif +namespace { + +#ifdef _WIN32 +#define GET_XMM_POINTER(ctx, index, low, high) \ + (void*)(&(ctx)->Xmm##index##.Low) +#else +#define GET_XMM_POINTER(ctx, index, low, high) \ + (void*)(&(ctx)->uc_mcontext->fpregs._xmm[index].element[0]) +#endif + +#if defined(ARCH_X86_64) +// We need to check, before patching, if there's enough space for a relative jump to the trampoline. +// If there isn't, the instruction must be handled specially in the illegal instruction handler itself. +bool shouldNotBePatched(void* code_address) { + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; + const auto status = Common::Decoder::Instance().decodeInstruction(instruction, operands, code_address); + if (!ZYAN_SUCCESS(status)) { + LOG_ERROR(Core, "Failed to decode instruction at: {}", fmt::ptr(code_address)); + } + + if (instruction.length < 5) { + // not enough bytes for a relative jump for the trampoline + return true; + } + + return false; +} + +bool handleIllegalInstruction(void* code_address) { + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; + const auto status = Common::Decoder::Instance().decodeInstruction(instruction, operands, code_address); + + switch (instruction.mnemonic) { + case ZYDIS_MNEMONIC_EXTRQ: { + bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && + operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + if (immediateForm) { + LOG_ERROR(Core, "EXTRQ immediate form should have been patched at code address: {}", fmt::ptr(code_address)); + } else { + ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER, + "Unexpected operand types for EXTRQ instruction"); + const auto dst = operands[0].reg.value; + const auto src = operands[1].reg.value; + } + break; + } + default: { + LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}", fmt::ptr(code_address), + ZydisMnemonicGetString(instruction.mnemonic)); + return false; + } + } +} +#elif defined(ARCH_ARM64) +// These functions shouldn't be needed for ARM as it will use a JIT so there's no need to patch instructions. +// Returning false lets it go through with whatever handler is set up. +bool shouldNotBePatched(void* code_address) { + return false; +} + +bool handleIllegalInstruction(void* code_address) { + return false; +} +#else +#error "Unsupported architecture" +#endif +} // namespace + namespace Core { #if defined(_WIN32) @@ -32,7 +104,11 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { pExp->ExceptionRecord->ExceptionInformation[0] == 1); break; case EXCEPTION_ILLEGAL_INSTRUCTION: - handled = signals->DispatchIllegalInstruction(code_address); + if (shouldNotBePatched(code_address)) { + handled = handleIllegalInstruction(code_address); + } else { + handled = signals->DispatchIllegalInstruction(code_address); + } break; default: break; @@ -99,9 +175,16 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { } break; case SIGILL: - if (!signals->DispatchIllegalInstruction(code_address)) { - UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", - fmt::ptr(code_address), DisassembleInstruction(code_address)); + if (shouldNotBePatched(code_address)) { + if (!handleIllegalInstruction(code_address)) { + UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", + fmt::ptr(code_address), DisassembleInstruction(code_address)); + } + } else { + if (!signals->DispatchIllegalInstruction(code_address)) { + UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", + fmt::ptr(code_address), DisassembleInstruction(code_address)); + } } break; default: