ARM/ARM64 instruction analysis, hook up to handler

This commit is contained in:
Henrik Rydgard 2019-02-12 10:58:20 +01:00 committed by Henrik Rydgård
parent aa802ecc0f
commit c988d42b04
18 changed files with 202 additions and 51 deletions

View File

@ -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));
}

View File

@ -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) {
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

View File

@ -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;

View File

@ -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);

View File

@ -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 ========================");

View File

@ -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

View File

@ -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 };

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);