summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/arm/nce/patcher.cpp83
-rw-r--r--src/core/arm/nce/patcher.h27
-rw-r--r--src/core/hle/kernel/k_process.cpp4
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp75
-rw-r--r--src/core/loader/nso.cpp41
-rw-r--r--src/core/loader/nso.h3
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;
22constexpr size_t MaxRelativeBranch = 128_MiB; 22constexpr size_t MaxRelativeBranch = 128_MiB;
23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); 23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
24 24
25Patcher::Patcher() : c(m_patch_instructions) {} 25Patcher::Patcher() : c(m_patch_instructions) {
26 26 // The first word of the patch section is always a branch to the first instruction of the
27Patcher::~Patcher() = default; 27 // module.
28 28 c.dw(0);
29void 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
39Patcher::~Patcher() = default;
40
41bool 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
106void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, 122bool 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
188size_t Patcher::GetSectionSize() const noexcept { 215size_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
62private: 62private:
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 068e71dff..8839ddbc2 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1233,10 +1233,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1233 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); 1233 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
1234 1234
1235#ifdef HAS_NCE 1235#ifdef HAS_NCE
1236 if (this->IsApplication() && Settings::IsNceEnabled()) { 1236 const auto& patch = code_set.PatchSegment();
1237 if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
1237 auto& buffer = m_kernel.System().DeviceMemory().buffer; 1238 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1238 const auto& code = code_set.CodeSegment(); 1239 const auto& code = code_set.CodeSegment();
1239 const auto& patch = code_set.PatchSegment();
1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size, 1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size,
1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute); 1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, 1242 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 c9f8707b7..b2173f697 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
23namespace Core::NCE {
24class Patcher {};
25} // namespace Core::NCE
26#endif
27
22namespace Loader { 28namespace Loader {
23 29
30struct 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
24AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, 70AppLoader_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