From 1b87113184e4bfa3faf3428ee459d82bed68fa69 Mon Sep 17 00:00:00 2001 From: Dani Messerman Date: Sun, 3 May 2015 21:20:25 +0300 Subject: [PATCH] Added an option to verify the output. This is done by stopping after each instruction and checking what the interpreter produced vs. the binary translation. --- src/binary_translation/CodeGen.cpp | 7 +- src/binary_translation/CodeGen.h | 3 +- src/binary_translation/ModuleGen.cpp | 46 +++++++--- src/binary_translation/ModuleGen.h | 10 +- src/binary_translation/main.cpp | 4 +- .../arm/dyncom/arm_dyncom_interpreter.cpp | 1 + .../BinaryTranslationLoader.cpp | 91 ++++++++++++++++++- .../BinaryTranslationLoader.h | 1 + 8 files changed, 142 insertions(+), 21 deletions(-) diff --git a/src/binary_translation/CodeGen.cpp b/src/binary_translation/CodeGen.cpp index 6bad65d0c..3fc358773 100644 --- a/src/binary_translation/CodeGen.cpp +++ b/src/binary_translation/CodeGen.cpp @@ -18,9 +18,10 @@ using namespace llvm; -CodeGen::CodeGen(const char* output_object_filename, const char* output_debug_filename) +CodeGen::CodeGen(const char* output_object_filename, const char* output_debug_filename, bool verify) : output_object_filename(output_object_filename), - output_debug_filename(output_debug_filename) + output_debug_filename(output_debug_filename), + verify(verify) { } @@ -68,7 +69,7 @@ void CodeGen::IntializeLLVM() void CodeGen::GenerateModule() { - moduleGenerator = std::make_unique(module.get()); + moduleGenerator = std::make_unique(module.get(), verify); moduleGenerator->Run(); } diff --git a/src/binary_translation/CodeGen.h b/src/binary_translation/CodeGen.h index 4a6b9e2dd..2e234d0d6 100644 --- a/src/binary_translation/CodeGen.h +++ b/src/binary_translation/CodeGen.h @@ -15,7 +15,7 @@ class ModuleGen; class CodeGen { public: - CodeGen(const char *output_object_filename, const char *output_debug_filename); + CodeGen(const char *output_object_filename, const char *output_debug_filename, bool verify); ~CodeGen(); void Run(); @@ -27,6 +27,7 @@ public: private: const char *output_object_filename; const char *output_debug_filename; + bool verify; std::unique_ptr moduleGenerator; diff --git a/src/binary_translation/ModuleGen.cpp b/src/binary_translation/ModuleGen.cpp index f2bbd0a1a..64ffc1352 100644 --- a/src/binary_translation/ModuleGen.cpp +++ b/src/binary_translation/ModuleGen.cpp @@ -14,8 +14,9 @@ using namespace llvm; -ModuleGen::ModuleGen(llvm::Module* module) - : module(module) +ModuleGen::ModuleGen(llvm::Module* module, bool verify) + : module(module), + verify(verify) { ir_builder = make_unique>(getGlobalContext()); machine = make_unique(this); @@ -47,25 +48,41 @@ void ModuleGen::Run() void ModuleGen::BranchReadPC() { - auto call = ir_builder->CreateCall(run_function); - call->setTailCall(); - ir_builder->CreateRetVoid(); + if (verify) + { + ir_builder->CreateRetVoid(); + } + else + { + auto call = ir_builder->CreateCall(run_function); + call->setTailCall(); + ir_builder->CreateRetVoid(); + } } void ModuleGen::BranchWritePCConst(InstructionBlock *current, u32 pc) { - auto i = instruction_blocks_by_pc.find(pc); - if (i != instruction_blocks_by_pc.end()) + if (verify) { - // Found instruction, jump to it - ir_builder->CreateBr(i->second->GetEntryBasicBlock()); - InstructionBlock::Link(i->second, current); + // Just write PC and exit on verify + machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc)); + ir_builder->CreateRetVoid(); } else { - // Didn't find instruction, write PC and exit - machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc)); - ir_builder->CreateRetVoid(); + auto i = instruction_blocks_by_pc.find(pc); + if (i != instruction_blocks_by_pc.end()) + { + // Found instruction, jump to it + ir_builder->CreateBr(i->second->GetEntryBasicBlock()); + InstructionBlock::Link(i->second, current); + } + else + { + // Didn't find instruction, write PC and exit + machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc)); + ir_builder->CreateRetVoid(); + } } } @@ -91,6 +108,9 @@ void ModuleGen::GenerateGlobals() block_address_array_type = ArrayType::get(block_address_type, block_address_array_size); block_address_array = new GlobalVariable(*module, block_address_array_type, true, GlobalValue::ExternalLinkage, nullptr, "BlockAddressArray"); + + // bool Verify - contains the value of verify for citra usage + new GlobalVariable(*module, ir_builder->getInt1Ty(), true, GlobalValue::ExternalLinkage, ir_builder->getInt1(verify), "Verify"); } void ModuleGen::GenerateBlockAddressArray() diff --git a/src/binary_translation/ModuleGen.h b/src/binary_translation/ModuleGen.h index 486e2d763..ef08bf964 100644 --- a/src/binary_translation/ModuleGen.h +++ b/src/binary_translation/ModuleGen.h @@ -18,7 +18,11 @@ namespace llvm class ModuleGen { public: - explicit ModuleGen(llvm::Module *module); + /* + * Verify - produce a code that can be verified + * this is done by returning after every opcode + */ + explicit ModuleGen(llvm::Module *module, bool verify); ~ModuleGen(); void Run(); @@ -50,11 +54,13 @@ private: // inter block jumps void ColorBlocks(); + llvm::Module *module; + bool verify; + std::unique_ptr machine; std::unique_ptr tbaa; std::unique_ptr> ir_builder; - llvm::Module *module; size_t block_address_array_base; size_t block_address_array_size; diff --git a/src/binary_translation/main.cpp b/src/binary_translation/main.cpp index 556fab3c9..839d38110 100644 --- a/src/binary_translation/main.cpp +++ b/src/binary_translation/main.cpp @@ -12,6 +12,7 @@ namespace cl = llvm::cl; cl::opt InputFilename(cl::Positional, cl::Required, cl::desc("")); cl::opt OutputFilename(cl::Positional, cl::Required, cl::desc("")); cl::opt DebugFilename(cl::Positional, cl::Optional, cl::desc("")); +cl::opt Verify("verify", cl::desc(""), cl::init(false)); int main(int argc, const char *const *argv) { @@ -39,6 +40,7 @@ int main(int argc, const char *const *argv) auto input_rom = InputFilename.c_str(); auto output_object = OutputFilename.c_str(); auto output_debug = DebugFilename.getNumOccurrences() ? DebugFilename.c_str() : nullptr; + bool verify = Verify; Core::Init(); Memory::Init(); @@ -50,6 +52,6 @@ int main(int argc, const char *const *argv) return -1; } - CodeGen code_generator(output_object, output_debug); + CodeGen code_generator(output_object, output_debug, verify); code_generator.Run(); } \ No newline at end of file diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 5f60b1fdc..a9b87b7a3 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -3717,6 +3717,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { #define GOTO_NEXT_INST \ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \ num_instrs++; \ + BinaryTranslationLoader::VerifyCallback(); \ switch(inst_base->idx) { \ case 0: goto VMLA_INST; \ case 1: goto VMLS_INST; \ diff --git a/src/core/binary_translation/BinaryTranslationLoader.cpp b/src/core/binary_translation/BinaryTranslationLoader.cpp index 81d4fb6f3..475d198d9 100644 --- a/src/core/binary_translation/BinaryTranslationLoader.cpp +++ b/src/core/binary_translation/BinaryTranslationLoader.cpp @@ -10,6 +10,7 @@ using namespace llvm; bool g_enabled = false; +bool g_verify = false; std::unique_ptr g_memory_manager; std::unique_ptr g_dyld; @@ -17,6 +18,15 @@ std::unique_ptr g_loaded_object_info; void (*g_run_function)(); +// Used by the verifier +bool (*g_can_run_function)(); +bool g_have_saved_state; // Whether there is a copied state +ARMul_State *g_state; +u32 g_regs_copy[16]; +u32 g_flags_copy[4]; +u32 g_regs_copy_before[16]; +u32 g_flags_copy_before[4]; + void BinaryTranslationLoader::Load(FileUtil::IOFile& file) { if (offsetof(ARMul_State, ZFlag) - offsetof(ARMul_State, NFlag) != 4 || @@ -61,6 +71,8 @@ void BinaryTranslationLoader::Load(FileUtil::IOFile& file) g_memory_manager->finalizeMemory(); g_run_function = static_cast(g_dyld->getSymbolAddress("Run")); + g_can_run_function = static_cast(g_dyld->getSymbolAddress("CanRun")); + g_verify = *static_cast(g_dyld->getSymbolAddress("Verify")); g_enabled = true; } @@ -74,11 +86,88 @@ void BinaryTranslationLoader::SetCpuState(ARMul_State* state) *regs_ptr = state->Reg; *flags_ptr = &state->NFlag; + g_have_saved_state = false; + g_state = state; } void BinaryTranslationLoader::Run() { - if (!g_enabled) return; + // If verify is enabled, it will run opcodes + if (!g_enabled || g_verify) return; g_run_function(); +} + +void Swap(void *a, void *b, size_t size) +{ + auto a_char = (char *)a; + auto b_char = (char *)b; + for (auto i = 0; i < size; ++i) + { + std::swap(a_char[i], b_char[i]); + } +} + +void ShowRegs(u32 *regs, u32 *flags) +{ + LOG_ERROR(BinaryTranslator, "%08x %08x %08x %08x %08x %08x %08x %08x", + regs[0], regs[1], regs[2], regs[3], + regs[4], regs[5], regs[6], regs[7]); + LOG_ERROR(BinaryTranslator, "%08x %08x %08x %08x %08x %08x %08x %08x", + regs[8], regs[9], regs[10], regs[11], + regs[12], regs[13], regs[14], regs[15]); + LOG_ERROR(BinaryTranslator, "%01x %01x %01x %01x", flags[0], flags[1], flags[2], flags[3]); +} + +void BinaryTranslationLoader::VerifyCallback() +{ + if (!g_enabled || !g_verify) return; + + // Swap the PC to the old state before checking if it can run + std::swap(g_regs_copy[15], g_state->Reg[15]); + auto can_run = g_can_run_function(); + std::swap(g_regs_copy[15], g_state->Reg[15]); + + if (g_have_saved_state && can_run) + { + // An opcode is finished, simulate it + + // Copy the state before + memcpy(g_regs_copy_before, g_regs_copy, sizeof(g_regs_copy)); + memcpy(g_flags_copy_before, &g_flags_copy, sizeof(g_flags_copy)); + + // Swap to the state before the opcode + Swap(g_state->Reg, g_regs_copy, sizeof(g_regs_copy)); + Swap(&g_state->NFlag, g_flags_copy, sizeof(g_flags_copy)); + + // Run the opcode + g_run_function(); + + // Test + if (memcmp(g_state->Reg, g_regs_copy, sizeof(g_regs_copy)) || memcmp(&g_state->NFlag, g_flags_copy, sizeof(g_flags_copy))) + { + LOG_ERROR(BinaryTranslator, "Verify failed"); + LOG_ERROR(BinaryTranslator, "Regs Before"); + ShowRegs(g_regs_copy_before, g_flags_copy_before); + LOG_ERROR(BinaryTranslator, "Regs OK"); + ShowRegs(g_regs_copy, g_flags_copy); + LOG_ERROR(BinaryTranslator, "Regs not OK"); + ShowRegs(g_state->Reg, &g_state->NFlag); + + // Don't spam + g_enabled = false; + + // Make sure it has a valid state to continue to run + Swap(g_state->Reg, g_regs_copy, sizeof(g_regs_copy)); + Swap(&g_state->NFlag, g_flags_copy, sizeof(g_flags_copy)); + } + } + else + { + // If this opcode is not translated or there is no saved state, just save the state and continue + memcpy(g_regs_copy, g_state->Reg, sizeof(g_regs_copy)); + memcpy(g_flags_copy, &g_state->NFlag, sizeof(g_flags_copy)); + + g_have_saved_state = true; + } } \ No newline at end of file diff --git a/src/core/binary_translation/BinaryTranslationLoader.h b/src/core/binary_translation/BinaryTranslationLoader.h index 51f72ea3a..584d70190 100644 --- a/src/core/binary_translation/BinaryTranslationLoader.h +++ b/src/core/binary_translation/BinaryTranslationLoader.h @@ -11,4 +11,5 @@ public: static void SetCpuState(ARMul_State *state); // Runs the state provided at SetCpuState. static void Run(); + static void VerifyCallback(); }; \ No newline at end of file