diff options
| author | 2024-01-15 13:52:38 -0500 | |
|---|---|---|
| committer | 2024-01-15 13:52:38 -0500 | |
| commit | 04f4eeaca2722b901a60dffd955aed993c95bd05 (patch) | |
| tree | a8a16cfc59ac26bb8a56fa80d91bf311fa03810e /src | |
| parent | Merge pull request #12665 from german77/proof (diff) | |
| parent | core: Support multiple modules per patcher (diff) | |
| download | yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.gz yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.xz yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.zip | |
Merge pull request #12677 from GPUCode/whyy-modders
core: Support multiple modules per patcher
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/arm/nce/patcher.cpp | 83 | ||||
| -rw-r--r-- | src/core/arm/nce/patcher.h | 27 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_process.cpp | 4 | ||||
| -rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 75 | ||||
| -rw-r--r-- | src/core/loader/nso.cpp | 41 | ||||
| -rw-r--r-- | src/core/loader/nso.h | 3 |
6 files changed, 154 insertions, 79 deletions
diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index 47a7a8880..c7285e3a0 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp | |||
| @@ -22,14 +22,10 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; | |||
| 22 | constexpr size_t MaxRelativeBranch = 128_MiB; | 22 | constexpr size_t MaxRelativeBranch = 128_MiB; |
| 23 | constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); | 23 | constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); |
| 24 | 24 | ||
| 25 | Patcher::Patcher() : c(m_patch_instructions) {} | 25 | Patcher::Patcher() : c(m_patch_instructions) { |
| 26 | 26 | // The first word of the patch section is always a branch to the first instruction of the | |
| 27 | Patcher::~Patcher() = default; | 27 | // module. |
| 28 | 28 | c.dw(0); | |
| 29 | void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, | ||
| 30 | const Kernel::CodeSet::Segment& code) { | ||
| 31 | // Branch to the first instruction of the module. | ||
| 32 | this->BranchToModule(0); | ||
| 33 | 29 | ||
| 34 | // Write save context helper function. | 30 | // Write save context helper function. |
| 35 | c.l(m_save_context); | 31 | c.l(m_save_context); |
| @@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, | |||
| 38 | // Write load context helper function. | 34 | // Write load context helper function. |
| 39 | c.l(m_load_context); | 35 | c.l(m_load_context); |
| 40 | WriteLoadContext(); | 36 | WriteLoadContext(); |
| 37 | } | ||
| 38 | |||
| 39 | Patcher::~Patcher() = default; | ||
| 40 | |||
| 41 | bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image, | ||
| 42 | const Kernel::CodeSet::Segment& code) { | ||
| 43 | // If we have patched modules but cannot reach the new module, then it needs its own patcher. | ||
| 44 | const size_t image_size = program_image.size(); | ||
| 45 | if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) { | ||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 49 | // Add a new module patch to our list | ||
| 50 | modules.emplace_back(); | ||
| 51 | curr_patch = &modules.back(); | ||
| 52 | |||
| 53 | // The first word of the patch section is always a branch to the first instruction of the | ||
| 54 | // module. | ||
| 55 | curr_patch->m_branch_to_module_relocations.push_back({0, 0}); | ||
| 41 | 56 | ||
| 42 | // Retrieve text segment data. | 57 | // Retrieve text segment data. |
| 43 | const auto text = std::span{program_image}.subspan(code.offset, code.size); | 58 | const auto text = std::span{program_image}.subspan(code.offset, code.size); |
| @@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, | |||
| 94 | } | 109 | } |
| 95 | 110 | ||
| 96 | if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { | 111 | if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { |
| 97 | m_exclusives.push_back(i); | 112 | curr_patch->m_exclusives.push_back(i); |
| 98 | } | 113 | } |
| 99 | } | 114 | } |
| 100 | 115 | ||
| 101 | // Determine patching mode for the final relocation step | 116 | // Determine patching mode for the final relocation step |
| 102 | const size_t image_size = program_image.size(); | 117 | total_program_size += image_size; |
| 103 | this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; | 118 | this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; |
| 119 | return true; | ||
| 104 | } | 120 | } |
| 105 | 121 | ||
| 106 | void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, | 122 | bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, |
| 107 | const Kernel::CodeSet::Segment& code, | 123 | const Kernel::CodeSet::Segment& code, |
| 108 | Kernel::PhysicalMemory& program_image, | 124 | Kernel::PhysicalMemory& program_image, |
| 109 | EntryTrampolines* out_trampolines) { | 125 | EntryTrampolines* out_trampolines) { |
| @@ -120,7 +136,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, | |||
| 120 | if (mode == PatchMode::PreText) { | 136 | if (mode == PatchMode::PreText) { |
| 121 | rc.B(rel.patch_offset - patch_size - rel.module_offset); | 137 | rc.B(rel.patch_offset - patch_size - rel.module_offset); |
| 122 | } else { | 138 | } else { |
| 123 | rc.B(image_size - rel.module_offset + rel.patch_offset); | 139 | rc.B(total_program_size - rel.module_offset + rel.patch_offset); |
| 124 | } | 140 | } |
| 125 | }; | 141 | }; |
| 126 | 142 | ||
| @@ -129,7 +145,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, | |||
| 129 | if (mode == PatchMode::PreText) { | 145 | if (mode == PatchMode::PreText) { |
| 130 | rc.B(patch_size - rel.patch_offset + rel.module_offset); | 146 | rc.B(patch_size - rel.patch_offset + rel.module_offset); |
| 131 | } else { | 147 | } else { |
| 132 | rc.B(rel.module_offset - image_size - rel.patch_offset); | 148 | rc.B(rel.module_offset - total_program_size - rel.patch_offset); |
| 133 | } | 149 | } |
| 134 | }; | 150 | }; |
| 135 | 151 | ||
| @@ -137,7 +153,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, | |||
| 137 | if (mode == PatchMode::PreText) { | 153 | if (mode == PatchMode::PreText) { |
| 138 | return GetInteger(load_base) + patch_offset; | 154 | return GetInteger(load_base) + patch_offset; |
| 139 | } else { | 155 | } else { |
| 140 | return GetInteger(load_base) + image_size + patch_offset; | 156 | return GetInteger(load_base) + total_program_size + patch_offset; |
| 141 | } | 157 | } |
| 142 | }; | 158 | }; |
| 143 | 159 | ||
| @@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, | |||
| 150 | }; | 166 | }; |
| 151 | 167 | ||
| 152 | // We are now ready to relocate! | 168 | // We are now ready to relocate! |
| 153 | for (const Relocation& rel : m_branch_to_patch_relocations) { | 169 | auto& patch = modules[m_relocate_module_index++]; |
| 170 | for (const Relocation& rel : patch.m_branch_to_patch_relocations) { | ||
| 154 | ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); | 171 | ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); |
| 155 | } | 172 | } |
| 156 | for (const Relocation& rel : m_branch_to_module_relocations) { | 173 | for (const Relocation& rel : patch.m_branch_to_module_relocations) { |
| 157 | ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), | 174 | ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), |
| 158 | rel); | 175 | rel); |
| 159 | } | 176 | } |
| 160 | 177 | ||
| 161 | // Rewrite PC constants and record post trampolines | 178 | // Rewrite PC constants and record post trampolines |
| 162 | for (const Relocation& rel : m_write_module_pc_relocations) { | 179 | for (const Relocation& rel : patch.m_write_module_pc_relocations) { |
| 163 | oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; | 180 | oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; |
| 164 | rc.dx(RebasePc(rel.module_offset)); | 181 | rc.dx(RebasePc(rel.module_offset)); |
| 165 | } | 182 | } |
| 166 | for (const Trampoline& rel : m_trampolines) { | 183 | for (const Trampoline& rel : patch.m_trampolines) { |
| 167 | out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); | 184 | out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); |
| 168 | } | 185 | } |
| 169 | 186 | ||
| 170 | // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. | 187 | // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. |
| 171 | // Convert to ordered to preserve this assumption. | 188 | // Convert to ordered to preserve this assumption. |
| 172 | for (const ModuleTextAddress i : m_exclusives) { | 189 | for (const ModuleTextAddress i : patch.m_exclusives) { |
| 173 | auto exclusive = Exclusive{text_words[i]}; | 190 | auto exclusive = Exclusive{text_words[i]}; |
| 174 | text_words[i] = exclusive.AsOrdered(); | 191 | text_words[i] = exclusive.AsOrdered(); |
| 175 | } | 192 | } |
| 176 | 193 | ||
| 177 | // Copy to program image | 194 | // Remove the patched module size from the total. This is done so total_program_size |
| 178 | if (this->mode == PatchMode::PreText) { | 195 | // always represents the distance from the currently patched module to the patch section. |
| 179 | std::memcpy(program_image.data(), m_patch_instructions.data(), | 196 | total_program_size -= image_size; |
| 180 | m_patch_instructions.size() * sizeof(u32)); | 197 | |
| 181 | } else { | 198 | // Only copy to the program image of the last module |
| 182 | program_image.resize(image_size + patch_size); | 199 | if (m_relocate_module_index == modules.size()) { |
| 183 | std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), | 200 | if (this->mode == PatchMode::PreText) { |
| 184 | m_patch_instructions.size() * sizeof(u32)); | 201 | ASSERT(image_size == total_program_size); |
| 202 | std::memcpy(program_image.data(), m_patch_instructions.data(), | ||
| 203 | m_patch_instructions.size() * sizeof(u32)); | ||
| 204 | } else { | ||
| 205 | program_image.resize(image_size + patch_size); | ||
| 206 | std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), | ||
| 207 | m_patch_instructions.size() * sizeof(u32)); | ||
| 208 | } | ||
| 209 | return true; | ||
| 185 | } | 210 | } |
| 211 | |||
| 212 | return false; | ||
| 186 | } | 213 | } |
| 187 | 214 | ||
| 188 | size_t Patcher::GetSectionSize() const noexcept { | 215 | size_t Patcher::GetSectionSize() const noexcept { |
| @@ -322,7 +349,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { | |||
| 322 | 349 | ||
| 323 | // Write the post-SVC trampoline address, which will jump back to the guest after restoring its | 350 | // Write the post-SVC trampoline address, which will jump back to the guest after restoring its |
| 324 | // state. | 351 | // state. |
| 325 | m_trampolines.push_back({c.offset(), module_dest}); | 352 | curr_patch->m_trampolines.push_back({c.offset(), module_dest}); |
| 326 | 353 | ||
| 327 | // Host called this location. Save the return address so we can | 354 | // Host called this location. Save the return address so we can |
| 328 | // unwind the stack properly when jumping back. | 355 | // unwind the stack properly when jumping back. |
diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h index c6d1608c1..a44f385e2 100644 --- a/src/core/arm/nce/patcher.h +++ b/src/core/arm/nce/patcher.h | |||
| @@ -31,9 +31,9 @@ public: | |||
| 31 | explicit Patcher(); | 31 | explicit Patcher(); |
| 32 | ~Patcher(); | 32 | ~Patcher(); |
| 33 | 33 | ||
| 34 | void PatchText(const Kernel::PhysicalMemory& program_image, | 34 | bool PatchText(const Kernel::PhysicalMemory& program_image, |
| 35 | const Kernel::CodeSet::Segment& code); | 35 | const Kernel::CodeSet::Segment& code); |
| 36 | void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, | 36 | bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, |
| 37 | Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); | 37 | Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); |
| 38 | size_t GetSectionSize() const noexcept; | 38 | size_t GetSectionSize() const noexcept; |
| 39 | 39 | ||
| @@ -61,16 +61,16 @@ private: | |||
| 61 | 61 | ||
| 62 | private: | 62 | private: |
| 63 | void BranchToPatch(uintptr_t module_dest) { | 63 | void BranchToPatch(uintptr_t module_dest) { |
| 64 | m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); | 64 | curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void BranchToModule(uintptr_t module_dest) { | 67 | void BranchToModule(uintptr_t module_dest) { |
| 68 | m_branch_to_module_relocations.push_back({c.offset(), module_dest}); | 68 | curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest}); |
| 69 | c.dw(0); | 69 | c.dw(0); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | void WriteModulePc(uintptr_t module_dest) { | 72 | void WriteModulePc(uintptr_t module_dest) { |
| 73 | m_write_module_pc_relocations.push_back({c.offset(), module_dest}); | 73 | curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest}); |
| 74 | c.dx(0); | 74 | c.dx(0); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| @@ -84,15 +84,22 @@ private: | |||
| 84 | uintptr_t module_offset; ///< Offset in bytes from the start of the text section. | 84 | uintptr_t module_offset; ///< Offset in bytes from the start of the text section. |
| 85 | }; | 85 | }; |
| 86 | 86 | ||
| 87 | struct ModulePatch { | ||
| 88 | std::vector<Trampoline> m_trampolines; | ||
| 89 | std::vector<Relocation> m_branch_to_patch_relocations{}; | ||
| 90 | std::vector<Relocation> m_branch_to_module_relocations{}; | ||
| 91 | std::vector<Relocation> m_write_module_pc_relocations{}; | ||
| 92 | std::vector<ModuleTextAddress> m_exclusives{}; | ||
| 93 | }; | ||
| 94 | |||
| 87 | oaknut::VectorCodeGenerator c; | 95 | oaknut::VectorCodeGenerator c; |
| 88 | std::vector<Trampoline> m_trampolines; | ||
| 89 | std::vector<Relocation> m_branch_to_patch_relocations{}; | ||
| 90 | std::vector<Relocation> m_branch_to_module_relocations{}; | ||
| 91 | std::vector<Relocation> m_write_module_pc_relocations{}; | ||
| 92 | std::vector<ModuleTextAddress> m_exclusives{}; | ||
| 93 | oaknut::Label m_save_context{}; | 96 | oaknut::Label m_save_context{}; |
| 94 | oaknut::Label m_load_context{}; | 97 | oaknut::Label m_load_context{}; |
| 95 | PatchMode mode{PatchMode::None}; | 98 | PatchMode mode{PatchMode::None}; |
| 99 | size_t total_program_size{}; | ||
| 100 | size_t m_relocate_module_index{}; | ||
| 101 | std::vector<ModulePatch> modules; | ||
| 102 | ModulePatch* curr_patch; | ||
| 96 | }; | 103 | }; |
| 97 | 104 | ||
| 98 | } // namespace Core::NCE | 105 | } // namespace Core::NCE |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index ae332a550..53735a225 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -1239,10 +1239,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { | |||
| 1239 | ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); | 1239 | ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); |
| 1240 | 1240 | ||
| 1241 | #ifdef HAS_NCE | 1241 | #ifdef HAS_NCE |
| 1242 | if (this->IsApplication() && Settings::IsNceEnabled()) { | 1242 | const auto& patch = code_set.PatchSegment(); |
| 1243 | if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) { | ||
| 1243 | auto& buffer = m_kernel.System().DeviceMemory().buffer; | 1244 | auto& buffer = m_kernel.System().DeviceMemory().buffer; |
| 1244 | const auto& code = code_set.CodeSegment(); | 1245 | const auto& code = code_set.CodeSegment(); |
| 1245 | const auto& patch = code_set.PatchSegment(); | ||
| 1246 | buffer.Protect(GetInteger(base_addr + code.addr), code.size, | 1246 | buffer.Protect(GetInteger(base_addr + code.addr), code.size, |
| 1247 | Common::MemoryPermission::Read | Common::MemoryPermission::Execute); | 1247 | Common::MemoryPermission::Read | Common::MemoryPermission::Execute); |
| 1248 | buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, | 1248 | buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 1e599e78b..9b75c660c 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -19,8 +19,54 @@ | |||
| 19 | #include "core/arm/nce/patcher.h" | 19 | #include "core/arm/nce/patcher.h" |
| 20 | #endif | 20 | #endif |
| 21 | 21 | ||
| 22 | #ifndef HAS_NCE | ||
| 23 | namespace Core::NCE { | ||
| 24 | class Patcher {}; | ||
| 25 | } // namespace Core::NCE | ||
| 26 | #endif | ||
| 27 | |||
| 22 | namespace Loader { | 28 | namespace Loader { |
| 23 | 29 | ||
| 30 | struct PatchCollection { | ||
| 31 | explicit PatchCollection(bool is_application_) : is_application{is_application_} { | ||
| 32 | module_patcher_indices.fill(-1); | ||
| 33 | patchers.emplace_back(); | ||
| 34 | } | ||
| 35 | |||
| 36 | std::vector<Core::NCE::Patcher>* GetPatchers() { | ||
| 37 | if (is_application && Settings::IsNceEnabled()) { | ||
| 38 | return &patchers; | ||
| 39 | } | ||
| 40 | return nullptr; | ||
| 41 | } | ||
| 42 | |||
| 43 | size_t GetTotalPatchSize() const { | ||
| 44 | size_t total_size{}; | ||
| 45 | #ifdef HAS_NCE | ||
| 46 | for (auto& patcher : patchers) { | ||
| 47 | total_size += patcher.GetSectionSize(); | ||
| 48 | } | ||
| 49 | #endif | ||
| 50 | return total_size; | ||
| 51 | } | ||
| 52 | |||
| 53 | void SaveIndex(size_t module) { | ||
| 54 | module_patcher_indices[module] = static_cast<s32>(patchers.size() - 1); | ||
| 55 | } | ||
| 56 | |||
| 57 | s32 GetIndex(size_t module) const { | ||
| 58 | return module_patcher_indices[module]; | ||
| 59 | } | ||
| 60 | |||
| 61 | s32 GetLastIndex() const { | ||
| 62 | return static_cast<s32>(patchers.size()) - 1; | ||
| 63 | } | ||
| 64 | |||
| 65 | bool is_application; | ||
| 66 | std::vector<Core::NCE::Patcher> patchers; | ||
| 67 | std::array<s32, 13> module_patcher_indices{}; | ||
| 68 | }; | ||
| 69 | |||
| 24 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, | 70 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, |
| 25 | bool override_update_) | 71 | bool override_update_) |
| 26 | : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { | 72 | : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { |
| @@ -142,18 +188,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 142 | std::size_t code_size{}; | 188 | std::size_t code_size{}; |
| 143 | 189 | ||
| 144 | // Define an nce patch context for each potential module. | 190 | // Define an nce patch context for each potential module. |
| 145 | #ifdef HAS_NCE | 191 | PatchCollection patch_ctx{is_application}; |
| 146 | std::array<Core::NCE::Patcher, 13> module_patchers; | ||
| 147 | #endif | ||
| 148 | |||
| 149 | const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { | ||
| 150 | #ifdef HAS_NCE | ||
| 151 | if (is_application && Settings::IsNceEnabled()) { | ||
| 152 | return &module_patchers[i]; | ||
| 153 | } | ||
| 154 | #endif | ||
| 155 | return nullptr; | ||
| 156 | }; | ||
| 157 | 192 | ||
| 158 | // Use the NSO module loader to figure out the code layout | 193 | // Use the NSO module loader to figure out the code layout |
| 159 | for (size_t i = 0; i < static_modules.size(); i++) { | 194 | for (size_t i = 0; i < static_modules.size(); i++) { |
| @@ -164,13 +199,14 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 164 | } | 199 | } |
| 165 | 200 | ||
| 166 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | 201 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 167 | const auto tentative_next_load_addr = | 202 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( |
| 168 | AppLoader_NSO::LoadModule(process, system, *module_file, code_size, | 203 | process, system, *module_file, code_size, should_pass_arguments, false, {}, |
| 169 | should_pass_arguments, false, {}, GetPatcher(i)); | 204 | patch_ctx.GetPatchers(), patch_ctx.GetLastIndex()); |
| 170 | if (!tentative_next_load_addr) { | 205 | if (!tentative_next_load_addr) { |
| 171 | return {ResultStatus::ErrorLoadingNSO, {}}; | 206 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 172 | } | 207 | } |
| 173 | 208 | ||
| 209 | patch_ctx.SaveIndex(i); | ||
| 174 | code_size = *tentative_next_load_addr; | 210 | code_size = *tentative_next_load_addr; |
| 175 | } | 211 | } |
| 176 | 212 | ||
| @@ -184,6 +220,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 184 | return 0; | 220 | return 0; |
| 185 | }(); | 221 | }(); |
| 186 | 222 | ||
| 223 | // Add patch size to the total module size | ||
| 224 | code_size += patch_ctx.GetTotalPatchSize(); | ||
| 225 | |||
| 187 | // Setup the process code layout | 226 | // Setup the process code layout |
| 188 | if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { | 227 | if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { |
| 189 | return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; | 228 | return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; |
| @@ -204,9 +243,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 204 | 243 | ||
| 205 | const VAddr load_addr{next_load_addr}; | 244 | const VAddr load_addr{next_load_addr}; |
| 206 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | 245 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 207 | const auto tentative_next_load_addr = | 246 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( |
| 208 | AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, | 247 | process, system, *module_file, load_addr, should_pass_arguments, true, pm, |
| 209 | should_pass_arguments, true, pm, GetPatcher(i)); | 248 | patch_ctx.GetPatchers(), patch_ctx.GetIndex(i)); |
| 210 | if (!tentative_next_load_addr) { | 249 | if (!tentative_next_load_addr) { |
| 211 | return {ResultStatus::ErrorLoadingNSO, {}}; | 250 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 212 | } | 251 | } |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index b053a0d14..583b7e927 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -77,7 +77,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 77 | const FileSys::VfsFile& nso_file, VAddr load_base, | 77 | const FileSys::VfsFile& nso_file, VAddr load_base, |
| 78 | bool should_pass_arguments, bool load_into_process, | 78 | bool should_pass_arguments, bool load_into_process, |
| 79 | std::optional<FileSys::PatchManager> pm, | 79 | std::optional<FileSys::PatchManager> pm, |
| 80 | Core::NCE::Patcher* patch) { | 80 | std::vector<Core::NCE::Patcher>* patches, |
| 81 | s32 patch_index) { | ||
| 81 | if (nso_file.GetSize() < sizeof(NSOHeader)) { | 82 | if (nso_file.GetSize() < sizeof(NSOHeader)) { |
| 82 | return std::nullopt; | 83 | return std::nullopt; |
| 83 | } | 84 | } |
| @@ -94,8 +95,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 94 | // Allocate some space at the beginning if we are patching in PreText mode. | 95 | // Allocate some space at the beginning if we are patching in PreText mode. |
| 95 | const size_t module_start = [&]() -> size_t { | 96 | const size_t module_start = [&]() -> size_t { |
| 96 | #ifdef HAS_NCE | 97 | #ifdef HAS_NCE |
| 97 | if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { | 98 | if (patches && load_into_process) { |
| 98 | return patch->GetSectionSize(); | 99 | auto* patch = &patches->operator[](patch_index); |
| 100 | if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { | ||
| 101 | return patch->GetSectionSize(); | ||
| 102 | } | ||
| 99 | } | 103 | } |
| 100 | #endif | 104 | #endif |
| 101 | return 0; | 105 | return 0; |
| @@ -160,27 +164,24 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 160 | #ifdef HAS_NCE | 164 | #ifdef HAS_NCE |
| 161 | // If we are computing the process code layout and using nce backend, patch. | 165 | // If we are computing the process code layout and using nce backend, patch. |
| 162 | const auto& code = codeset.CodeSegment(); | 166 | const auto& code = codeset.CodeSegment(); |
| 163 | if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { | 167 | auto* patch = patches ? &patches->operator[](patch_index) : nullptr; |
| 168 | if (patch && !load_into_process) { | ||
| 164 | // Patch SVCs and MRS calls in the guest code | 169 | // Patch SVCs and MRS calls in the guest code |
| 165 | patch->PatchText(program_image, code); | 170 | while (!patch->PatchText(program_image, code)) { |
| 166 | 171 | patch = &patches->emplace_back(); | |
| 167 | // Add patch section size to the module size. | 172 | } |
| 168 | image_size += static_cast<u32>(patch->GetSectionSize()); | ||
| 169 | } else if (patch) { | 173 | } else if (patch) { |
| 170 | // Relocate code patch and copy to the program_image. | 174 | // Relocate code patch and copy to the program_image. |
| 171 | patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); | 175 | if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) { |
| 172 | 176 | // Update patch section. | |
| 173 | // Update patch section. | 177 | auto& patch_segment = codeset.PatchSegment(); |
| 174 | auto& patch_segment = codeset.PatchSegment(); | 178 | patch_segment.addr = |
| 175 | patch_segment.addr = | 179 | patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; |
| 176 | patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; | 180 | patch_segment.size = static_cast<u32>(patch->GetSectionSize()); |
| 177 | patch_segment.size = static_cast<u32>(patch->GetSectionSize()); | ||
| 178 | |||
| 179 | // Add patch section size to the module size. In PreText mode image_size | ||
| 180 | // already contains the patch segment as part of module_start. | ||
| 181 | if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) { | ||
| 182 | image_size += patch_segment.size; | ||
| 183 | } | 181 | } |
| 182 | |||
| 183 | // Refresh image_size to take account the patch section if it was added by RelocateAndCopy | ||
| 184 | image_size = static_cast<u32>(program_image.size()); | ||
| 184 | } | 185 | } |
| 185 | #endif | 186 | #endif |
| 186 | 187 | ||
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 29b86ed4c..6356697e3 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -93,7 +93,8 @@ public: | |||
| 93 | const FileSys::VfsFile& nso_file, VAddr load_base, | 93 | const FileSys::VfsFile& nso_file, VAddr load_base, |
| 94 | bool should_pass_arguments, bool load_into_process, | 94 | bool should_pass_arguments, bool load_into_process, |
| 95 | std::optional<FileSys::PatchManager> pm = {}, | 95 | std::optional<FileSys::PatchManager> pm = {}, |
| 96 | Core::NCE::Patcher* patch = nullptr); | 96 | std::vector<Core::NCE::Patcher>* patches = nullptr, |
| 97 | s32 patch_index = -1); | ||
| 97 | 98 | ||
| 98 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | 99 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; |
| 99 | 100 | ||