mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
ARM/ARM64 instruction analysis, hook up to handler
This commit is contained in:
parent
aa802ecc0f
commit
c988d42b04
@ -19,7 +19,7 @@ public:
|
||||
CodeBlockCommon() {}
|
||||
virtual ~CodeBlockCommon() {}
|
||||
|
||||
bool IsInSpace(const u8 *ptr) {
|
||||
bool IsInSpace(const u8 *ptr) const {
|
||||
return (ptr >= region) && (ptr < (region + region_size));
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common/ExceptionHandlerSetup.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@ -84,6 +83,7 @@ void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_LOG(SYSTEM, "Installing exception handler");
|
||||
g_badAccessHandler = badAccessHandler;
|
||||
g_vectoredExceptionHandle = AddVectoredExceptionHandler(TRUE, Handler);
|
||||
}
|
||||
@ -91,6 +91,7 @@ void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
|
||||
void UninstallExceptionHandler() {
|
||||
RemoveVectoredExceptionHandler(g_vectoredExceptionHandle);
|
||||
g_badAccessHandler = nullptr;
|
||||
INFO_LOG(SYSTEM, "Removed exception handler");
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE)
|
||||
@ -186,7 +187,14 @@ static void ExceptionThread(mach_port_t port) {
|
||||
}
|
||||
|
||||
void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
|
||||
g_badAccessHandler = badAccessHandler;
|
||||
if (!g_badAccessHandler) {
|
||||
g_badAccessHandler = badAccessHandler;
|
||||
} else {
|
||||
// The rest of the setup we don't need to do again.
|
||||
g_badAccessHandler = badAccessHandler;
|
||||
return;
|
||||
}
|
||||
INFO_LOG(SYSTEM, "Installing exception handler");
|
||||
mach_port_t port;
|
||||
CheckKR("mach_port_allocate",
|
||||
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port));
|
||||
@ -275,6 +283,10 @@ static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) {
|
||||
}
|
||||
|
||||
void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
|
||||
if (g_badAccessHandler) {
|
||||
return;
|
||||
}
|
||||
NOTICE_LOG(SYSTEM, "Installed exception handler");
|
||||
g_badAccessHandler = badAccessHandler;
|
||||
|
||||
stack_t signal_stack;
|
||||
@ -308,10 +320,14 @@ void UninstallExceptionHandler() {
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &old_sa_bus, nullptr);
|
||||
#endif
|
||||
NOTICE_LOG(SYSTEM, "Uninstalled exception handler");
|
||||
g_badAccessHandler = nullptr;
|
||||
}
|
||||
#else // _M_GENERIC or unsupported platform
|
||||
|
||||
void InstallExceptionHandler(BadAccessHandler badAccessHandler) { }
|
||||
void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
|
||||
ERROR_LOG(SYSTEM, "Exception handler not implemented on this platform, can't install");
|
||||
}
|
||||
void UninstallExceptionHandler() { }
|
||||
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include "x64Analyzer.h"
|
||||
|
||||
bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info)
|
||||
bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
{
|
||||
int accessType = 0;
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
struct InstructionInfo
|
||||
struct LSInstructionInfo
|
||||
{
|
||||
int operandSize; //8, 16, 32, 64
|
||||
int instructionSize;
|
||||
@ -60,4 +60,4 @@ enum AccessType {
|
||||
OP_ACCESS_WRITE = 1
|
||||
};
|
||||
|
||||
bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info);
|
||||
bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info);
|
||||
|
@ -237,6 +237,7 @@ void ArmJit::GenerateFixedCode() {
|
||||
CMP(R0, 0);
|
||||
B_CC(CC_EQ, outerLoop);
|
||||
|
||||
const uint8_t *quitLoop = GetCodePtr();
|
||||
SetJumpTarget(badCoreState);
|
||||
|
||||
SaveDowncount();
|
||||
@ -251,6 +252,12 @@ void ArmJit::GenerateFixedCode() {
|
||||
|
||||
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns
|
||||
|
||||
crashHandler = GetCodePtr();
|
||||
MOVP2R(R0, &coreState);
|
||||
MOVI2R(R1, CORE_ERROR);
|
||||
STR(R1, R0, 0);
|
||||
B(quitLoop);
|
||||
|
||||
// Uncomment if you want to see the output...
|
||||
if (disasm) {
|
||||
INFO_LOG(JIT, "THE DISASM ========================");
|
||||
|
@ -51,6 +51,8 @@ public:
|
||||
|
||||
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
|
||||
|
||||
const u8 *GetCrashHandler() const override { return crashHandler; }
|
||||
bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
|
||||
MIPSOpcode GetOriginalOp(MIPSOpcode op) override;
|
||||
|
||||
@ -313,6 +315,8 @@ public:
|
||||
|
||||
const u8 *restoreRoundingMode;
|
||||
const u8 *applyRoundingMode;
|
||||
|
||||
const u8 *crashHandler;
|
||||
};
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
@ -277,6 +277,7 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
|
||||
CMP(SCRATCH1, 0);
|
||||
B(CC_EQ, outerLoop);
|
||||
|
||||
const uint8_t *quitLoop = GetCodePtr();
|
||||
SetJumpTarget(badCoreState);
|
||||
|
||||
SaveStaticRegisters();
|
||||
@ -286,6 +287,12 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
|
||||
|
||||
RET();
|
||||
|
||||
crashHandler = GetCodePtr();
|
||||
MOVP2R(SCRATCH1_64, &coreState);
|
||||
MOVI2R(SCRATCH2, CORE_ERROR);
|
||||
STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);
|
||||
B(quitLoop);
|
||||
|
||||
// Generate some integer conversion funcs.
|
||||
// MIPS order!
|
||||
static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M };
|
||||
|
@ -52,6 +52,8 @@ public:
|
||||
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
|
||||
const u8 *DoJit(u32 em_address, JitBlock *b);
|
||||
|
||||
const u8 *GetCrashHandler() const override { return crashHandler; }
|
||||
bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
|
||||
MIPSOpcode GetOriginalOp(MIPSOpcode op) override;
|
||||
|
||||
@ -281,6 +283,8 @@ public:
|
||||
const u8 *applyRoundingMode;
|
||||
const u8 *updateRoundingMode;
|
||||
|
||||
const u8 *crashHandler;
|
||||
|
||||
int jitStartOffset;
|
||||
|
||||
// Indexed by FPCR FZ:RN bits for convenience. Uses SCRATCH2.
|
||||
|
@ -160,7 +160,12 @@ public:
|
||||
void InvalidateCacheAt(u32 em_address, int length = 4) override;
|
||||
void UpdateFCR31() override;
|
||||
|
||||
bool CodeInRange(const u8 *ptr) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
const u8 *GetDispatcher() const override { return nullptr; }
|
||||
const u8 *GetCrashHandler() const override { return nullptr; }
|
||||
|
||||
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override;
|
||||
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
|
||||
|
@ -121,8 +121,10 @@ namespace MIPSComp {
|
||||
public:
|
||||
virtual ~JitInterface() {}
|
||||
|
||||
virtual bool CodeInRange(const u8 *ptr) const = 0;
|
||||
virtual bool DescribeCodePtr(const u8 *ptr, std::string &name) = 0;
|
||||
virtual const u8 *GetDispatcher() const = 0;
|
||||
virtual const u8 *GetCrashHandler() const = 0;
|
||||
virtual JitBlockCache *GetBlockCache() = 0;
|
||||
virtual JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() = 0;
|
||||
virtual void InvalidateCacheAt(u32 em_address, int length = 4) = 0;
|
||||
|
@ -206,11 +206,21 @@ void Jit::GenerateFixedCode(JitOptions &jo) {
|
||||
}
|
||||
J_CC(CC_Z, outerLoop, true);
|
||||
|
||||
const uint8_t *quitLoop = GetCodePtr();
|
||||
SetJumpTarget(badCoreState);
|
||||
RestoreRoundingMode(true);
|
||||
ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
||||
RET();
|
||||
|
||||
crashHandler = GetCodePtr();
|
||||
if (RipAccessible((const void *)&coreState)) {
|
||||
MOV(32, M(&coreState), Imm32(CORE_RUNTIME_ERROR));
|
||||
} else {
|
||||
MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
|
||||
MOV(32, MatR(RAX), Imm32(CORE_RUNTIME_ERROR));
|
||||
}
|
||||
JMP(quitLoop, true);
|
||||
|
||||
// Let's spare the pre-generated code from unprotect-reprotect.
|
||||
endOfPregeneratedCode = AlignCodePage();
|
||||
EndWrite();
|
||||
|
@ -442,6 +442,8 @@ bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) {
|
||||
name = "enterDispatcher";
|
||||
else if (ptr == restoreRoundingMode)
|
||||
name = "restoreRoundingMode";
|
||||
else if (ptr == crashHandler)
|
||||
name = "crashHandler";
|
||||
else {
|
||||
u32 jitAddr = blocks.GetAddressFromBlockPtr(ptr);
|
||||
|
||||
|
@ -63,6 +63,8 @@ public:
|
||||
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
|
||||
const u8 *DoJit(u32 em_address, JitBlock *b);
|
||||
|
||||
const u8 *GetCrashHandler() const override { return crashHandler; }
|
||||
bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
|
||||
|
||||
void Comp_RunBlock(MIPSOpcode op) override;
|
||||
@ -325,6 +327,8 @@ private:
|
||||
|
||||
const u8 *endOfPregeneratedCode;
|
||||
|
||||
const u8 *crashHandler;
|
||||
|
||||
friend class JitSafeMem;
|
||||
friend class JitSafeMemFuncs;
|
||||
};
|
||||
|
@ -28,8 +28,20 @@
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/MemArena.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
|
||||
|
||||
#if defined(PPSSPP_ARCH_AMD64) || defined(PPSSPP_ARCH_X86)
|
||||
#include "Common/MachineContext.h"
|
||||
#include "Common/x64Analyzer.h"
|
||||
#elif defined(PPSSPP_ARCH_ARM64)
|
||||
#include "Core/Util/DisArm64.h"
|
||||
typedef sigcontext SContext;
|
||||
#define CTX_PC pc
|
||||
#elif defined(PPSSPP_ARCH_ARM)
|
||||
#include "ext/disarm.h"
|
||||
typedef sigcontext SContext;
|
||||
#define CTX_PC arm_pc
|
||||
#endif
|
||||
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/HDRemaster.h"
|
||||
@ -43,6 +55,8 @@
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
#include "UI/OnScreenDisplay.h"
|
||||
|
||||
namespace Memory {
|
||||
|
||||
@ -87,6 +101,8 @@ u32 g_PSPModel;
|
||||
|
||||
std::recursive_mutex g_shutdownLock;
|
||||
|
||||
static int64_t g_numReportedBadAccesses = 0;
|
||||
|
||||
// We don't declare the IO region in here since its handled by other means.
|
||||
static MemoryView views[] =
|
||||
{
|
||||
@ -292,6 +308,8 @@ void Init() {
|
||||
|
||||
INFO_LOG(MEMMAP, "Memory system initialized. Base at %p (RAM at @ %p, uncached @ %p)",
|
||||
base, m_pPhysicalRAM, m_pUncachedRAM);
|
||||
|
||||
g_numReportedBadAccesses = 0;
|
||||
}
|
||||
|
||||
void Reinit() {
|
||||
@ -464,7 +482,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
|
||||
const uint8_t *codePtr = (uint8_t *)(context->CTX_PC);
|
||||
|
||||
// TODO: Check that codePtr is within the current JIT space.
|
||||
bool inJitSpace = true; // MIPSComp::jit->IsInSpace(codePtr);
|
||||
bool inJitSpace = MIPSComp::jit->CodeInRange(codePtr);
|
||||
if (!inJitSpace) {
|
||||
// This is a crash in non-jitted code. Not something we want to handle here, ignore.
|
||||
return false;
|
||||
@ -486,32 +504,49 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
|
||||
// OK, a guest executable did a bad access. Take care of it.
|
||||
|
||||
uint32_t guestAddress = hostAddress - baseAddress;
|
||||
ERROR_LOG(SYSTEM, "Bad memory access detected and ignored: %08x (%p)", guestAddress, hostAddress);
|
||||
// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
|
||||
|
||||
// TODO: Share the struct between the various analyzers, that will allow us to share most of
|
||||
// the implementations here.
|
||||
|
||||
#if defined(PPSSPP_ARCH_AMD64) || defined(PPSSPP_ARCH_X86)
|
||||
// X86, X86-64. Variable instruction size so need to analyze the mov instruction in detail.
|
||||
|
||||
InstructionInfo info;
|
||||
DisassembleMov(codePtr, info);
|
||||
// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
|
||||
LSInstructionInfo info;
|
||||
X86AnalyzeMOV(codePtr, info);
|
||||
#elif defined(PPSSPP_ARCH_ARM64)
|
||||
uint32_t word;
|
||||
memcpy(&word, codePtr, 4);
|
||||
// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
|
||||
Arm64LSInstructionInfo info;
|
||||
Arm64AnalyzeLoadStore((uint64_t)codePtr, word, &info);
|
||||
#elif defined(PPSSPP_ARCH_ARM)
|
||||
uint32_t word;
|
||||
memcpy(&word, codePtr, 4);
|
||||
// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
|
||||
ArmLSInstructionInfo info;
|
||||
ArmAnalyzeLoadStore((uint32_t)codePtr, word, &info);
|
||||
#endif
|
||||
|
||||
if (g_Config.bIgnoreBadMemAccess) {
|
||||
if (!info.isMemoryWrite) {
|
||||
// Must have been a read. Fill the register with 0.
|
||||
// It was a read. Fill the destination register with 0.
|
||||
// TODO
|
||||
}
|
||||
// Move on to the next instruction.
|
||||
context->CTX_PC += info.instructionSize;
|
||||
// Fall through to logging.
|
||||
} else {
|
||||
// Jump to a crash handler.
|
||||
// TODO
|
||||
context->CTX_PC += info.instructionSize;
|
||||
// Jump to a crash handler that will exit the game.
|
||||
context->CTX_PC = (uintptr_t)MIPSComp::jit->GetCrashHandler();
|
||||
ERROR_LOG(SYSTEM, "Bad memory access detected! %08x (%p) Stopping emulation.", guestAddress, (void *)hostAddress);
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
// ARM, ARM64 : All instructions are always 4 bytes in size. As an initial implementation,
|
||||
// let's just skip the offending instruction.
|
||||
context->CTX_PC += 4;
|
||||
#endif
|
||||
g_numReportedBadAccesses++;
|
||||
if (g_numReportedBadAccesses < 100) {
|
||||
ERROR_LOG(SYSTEM, "Bad memory access detected and ignored: %08x (%p)", guestAddress, (void *)hostAddress);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -265,6 +265,41 @@ static void BranchExceptionAndSystem(uint32_t w, uint64_t addr, Instruction *ins
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64AnalyzeLoadStore(uint64_t addr, uint32_t w, Arm64LSInstructionInfo *info) {
|
||||
*info = {};
|
||||
info->instructionSize = 4;
|
||||
int id = (w >> 25) & 0xF;
|
||||
switch (id) {
|
||||
case 4: case 6: case 0xC: case 0xE:
|
||||
info->isLoadOrStore = true;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(CPU, "Tried to disassemble %08x at %p as a load/store instruction", w, (void *)addr);
|
||||
return; // not the expected instruction
|
||||
}
|
||||
|
||||
info->size = w >> 30;
|
||||
info->Rt = (w & 0x1F);
|
||||
info->Rn = ((w >> 5) & 0x1F);
|
||||
info->Rm = ((w >> 16) & 0x1F);
|
||||
int opc = (w >> 22) & 0x3;
|
||||
if (opc == 0 || opc == 2) {
|
||||
info->isMemoryWrite = true;
|
||||
}
|
||||
|
||||
if (((w >> 27) & 7) == 7) {
|
||||
int V = (w >> 26) & 1;
|
||||
if (V == 0) {
|
||||
info->isIntegerLoadStore = true;
|
||||
} else {
|
||||
info->isFPLoadStore = true;
|
||||
}
|
||||
} else {
|
||||
info->isPairLoadStore = true;
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadStore(uint32_t w, uint64_t addr, Instruction *instr) {
|
||||
int size = w >> 30;
|
||||
int imm9 = SignExtend9((w >> 12) & 0x1FF);
|
||||
|
@ -24,3 +24,24 @@ typedef bool (*SymbolCallback)(char *buffer, int bufsize, uint8_t *address);
|
||||
|
||||
void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord, SymbolCallback symbolCallback = nullptr);
|
||||
|
||||
// Information about a load/store instruction.
|
||||
struct Arm64LSInstructionInfo {
|
||||
int instructionSize;
|
||||
|
||||
bool isLoadOrStore;
|
||||
|
||||
bool isIntegerLoadStore;
|
||||
bool isFPLoadStore;
|
||||
bool isPairLoadStore;
|
||||
|
||||
int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit
|
||||
bool isMemoryWrite;
|
||||
|
||||
int Rt;
|
||||
int Rn;
|
||||
int Rm;
|
||||
|
||||
// TODO: more.
|
||||
};
|
||||
|
||||
void Arm64AnalyzeLoadStore(uint64_t addr, uint32_t op, Arm64LSInstructionInfo *info);
|
||||
|
@ -72,6 +72,7 @@
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "Common/ArmEmitter.h"
|
||||
#include "ext/disarm.h"
|
||||
|
||||
static const char *CCFlagsStr[] = {
|
||||
"EQ", // Equal
|
||||
@ -759,30 +760,12 @@ static bool DisasmNeon(uint32_t op, char *text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info) {
|
||||
*info = {};
|
||||
info->instructionSize = 4;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
typedef unsigned int word;
|
||||
|
30
ext/disarm.h
30
ext/disarm.h
@ -17,18 +17,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This stuff only disassembles very old ARM but should be sufficient
|
||||
// for the basics except for MOVW/MOVT.
|
||||
|
||||
|
||||
// This stuff used to only disassemble very old ARM but has now
|
||||
// been extended to support most (but not all) modern instructions, including NEON.
|
||||
|
||||
// Disarm itself has the license you can see in the cpp file.
|
||||
// I'm not entirely sure it's 100% gpl compatible but it's nearly
|
||||
// public domain so meh.
|
||||
|
||||
// The only changes I've done is C++ compat and replaced the main
|
||||
// program with this function.
|
||||
|
||||
const char *ArmRegName(int r);
|
||||
void ArmDis(unsigned int addr, unsigned int w, char *output, int bufsize, bool includeWord);
|
||||
|
||||
// Information about a load/store instruction.
|
||||
struct ArmLSInstructionInfo {
|
||||
int instructionSize;
|
||||
|
||||
bool isIntegerLoadStore;
|
||||
bool isFPLoadStore;
|
||||
bool isMultiLoadStore;
|
||||
|
||||
int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit
|
||||
bool isMemoryWrite;
|
||||
|
||||
int Rt;
|
||||
int Rn;
|
||||
int Rm;
|
||||
|
||||
// TODO: more.
|
||||
};
|
||||
|
||||
void ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info);
|
||||
|
Loading…
Reference in New Issue
Block a user