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.
This commit is contained in:
parent
7b0fdb52cd
commit
755bd2c445
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -229,6 +229,7 @@
|
|||
<ClCompile Include="PowerPC\JitCommon\JitBase.cpp" />
|
||||
<ClCompile Include="PowerPC\JitCommon\JitCache.cpp" />
|
||||
<ClCompile Include="PowerPC\JitCommon\Jit_Util.cpp" />
|
||||
<ClCompile Include="PowerPC\JitCommon\TrampolineCache.cpp" />
|
||||
<ClCompile Include="PowerPC\JitInterface.cpp" />
|
||||
<ClCompile Include="PowerPC\PowerPC.cpp" />
|
||||
<ClCompile Include="PowerPC\PPCAnalyst.cpp" />
|
||||
|
@ -406,6 +407,7 @@
|
|||
<ClInclude Include="PowerPC\JitCommon\JitBase.h" />
|
||||
<ClInclude Include="PowerPC\JitCommon\JitCache.h" />
|
||||
<ClInclude Include="PowerPC\JitCommon\Jit_Util.h" />
|
||||
<ClInclude Include="PowerPC\JitCommon\TrampolineCache.h" />
|
||||
<ClInclude Include="PowerPC\JitInterface.h" />
|
||||
<ClInclude Include="PowerPC\PowerPC.h" />
|
||||
<ClInclude Include="PowerPC\PPCAnalyst.h" />
|
||||
|
|
|
@ -640,6 +640,9 @@
|
|||
<ClCompile Include="PowerPC\JitCommon\JitCache.cpp">
|
||||
<Filter>PowerPC\JitCommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PowerPC\JitCommon\TrampolineCache.cpp">
|
||||
<Filter>PowerPC\JitCommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PowerPC\Jit64IL\IR_X86.cpp">
|
||||
<Filter>PowerPC\JitIL</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1182,6 +1185,9 @@
|
|||
<ClInclude Include="PowerPC\JitCommon\JitCache.h">
|
||||
<Filter>PowerPC\JitCommon</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PowerPC\JitCommon\TrampolineCache.h">
|
||||
<Filter>PowerPC\JitCommon</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PowerPC\Jit64IL\JitIL.h">
|
||||
<Filter>PowerPC\JitIL</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -3,24 +3,14 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
|
||||
#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 <windows.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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 <stddef.h>
|
||||
#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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
|
||||
#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 <windows.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
};
|
|
@ -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()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <string>
|
||||
#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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue