diff options
Diffstat (limited to 'src/core/arm/nce/patcher.cpp')
| -rw-r--r-- | src/core/arm/nce/patcher.cpp | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index 7d2041d4d2..f21d7cacdc 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -45,6 +45,23 @@ Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_pre) { WriteLoadContext(c_pre); } +Patcher::Patcher(std::span<const u8> text) : c(m_patch_instructions), c_pre(m_patch_instructions_pre) { + modules.emplace_back(); + curr_patch = &modules.back(); + const auto words = std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)}; + for (u32 i = ModuleCodeIndex; i < static_cast<u32>(words.size()); ++i) { + const uintptr_t offset = i * sizeof(u32); + if (auto mrs = MRS{words[i]}; mrs.Verify() && mrs.GetRt() != 31 && (mrs.GetSystemReg() == TpidrEl0 || mrs.GetSystemReg() == TpidrroEl0)) { + BranchToPatch(offset); + const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0 : oaknut::SystemReg::TPIDR_EL0; + WriteMrsHandler(offset + sizeof(u32), oaknut::XReg{static_cast<int>(mrs.GetRt())}, src_reg); + } else if (auto msr = MSR{words[i]}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) { + BranchToPatch(offset); + WriteMsrHandler(offset + sizeof(u32), oaknut::XReg{static_cast<int>(msr.GetRt())}); + } + } +} + Patcher::~Patcher() = default; bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code) { @@ -182,6 +199,31 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet return true; } +std::span<const u32> Patcher::RelocateRuntimeTls( + s64 patch_offset, std::vector<InstructionPatch>& out_patches) { + const auto& patch = modules.front(); + const auto can_branch = [](s64 offset) { + return offset % 4 == 0 && offset >= -static_cast<s64>(MaxRelativeBranch) && offset < static_cast<s64>(MaxRelativeBranch); + }; + out_patches.clear(); + out_patches.reserve(patch.m_branch_to_patch_relocations.size()); + for (const auto& rel : patch.m_branch_to_patch_relocations) { + const s64 offset = patch_offset + rel.patch_offset - static_cast<s64>(rel.module_offset); + if (!can_branch(offset)) {return {};} + u32 replacement; + oaknut::CodeGenerator rc{&replacement}; + rc.B(offset); + out_patches.push_back({rel.module_offset, replacement}); + } + for (const auto& rel : patch.m_branch_to_module_relocations) { + const s64 offset = static_cast<s64>(rel.module_offset) - patch_offset - rel.patch_offset; + if (!can_branch(offset)) {return {};} + oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; + rc.B(offset); + } + return m_patch_instructions; +} + bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines) { const size_t patch_size = GetSectionSize(); const size_t pre_patch_size = GetPreSectionSize(); |
