diff --git a/src/binary_translation/CMakeLists.txt b/src/binary_translation/CMakeLists.txt index 000392281..394d4321d 100644 --- a/src/binary_translation/CMakeLists.txt +++ b/src/binary_translation/CMakeLists.txt @@ -13,6 +13,7 @@ set(SRCS Instructions/MovShift.cpp Instructions/Branch.cpp Instructions/Arithmetic.cpp + Instructions/Ldr.cpp ) set(HEADERS CodeGen.h @@ -30,6 +31,7 @@ set(HEADERS Instructions/MovShift.h Instructions/Branch.h Instructions/Arithmetic.h + Instructions/Ldr.h ) create_directory_groups(${SRCS} ${HEADERS}) diff --git a/src/binary_translation/InstructionBlock.h b/src/binary_translation/InstructionBlock.h index 53038a5e7..d0b372b76 100644 --- a/src/binary_translation/InstructionBlock.h +++ b/src/binary_translation/InstructionBlock.h @@ -49,8 +49,7 @@ public: * Creates a basic block for use by instructions */ llvm::BasicBlock *CreateBasicBlock(const char *name); - - /* + /* * Links two instructions, adding to prev and next lists */ static void Link(InstructionBlock *prev, InstructionBlock *next); diff --git a/src/binary_translation/Instructions/Instruction.h b/src/binary_translation/Instructions/Instruction.h index 343a8cb45..51652bfe4 100644 --- a/src/binary_translation/Instructions/Instruction.h +++ b/src/binary_translation/Instructions/Instruction.h @@ -107,5 +107,5 @@ Instruction::FieldDefObject Instruction::FieldDef(Type* field) template void Instruction::WriteFunction(u32 value, void *field_address) { - *static_cast(field_address) = static_cast(value); + *static_cast(field_address) = Type(value); } \ No newline at end of file diff --git a/src/binary_translation/Instructions/Ldr.cpp b/src/binary_translation/Instructions/Ldr.cpp new file mode 100644 index 000000000..d1129bc24 --- /dev/null +++ b/src/binary_translation/Instructions/Ldr.cpp @@ -0,0 +1,107 @@ +#include "Ldr.h" +#include "Disassembler.h" +#include "InstructionBlock.h" +#include +#include +#include +#include "MachineState.h" + +static RegisterInstruction register_instruction; + +bool Ldr::Decode() +{ + if (ReadFields({ CondDef(), FieldDef<4>(5), FieldDef<1>(&U), FieldDef<7>(0x1f), + FieldDef<4>(&rt), FieldDef<12>(&imm12)})) + { + form = Form::PC; + return true; + } + if (ReadFields({ CondDef(), FieldDef<3>(2), FieldDef<1>(&P), FieldDef<1>(&U), FieldDef<1>(0), FieldDef<1>(&W), FieldDef<1>(1), FieldDef<4>(&rn), + FieldDef<4>(&rt), FieldDef<12>(&imm12) })) + { + form = Form::Reg; + + if (!P && W) return false; // SEE LDRT; + if (rn == Register::SP && !P && U && !W && imm12 == 4) return false; // SEE POP; + if ((!P || W) && rn == rt) return false; // UNPREDICTABLE; + + return true; + } + if (ReadFields({ CondDef(), FieldDef<6>(0x22), FieldDef<1>(&W), FieldDef<1>(1), FieldDef<4>(&rn), + FieldDef<16>(®ister_list) })) + { + form = Form::MultiReg; + + if (W && rn == Register::SP && register_list.size() > 1) return false; // SEE POP (ARM); + if (rn == Register::PC || register_list.size() < 1) return false; // UNPREDICTABLE; + if (W && register_list[(u32)rn]) return false; // UNPREDICTABLE; + + return true; + } + return false; +} + +void Ldr::GenerateInstructionCode(InstructionBlock* instruction_block) +{ + auto ir_builder = instruction_block->IrBuilder(); + + if (form != Form::MultiReg) + { + llvm::Value *address = nullptr; + llvm::Value *value = nullptr; + + auto add = (bool)U; + + if (form == Form::PC) + { + auto base = instruction_block->Address() + 8; + auto constAddress = add ? base + imm12 : base - imm12; + auto constAddressEnd = constAddress + 4; + // If the value is read only, inline it + if (constAddress >= Loader::ROMCodeStart && constAddressEnd <= (Loader::ROMCodeStart + Loader::ROMCodeSize) || + constAddress >= Loader::ROMReadOnlyDataStart && constAddressEnd <= (Loader::ROMReadOnlyDataStart + Loader::ROMReadOnlyDataSize)) + { + value = ir_builder->getInt32(Memory::Read32(constAddress)); + } + else + { + address = ir_builder->getInt32(constAddress); + } + } + else + { + auto index = (bool)P; + auto wback = !P || W; + auto source_register = instruction_block->Read(rn); + auto imm32 = ir_builder->getInt32(add ? imm12 : -imm12); + + auto offset_address = ir_builder->CreateAdd(source_register, imm32); + address = index ? offset_address : source_register; + if (wback) + instruction_block->Write(rn, offset_address); + } + + if (!value) value = instruction_block->Module()->Machine()->ReadMemory32(address); + instruction_block->Write(rt, value); + + if (rt == Register::PC) + instruction_block->Module()->BranchReadPC(); + } + else + { + auto wback = (bool)W; + auto address = instruction_block->Read(rn); + for (auto i = 0; i < 16; ++i) + { + if (!register_list[i]) continue; + instruction_block->Write((Register)i, instruction_block->Module()->Machine()->ReadMemory32(address)); + address = ir_builder->CreateAdd(address, ir_builder->getInt32(4)); + } + + if (wback) + instruction_block->Write(rn, address); + + if (register_list[15]) + instruction_block->Module()->BranchReadPC(); + } +} \ No newline at end of file diff --git a/src/binary_translation/Instructions/Ldr.h b/src/binary_translation/Instructions/Ldr.h new file mode 100644 index 000000000..73c25cac8 --- /dev/null +++ b/src/binary_translation/Instructions/Ldr.h @@ -0,0 +1,26 @@ +#include "Instruction.h" +#include "Types.h" +#include + +class Ldr : public Instruction +{ +public: + enum class Form + { + PC, Reg, MultiReg + }; + +public: + virtual bool Decode() override; + void GenerateInstructionCode(InstructionBlock* instruction_block) override; + +private: + Form form; + bool U; + Register rt; + u32 imm12; + bool P; + bool W; + Register rn; + std::bitset<16> register_list; +}; \ No newline at end of file diff --git a/src/binary_translation/MachineState.cpp b/src/binary_translation/MachineState.cpp index 127b73ad2..dc9314dc7 100644 --- a/src/binary_translation/MachineState.cpp +++ b/src/binary_translation/MachineState.cpp @@ -23,6 +23,12 @@ void MachineState::GenerateGlobals() auto flags_global_initializer = ConstantPointerNull::get(IntegerType::getInt1PtrTy(getGlobalContext())); flags_global = new GlobalVariable(*module->Module(), flags_global_initializer->getType(), false, GlobalValue::ExternalLinkage, flags_global_initializer, "Flags"); + + auto memory_read_32_signature = FunctionType::get(IntegerType::getInt32Ty(getGlobalContext()), IntegerType::getInt32Ty(getGlobalContext()), false); + auto memory_read_32_ptr = PointerType::get(memory_read_32_signature, 0); + auto memory_read_32_initializer = ConstantPointerNull::get(memory_read_32_ptr); + memory_read_32_global = new GlobalVariable(*module->Module(), memory_read_32_ptr, + false, GlobalValue::ExternalLinkage, memory_read_32_initializer, "Memory::Read32"); } Value *MachineState::GetRegisterPtr(Register reg) @@ -87,4 +93,17 @@ Value* MachineState::ConditionPassed(Condition cond) if (not) pred = ir_builder->CreateNot(pred); return pred; +} + +llvm::Value* MachineState::ReadMemory32(llvm::Value* address) +{ + auto ir_builder = module->IrBuilder(); + + auto memory_read_32 = ir_builder->CreateLoad(memory_read_32_global); + module->GetTBAA()->TagConst(memory_read_32); + + auto call = ir_builder->CreateCall(memory_read_32, address); + call->setOnlyReadsMemory(); + module->GetTBAA()->TagMemory(call); + return call; } \ No newline at end of file diff --git a/src/binary_translation/MachineState.h b/src/binary_translation/MachineState.h index e4d9ddac4..603626ecf 100644 --- a/src/binary_translation/MachineState.h +++ b/src/binary_translation/MachineState.h @@ -23,6 +23,7 @@ public: llvm::Value *ReadRegiser(Register reg); llvm::Value *WriteRegiser(Register reg, llvm::Value *value); llvm::Value* ConditionPassed(Condition cond); + llvm::Value* ReadMemory32(llvm::Value* address); private: // Returns the address of a register or a flag llvm::Value *GetRegisterPtr(Register reg); @@ -40,4 +41,10 @@ private: * Orderered N, Z, C, V */ llvm::GlobalVariable *flags_global; + + /* + * u32 (u32) Memory::Read + * Reads the memory at address + */ + llvm::GlobalVariable *memory_read_32_global; }; \ No newline at end of file diff --git a/src/binary_translation/TBAA.cpp b/src/binary_translation/TBAA.cpp index ed638a337..c481e5750 100644 --- a/src/binary_translation/TBAA.cpp +++ b/src/binary_translation/TBAA.cpp @@ -21,6 +21,7 @@ void TBAA::GenerateTags() } const_node = md_builder.createTBAAScalarTypeNode("Readonly", tbaa_root); instruction_count_node = md_builder.createTBAAScalarTypeNode("InstructionCount", tbaa_root); + memory_node = md_builder.createTBAAScalarTypeNode("Memory", tbaa_root); } void TBAA::TagRegister(Instruction* instruction, Register reg) @@ -36,4 +37,9 @@ void TBAA::TagConst(Instruction* instruction) void TBAA::TagInstructionCount(llvm::Instruction* instruction) { instruction->setMetadata(LLVMContext::MD_tbaa, instruction_count_node); +} + +void TBAA::TagMemory(llvm::Instruction* instruction) +{ + instruction->setMetadata(LLVMContext::MD_tbaa, memory_node); } \ No newline at end of file diff --git a/src/binary_translation/TBAA.h b/src/binary_translation/TBAA.h index 71798e034..657b4fdde 100644 --- a/src/binary_translation/TBAA.h +++ b/src/binary_translation/TBAA.h @@ -1,5 +1,6 @@ #pragma once #include "Instructions/Types.h" +#include namespace llvm { @@ -22,10 +23,12 @@ public: void TagRegister(llvm::Instruction *instruction, Register reg); void TagConst(llvm::Instruction *instruction); void TagInstructionCount(llvm::Instruction *instruction); + void TagMemory(llvm::Instruction *instruction); private: llvm::MDNode *register_nodes[RegisterCount]; // Tag for everything that is never written. // Since it is never written, one tag works llvm::MDNode *const_node; llvm::MDNode *instruction_count_node; + llvm::MDNode *memory_node; }; \ No newline at end of file diff --git a/src/core/binary_translation/BinaryTranslationLoader.cpp b/src/core/binary_translation/BinaryTranslationLoader.cpp index 4904aeb62..217005094 100644 --- a/src/core/binary_translation/BinaryTranslationLoader.cpp +++ b/src/core/binary_translation/BinaryTranslationLoader.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace llvm; @@ -120,14 +121,16 @@ void BinaryTranslationLoader::Load(FileUtil::IOFile& file) g_can_run_function = static_cast(g_dyld->getSymbolAddress("CanRun")); auto verify_ptr = static_cast(g_dyld->getSymbolAddress("Verify")); g_instruction_count = static_cast(g_dyld->getSymbolAddress("InstructionCount")); + auto memory_read_32_ptr = static_cast(g_dyld->getSymbolAddress("Memory::Read32")); - if (!g_run_function || !g_can_run_function || !verify_ptr || !g_instruction_count) + if (!g_run_function || !g_can_run_function || !verify_ptr || !g_instruction_count || !memory_read_32_ptr) { LOG_WARNING(Loader, "Cannot load optimized file, missing critical function"); return; } g_verify = *verify_ptr; + *memory_read_32_ptr = &Memory::Read32; g_enabled = true;