From 755bd2c445488709a9c2b7d283ca5e6e56fdc56e Mon Sep 17 00:00:00 2001 From: comex Date: Thu, 11 Sep 2014 00:24:22 -0400 Subject: [PATCH] Reorganize backpatching a bit. Untested on ARM. Rather than *MemTools.cpp checking whether the address is in the emulated range itself (which, as of the next commit, doesn't cover every kind of access the JIT might want to intercept) and doing PC replacement, they just pass the access address and context to jit->HandleFault, which does the rest itself. Because SContext is now in JitInterface, I wanted JitBackpatch.h (which defines it) to be lightweight, so I moved TrampolineCache and associated x64{Analyzer,Emitter} dependencies into its own file. I hate adding new files in three places, two of which are MSVC... While I'm at it, edit a misleading comment. --- Source/Core/Core/ArmMemTools.cpp | 33 +--- Source/Core/Core/CMakeLists.txt | 5 +- Source/Core/Core/Core.vcxproj | 4 +- Source/Core/Core/Core.vcxproj.filters | 8 +- Source/Core/Core/PowerPC/Jit64IL/JitIL.h | 5 + Source/Core/Core/PowerPC/JitArm32/Jit.h | 6 +- .../PowerPC/JitArm32/JitArm_BackPatch.cpp | 19 +- .../Core/PowerPC/JitCommon/JitBackpatch.cpp | 175 +++--------------- .../Core/PowerPC/JitCommon/JitBackpatch.h | 17 +- Source/Core/Core/PowerPC/JitCommon/JitBase.h | 11 +- .../PowerPC/JitCommon/TrampolineCache.cpp | 156 ++++++++++++++++ .../Core/PowerPC/JitCommon/TrampolineCache.h | 22 +++ Source/Core/Core/PowerPC/JitInterface.cpp | 8 +- Source/Core/Core/PowerPC/JitInterface.h | 4 +- Source/Core/Core/x64MemTools.cpp | 46 +---- 15 files changed, 258 insertions(+), 261 deletions(-) create mode 100644 Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp create mode 100644 Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h diff --git a/Source/Core/Core/ArmMemTools.cpp b/Source/Core/Core/ArmMemTools.cpp index c9816aba00..8b166580a8 100644 --- a/Source/Core/Core/ArmMemTools.cpp +++ b/Source/Core/Core/ArmMemTools.cpp @@ -32,9 +32,9 @@ typedef struct ucontext { } ucontext_t; #endif -void sigsegv_handler(int signal, siginfo_t *info, void *raw_context) +static void sigsegv_handler(int sig, siginfo_t *info, void *raw_context) { - if (signal != SIGSEGV) + if (sig != SIGSEGV) { // We are not interested in other signals - handle it as usual. return; @@ -47,33 +47,18 @@ void sigsegv_handler(int signal, siginfo_t *info, void *raw_context) return; } - // Get all the information we can out of the context. mcontext_t *ctx = &context->uc_mcontext; - void *fault_memory_ptr = (void*)ctx->arm_r10; - u8 *fault_instruction_ptr = (u8 *)ctx->arm_pc; + // comex says hello, and is most curious whether this is arm_r10 for a + // reason as opposed to si_addr like the x64MemTools.cpp version. Is there + // even a need for this file to be architecture specific? + uintptr_t fault_memory_ptr = (uintptr_t)ctx->arm_r10; - if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) + if (!JitInterface::HandleFault(fault_memory_ptr, ctx)) { - // Let's not prevent debugging. - return; - } - - u64 bad_address = (u64)fault_memory_ptr; - u64 memspace_bottom = (u64)Memory::base; - if (bad_address < memspace_bottom) - { - PanicAlertT("Exception handler - access below memory space. %08llx%08llx", - bad_address >> 32, bad_address); - } - - u32 em_address = (u32)(bad_address - memspace_bottom); - - const u8 *new_rip = jit->BackPatch(fault_instruction_ptr, em_address, ctx); - if (new_rip) - { - ctx->arm_pc = (u32) new_rip; + // retry and crash + signal(SIGSEGV, SIG_DFL); } } diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 78172d5cb0..39800a448e 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -195,9 +195,10 @@ if(_M_X86) PowerPC/Jit64/Jit_Paired.cpp PowerPC/Jit64/JitRegCache.cpp PowerPC/Jit64/Jit_SystemRegisters.cpp - PowerPC/JitCommon/JitBackpatch.cpp PowerPC/JitCommon/JitAsmCommon.cpp - PowerPC/JitCommon/Jit_Util.cpp) + PowerPC/JitCommon/JitBackpatch.cpp + PowerPC/JitCommon/Jit_Util.cpp + PowerPC/JitCommon/TrampolineCache.cpp) elseif(_M_ARM_32) set(SRCS ${SRCS} ArmMemTools.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 7ead1e4172..b46357fd3d 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -229,6 +229,7 @@ + @@ -406,6 +407,7 @@ + @@ -464,4 +466,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 39e6aec8f4..faeb9bcd24 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -640,6 +640,9 @@ PowerPC\JitCommon + + PowerPC\JitCommon + PowerPC\JitIL @@ -1182,6 +1185,9 @@ PowerPC\JitCommon + + PowerPC\JitCommon + PowerPC\JitIL @@ -1204,4 +1210,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/PowerPC/Jit64IL/JitIL.h b/Source/Core/Core/PowerPC/Jit64IL/JitIL.h index d0185719f3..5592500c2a 100644 --- a/Source/Core/Core/PowerPC/Jit64IL/JitIL.h +++ b/Source/Core/Core/PowerPC/Jit64IL/JitIL.h @@ -56,6 +56,10 @@ public: void Trace(); + JitBlockCache *GetBlockCache() override { return &blocks; } + + bool HandleFault(uintptr_t access_address, SContext* ctx) override { return false; } + void ClearCache() override; const u8 *GetDispatcher() { @@ -105,4 +109,5 @@ public: void DynaRunTable31(UGeckoInstruction _inst) override; void DynaRunTable59(UGeckoInstruction _inst) override; void DynaRunTable63(UGeckoInstruction _inst) override; + }; diff --git a/Source/Core/Core/PowerPC/JitArm32/Jit.h b/Source/Core/Core/PowerPC/JitArm32/Jit.h index 3cd4cf4478..3fa62d80ab 100644 --- a/Source/Core/Core/PowerPC/JitArm32/Jit.h +++ b/Source/Core/Core/PowerPC/JitArm32/Jit.h @@ -58,6 +58,8 @@ private: void SetFPException(ArmGen::ARMReg Reg, u32 Exception); ArmGen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set); + + bool BackPatch(SContext* ctx); public: JitArm() : code_buffer(32000) {} ~JitArm() {} @@ -72,9 +74,7 @@ public: JitBaseBlockCache *GetBlockCache() { return &blocks; } - const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx); - - bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); } + bool HandleFault(uintptr_t access_address, SContext* ctx) override; void Trace(); diff --git a/Source/Core/Core/PowerPC/JitArm32/JitArm_BackPatch.cpp b/Source/Core/Core/PowerPC/JitArm32/JitArm_BackPatch.cpp index 6ba24195f5..ee0cf1ee76 100644 --- a/Source/Core/Core/PowerPC/JitArm32/JitArm_BackPatch.cpp +++ b/Source/Core/Core/PowerPC/JitArm32/JitArm_BackPatch.cpp @@ -66,12 +66,23 @@ bool DisamLoadStore(const u32 inst, ARMReg &rD, u8 &accessSize, bool &Store) } return true; } -const u8 *JitArm::BackPatch(u8 *codePtr, u32, void *ctx_void) + +bool JitArm::HandleFault(uintptr_t access_address, SContext* ctx) +{ + if (access_address < (uintptr_t)Memory::base) + { + PanicAlertT("Exception handler - access below memory space. %08llx%08llx", + access_address >> 32, access_address); + } + return BackPatch(ctx); +} + +bool JitArm::BackPatch(SContext* ctx) { // TODO: This ctx needs to be filled with our information - SContext *ctx = (SContext *)ctx_void; // We need to get the destination register before we start + u8* codePtr = (u8*)ctx->CTX_PC; u32 Value = *(u32*)codePtr; ARMReg rD; u8 accessSize; @@ -109,7 +120,7 @@ const u8 *JitArm::BackPatch(u8 *codePtr, u32, void *ctx_void) u32 newPC = ctx->CTX_PC - (ARMREGOFFSET + 4 * 4); ctx->CTX_PC = newPC; emitter.FlushIcache(); - return (u8*)ctx->CTX_PC; + return true; } else { @@ -135,7 +146,7 @@ const u8 *JitArm::BackPatch(u8 *codePtr, u32, void *ctx_void) emitter.MOV(rD, R14); // 8 ctx->CTX_PC -= ARMREGOFFSET + (4 * 4); emitter.FlushIcache(); - return (u8*)ctx->CTX_PC; + return true; } return 0; } diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp index 6e89ae32eb..ea921817b8 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp @@ -3,24 +3,14 @@ // Refer to the license.txt file included. #include -#include #include "disasm.h" -#include "Common/CommonTypes.h" -#include "Common/StringUtil.h" #include "Core/PowerPC/JitCommon/JitBackpatch.h" #include "Core/PowerPC/JitCommon/JitBase.h" -#ifdef _WIN32 - #include -#endif - - using namespace Gen; -extern u8 *trampolineCodePtr; - static void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) { u64 code_addr = (u64)codePtr; @@ -35,176 +25,51 @@ static void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) return; } -void TrampolineCache::Init() +// This generates some fairly heavy trampolines, but it doesn't really hurt. +// Only instructions that access I/O will get these, and there won't be that +// many of them in a typical program/game. +bool Jitx86Base::HandleFault(uintptr_t access_address, SContext* ctx) { - AllocCodeSpace(4 * 1024 * 1024); + // TODO: do we properly handle off-the-end? + if (access_address >= (uintptr_t)Memory::base && access_address < (uintptr_t)Memory::base + 0x100010000) + return BackPatch((u32)(access_address - (uintptr_t)Memory::base), ctx); + + return false; } -void TrampolineCache::Shutdown() +bool Jitx86Base::BackPatch(u32 emAddress, SContext* ctx) { - FreeCodeSpace(); -} + u8* codePtr = (u8*) ctx->CTX_PC; -// Extremely simplistic - just generate the requested trampoline. May reuse them in the future. -const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 registersInUse) -{ - if (GetSpaceLeft() < 1024) - PanicAlert("Trampoline cache full"); - - const u8 *trampoline = GetCodePtr(); - X64Reg addrReg = (X64Reg)info.scaledReg; - X64Reg dataReg = (X64Reg)info.regOperandReg; - - // It's a read. Easy. - // RSP alignment here is 8 due to the call. - ABI_PushRegistersAndAdjustStack(registersInUse, 8); - - if (addrReg != ABI_PARAM1) - MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg)); - - if (info.displacement) - ADD(32, R(ABI_PARAM1), Imm32(info.displacement)); - - switch (info.operandSize) - { - case 4: - CALL((void *)&Memory::Read_U32); - break; - case 2: - CALL((void *)&Memory::Read_U16); - SHL(32, R(ABI_RETURN), Imm8(16)); - break; - case 1: - CALL((void *)&Memory::Read_U8); - break; - } - - if (info.signExtend && info.operandSize == 1) - { - // Need to sign extend value from Read_U8. - MOVSX(32, 8, dataReg, R(ABI_RETURN)); - } - else if (dataReg != EAX) - { - MOV(32, R(dataReg), R(ABI_RETURN)); - } - - ABI_PopRegistersAndAdjustStack(registersInUse, 8); - RET(); - return trampoline; -} - -// Extremely simplistic - just generate the requested trampoline. May reuse them in the future. -const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc) -{ - if (GetSpaceLeft() < 1024) - PanicAlert("Trampoline cache full"); - - const u8 *trampoline = GetCodePtr(); - - X64Reg dataReg = (X64Reg)info.regOperandReg; - X64Reg addrReg = (X64Reg)info.scaledReg; - - // It's a write. Yay. Remember that we don't have to be super efficient since it's "just" a - // hardware access - we can take shortcuts. - // Don't treat FIFO writes specially for now because they require a burst - // check anyway. - - // PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs - MOV(32, PPCSTATE(pc), Imm32(pc)); - - ABI_PushRegistersAndAdjustStack(registersInUse, 8); - - if (info.hasImmediate) - { - if (addrReg != ABI_PARAM2) - MOV(64, R(ABI_PARAM2), R(addrReg)); - // we have to swap back the immediate to pass it to the write functions - switch (info.operandSize) - { - case 8: - PanicAlert("Invalid 64-bit immediate!"); - break; - case 4: - MOV(32, R(ABI_PARAM1), Imm32(Common::swap32((u32)info.immediate))); - break; - case 2: - MOV(16, R(ABI_PARAM1), Imm16(Common::swap16((u16)info.immediate))); - break; - case 1: - MOV(8, R(ABI_PARAM1), Imm8((u8)info.immediate)); - break; - } - } - else - { - MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg); - } - if (info.displacement) - { - ADD(32, R(ABI_PARAM2), Imm32(info.displacement)); - } - - switch (info.operandSize) - { - case 8: - CALL((void *)&Memory::Write_U64); - break; - case 4: - CALL((void *)&Memory::Write_U32); - break; - case 2: - CALL((void *)&Memory::Write_U16); - break; - case 1: - CALL((void *)&Memory::Write_U8); - break; - } - - ABI_PopRegistersAndAdjustStack(registersInUse, 8); - RET(); - - return trampoline; -} - - -// This generates some fairly heavy trampolines, but: -// 1) It's really necessary. We don't know anything about the context. -// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be -// that many of them in a typical program/game. -const u8 *Jitx86Base::BackPatch(u8 *codePtr, u32 emAddress, void *ctx_void) -{ - SContext *ctx = (SContext *)ctx_void; - - if (!jit->IsInCodeSpace(codePtr)) - return nullptr; // this will become a regular crash real soon after this + if (!IsInSpace(codePtr)) + return false; // this will become a regular crash real soon after this InstructionInfo info = {}; if (!DisassembleMov(codePtr, &info)) { BackPatchError("BackPatch - failed to disassemble MOV instruction", codePtr, emAddress); - return nullptr; + return false; } if (info.otherReg != RMEM) { PanicAlert("BackPatch : Base reg not RMEM." "\n\nAttempted to access %08x.", emAddress); - return nullptr; + return false; } if (info.byteSwap && info.instructionSize < BACKPATCH_SIZE) { PanicAlert("BackPatch: MOVBE is too small"); - return nullptr; + return false; } auto it = registersInUseAtLoc.find(codePtr); if (it == registersInUseAtLoc.end()) { PanicAlert("BackPatch: no register use entry for address %p", codePtr); - return nullptr; + return false; } u32 registersInUse = it->second; @@ -228,7 +93,7 @@ const u8 *Jitx86Base::BackPatch(u8 *codePtr, u32 emAddress, void *ctx_void) { emitter.NOP(padding); } - return codePtr; + ctx->CTX_PC = (u64)codePtr; } else { @@ -281,6 +146,8 @@ const u8 *Jitx86Base::BackPatch(u8 *codePtr, u32 emAddress, void *ctx_void) { emitter.NOP(padding); } - return start; + ctx->CTX_PC = (u64)start; } + + return true; } diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h index 3ca7656b21..39e3389501 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.h @@ -5,11 +5,6 @@ #pragma once #include "Common/CommonTypes.h" -#include "Common/x64Analyzer.h" -#include "Common/x64Emitter.h" - -// We need at least this many bytes for backpatching. -const int BACKPATCH_SIZE = 5; // meh. #if defined(_WIN32) @@ -147,8 +142,8 @@ const int BACKPATCH_SIZE = 5; #endif #if _M_X86_64 -#define CTX_PC CTX_RIP #include +#define CTX_PC CTX_RIP static inline u64 *ContextRN(SContext* ctx, int n) { static const u8 offsets[] = @@ -173,13 +168,3 @@ static inline u64 *ContextRN(SContext* ctx, int n) return (u64 *) ((char *) ctx + offsets[n]); } #endif - -class TrampolineCache : public Gen::X64CodeBlock -{ -public: - void Init(); - void Shutdown(); - - const u8 *GetReadTrampoline(const InstructionInfo &info, u32 registersInUse); - const u8 *GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc); -}; diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.h b/Source/Core/Core/PowerPC/JitCommon/JitBase.h index c6ff6e4967..52463ec619 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.h @@ -26,6 +26,7 @@ #include "Core/PowerPC/JitCommon/JitAsmCommon.h" #include "Core/PowerPC/JitCommon/JitBackpatch.h" #include "Core/PowerPC/JitCommon/JitCache.h" +#include "Core/PowerPC/JitCommon/TrampolineCache.h" // TODO: find a better place for x86-specific stuff // The following register assignments are common to Jit64 and Jit64IL: @@ -110,24 +111,20 @@ public: virtual void Jit(u32 em_address) = 0; - virtual const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) = 0; - virtual const CommonAsmRoutinesBase *GetAsmRoutines() = 0; - virtual bool IsInCodeSpace(u8 *ptr) = 0; + virtual bool HandleFault(uintptr_t access_address, SContext* ctx) = 0; }; class Jitx86Base : public JitBase, public EmuCodeBlock { protected: + bool BackPatch(u32 emAddress, SContext* ctx); JitBlockCache blocks; TrampolineCache trampolines; public: JitBlockCache *GetBlockCache() override { return &blocks; } - - const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) override; - - bool IsInCodeSpace(u8 *ptr) override { return IsInSpace(ptr); } + bool HandleFault(uintptr_t access_address, SContext* ctx) override; }; extern JitBase *jit; diff --git a/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp new file mode 100644 index 0000000000..5e961bc6e5 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp @@ -0,0 +1,156 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" +#include "Common/x64ABI.h" +#include "Core/HW/Memmap.h" +#include "Core/PowerPC/JitCommon/JitBase.h" +#include "Core/PowerPC/JitCommon/TrampolineCache.h" + +#ifdef _WIN32 + #include +#endif + + +using namespace Gen; + +extern u8 *trampolineCodePtr; + +void TrampolineCache::Init() +{ + AllocCodeSpace(4 * 1024 * 1024); +} + +void TrampolineCache::Shutdown() +{ + FreeCodeSpace(); +} + +// Extremely simplistic - just generate the requested trampoline. May reuse them in the future. +const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 registersInUse) +{ + if (GetSpaceLeft() < 1024) + PanicAlert("Trampoline cache full"); + + const u8 *trampoline = GetCodePtr(); + X64Reg addrReg = (X64Reg)info.scaledReg; + X64Reg dataReg = (X64Reg)info.regOperandReg; + + // It's a read. Easy. + // RSP alignment here is 8 due to the call. + ABI_PushRegistersAndAdjustStack(registersInUse, 8); + + if (addrReg != ABI_PARAM1) + MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg)); + + if (info.displacement) + ADD(32, R(ABI_PARAM1), Imm32(info.displacement)); + + switch (info.operandSize) + { + case 4: + CALL((void *)&Memory::Read_U32); + break; + case 2: + CALL((void *)&Memory::Read_U16); + SHL(32, R(ABI_RETURN), Imm8(16)); + break; + case 1: + CALL((void *)&Memory::Read_U8); + break; + } + + if (info.signExtend && info.operandSize == 1) + { + // Need to sign extend value from Read_U8. + MOVSX(32, 8, dataReg, R(ABI_RETURN)); + } + else if (dataReg != EAX) + { + MOV(32, R(dataReg), R(ABI_RETURN)); + } + + ABI_PopRegistersAndAdjustStack(registersInUse, 8); + RET(); + return trampoline; +} + +// Extremely simplistic - just generate the requested trampoline. May reuse them in the future. +const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc) +{ + if (GetSpaceLeft() < 1024) + PanicAlert("Trampoline cache full"); + + const u8 *trampoline = GetCodePtr(); + + X64Reg dataReg = (X64Reg)info.regOperandReg; + X64Reg addrReg = (X64Reg)info.scaledReg; + + // It's a write. Yay. Remember that we don't have to be super efficient since it's "just" a + // hardware access - we can take shortcuts. + // Don't treat FIFO writes specially for now because they require a burst + // check anyway. + + // PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs + MOV(32, PPCSTATE(pc), Imm32(pc)); + + ABI_PushRegistersAndAdjustStack(registersInUse, 8); + + if (info.hasImmediate) + { + if (addrReg != ABI_PARAM2) + MOV(64, R(ABI_PARAM2), R(addrReg)); + // we have to swap back the immediate to pass it to the write functions + switch (info.operandSize) + { + case 8: + PanicAlert("Invalid 64-bit immediate!"); + break; + case 4: + MOV(32, R(ABI_PARAM1), Imm32(Common::swap32((u32)info.immediate))); + break; + case 2: + MOV(16, R(ABI_PARAM1), Imm16(Common::swap16((u16)info.immediate))); + break; + case 1: + MOV(8, R(ABI_PARAM1), Imm8((u8)info.immediate)); + break; + } + } + else + { + MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg); + } + if (info.displacement) + { + ADD(32, R(ABI_PARAM2), Imm32(info.displacement)); + } + + switch (info.operandSize) + { + case 8: + CALL((void *)&Memory::Write_U64); + break; + case 4: + CALL((void *)&Memory::Write_U32); + break; + case 2: + CALL((void *)&Memory::Write_U16); + break; + case 1: + CALL((void *)&Memory::Write_U8); + break; + } + + ABI_PopRegistersAndAdjustStack(registersInUse, 8); + RET(); + + return trampoline; +} + + diff --git a/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h new file mode 100644 index 0000000000..516a071ac2 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h @@ -0,0 +1,22 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Common/x64Analyzer.h" +#include "Common/x64Emitter.h" + +// We need at least this many bytes for backpatching. +const int BACKPATCH_SIZE = 5; + +class TrampolineCache : public Gen::X64CodeBlock +{ +public: + void Init(); + void Shutdown(); + + const u8 *GetReadTrampoline(const InstructionInfo &info, u32 registersInUse); + const u8 *GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc); +}; diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index ea9b12be70..6bfd1c4009 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -190,13 +190,9 @@ namespace JitInterface } #endif } - bool IsInCodeSpace(u8 *ptr) + bool HandleFault(uintptr_t access_address, SContext* ctx) { - return jit->IsInCodeSpace(ptr); - } - const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) - { - return jit->BackPatch(codePtr, em_address, ctx); + return jit->HandleFault(access_address, ctx); } void ClearCache() diff --git a/Source/Core/Core/PowerPC/JitInterface.h b/Source/Core/Core/PowerPC/JitInterface.h index 3cb57422bb..a8ed783726 100644 --- a/Source/Core/Core/PowerPC/JitInterface.h +++ b/Source/Core/Core/PowerPC/JitInterface.h @@ -7,6 +7,7 @@ #include #include "Common/ChunkFile.h" #include "Core/PowerPC/CPUCoreBase.h" +#include "Core/PowerPC/JitCommon/JitBackpatch.h" namespace JitInterface { @@ -20,8 +21,7 @@ namespace JitInterface void WriteProfileResults(const std::string& filename); // Memory Utilities - bool IsInCodeSpace(u8 *ptr); - const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx); + bool HandleFault(uintptr_t access_address, SContext* ctx); // used by JIT to read instructions u32 Read_Opcode_JIT(const u32 _Address); diff --git a/Source/Core/Core/x64MemTools.cpp b/Source/Core/Core/x64MemTools.cpp index 057d3a83f0..518c3bb160 100644 --- a/Source/Core/Core/x64MemTools.cpp +++ b/Source/Core/Core/x64MemTools.cpp @@ -23,42 +23,6 @@ namespace EMM { -static bool DoFault(u64 bad_address, SContext *ctx) -{ - if (!JitInterface::IsInCodeSpace((u8*) ctx->CTX_PC)) - { - // Let's not prevent debugging. - return false; - } - - u64 memspace_bottom = (u64)Memory::base; - u64 memspace_top = memspace_bottom + -#if _ARCH_64 - 0x100000000ULL; -#else - 0x40000000; -#endif - - if (bad_address < memspace_bottom || bad_address >= memspace_top) - { - return false; - } - - u32 em_address = (u32)(bad_address - memspace_bottom); - const u8 *new_pc = jit->BackPatch((u8*) ctx->CTX_PC, em_address, ctx); - if (new_pc) - { - ctx->CTX_PC = (u64) new_pc; - } - else - { - // there was an error, give the debugger a chance - return false; - } - - return true; -} - #ifdef _WIN32 LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs) @@ -74,10 +38,10 @@ LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs) } // virtual address of the inaccessible data - u64 badAddress = (u64)pPtrs->ExceptionRecord->ExceptionInformation[1]; + uintptr_t badAddress = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1]; CONTEXT *ctx = pPtrs->ContextRecord; - if (DoFault(badAddress, ctx)) + if (JitInterface::HandleFault(badAddress, ctx)) { return (DWORD)EXCEPTION_CONTINUE_EXECUTION; } @@ -198,7 +162,7 @@ void ExceptionThread(mach_port_t port) x86_thread_state64_t *state = (x86_thread_state64_t *) msg_in.old_state; - bool ok = DoFault(msg_in.code[1], state); + bool ok = JitInterface::HandleFault((uintptr_t) msg_in.code[1], state); // Set up the reply. msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0); @@ -263,12 +227,12 @@ static void sigsegv_handler(int sig, siginfo_t *info, void *raw_context) // Huh? Return. return; } - u64 bad_address = (u64)info->si_addr; + uintptr_t bad_address = (uintptr_t)info->si_addr; // Get all the information we can out of the context. mcontext_t *ctx = &context->uc_mcontext; // assume it's not a write - if (!DoFault(bad_address, ctx)) + if (!JitInterface::HandleFault(bad_address, ctx)) { // retry and crash signal(SIGSEGV, SIG_DFL);