summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorliamwhite <liamwhite@users.noreply.github.com>2024-01-15 19:52:38 +0100
committerGitHub <noreply@github.com>2024-01-15 19:52:38 +0100
commit04f4eeaca2722b901a60dffd955aed993c95bd05 (patch)
treea8a16cfc59ac26bb8a56fa80d91bf311fa03810e /src
parentMerge pull request #12665 from german77/proof (diff)
parentcore: Support multiple modules per patcher (diff)
downloadyuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar
yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.gz
yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.bz2
yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.lz
yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.xz
yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.tar.zst
yuzu-04f4eeaca2722b901a60dffd955aed993c95bd05.zip
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;
constexpr size_t MaxRelativeBranch = 128_MiB;
constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
-Patcher::Patcher() : c(m_patch_instructions) {}
-
-Patcher::~Patcher() = default;
-
-void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
- const Kernel::CodeSet::Segment& code) {
- // Branch to the first instruction of the module.
- this->BranchToModule(0);
+Patcher::Patcher() : c(m_patch_instructions) {
+ // The first word of the patch section is always a branch to the first instruction of the
+ // module.
+ c.dw(0);
// Write save context helper function.
c.l(m_save_context);
@@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
// Write load context helper function.
c.l(m_load_context);
WriteLoadContext();
+}
+
+Patcher::~Patcher() = default;
+
+bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
+ const Kernel::CodeSet::Segment& code) {
+ // If we have patched modules but cannot reach the new module, then it needs its own patcher.
+ const size_t image_size = program_image.size();
+ if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) {
+ return false;
+ }
+
+ // Add a new module patch to our list
+ modules.emplace_back();
+ curr_patch = &modules.back();
+
+ // The first word of the patch section is always a branch to the first instruction of the
+ // module.
+ curr_patch->m_branch_to_module_relocations.push_back({0, 0});
// Retrieve text segment data.
const auto text = std::span{program_image}.subspan(code.offset, code.size);
@@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
}
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
- m_exclusives.push_back(i);
+ curr_patch->m_exclusives.push_back(i);
}
}
// Determine patching mode for the final relocation step
- const size_t image_size = program_image.size();
+ total_program_size += image_size;
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
+ return true;
}
-void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
+bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
const Kernel::CodeSet::Segment& code,
Kernel::PhysicalMemory& program_image,
EntryTrampolines* out_trampolines) {
@@ -120,7 +136,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
if (mode == PatchMode::PreText) {
rc.B(rel.patch_offset - patch_size - rel.module_offset);
} else {
- rc.B(image_size - rel.module_offset + rel.patch_offset);
+ rc.B(total_program_size - rel.module_offset + rel.patch_offset);
}
};
@@ -129,7 +145,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
if (mode == PatchMode::PreText) {
rc.B(patch_size - rel.patch_offset + rel.module_offset);
} else {
- rc.B(rel.module_offset - image_size - rel.patch_offset);
+ rc.B(rel.module_offset - total_program_size - rel.patch_offset);
}
};
@@ -137,7 +153,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
if (mode == PatchMode::PreText) {
return GetInteger(load_base) + patch_offset;
} else {
- return GetInteger(load_base) + image_size + patch_offset;
+ return GetInteger(load_base) + total_program_size + patch_offset;
}
};
@@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
};
// We are now ready to relocate!
- for (const Relocation& rel : m_branch_to_patch_relocations) {
+ auto& patch = modules[m_relocate_module_index++];
+ for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
}
- for (const Relocation& rel : m_branch_to_module_relocations) {
+ for (const Relocation& rel : patch.m_branch_to_module_relocations) {
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
rel);
}
// Rewrite PC constants and record post trampolines
- for (const Relocation& rel : m_write_module_pc_relocations) {
+ for (const Relocation& rel : patch.m_write_module_pc_relocations) {
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
rc.dx(RebasePc(rel.module_offset));
}
- for (const Trampoline& rel : m_trampolines) {
+ for (const Trampoline& rel : patch.m_trampolines) {
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
}
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
// Convert to ordered to preserve this assumption.
- for (const ModuleTextAddress i : m_exclusives) {
+ for (const ModuleTextAddress i : patch.m_exclusives) {
auto exclusive = Exclusive{text_words[i]};
text_words[i] = exclusive.AsOrdered();
}
- // Copy to program image
- if (this->mode == PatchMode::PreText) {
- std::memcpy(program_image.data(), m_patch_instructions.data(),
- m_patch_instructions.size() * sizeof(u32));
- } else {
- program_image.resize(image_size + patch_size);
- std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
- m_patch_instructions.size() * sizeof(u32));
+ // Remove the patched module size from the total. This is done so total_program_size
+ // always represents the distance from the currently patched module to the patch section.
+ total_program_size -= image_size;
+
+ // Only copy to the program image of the last module
+ if (m_relocate_module_index == modules.size()) {
+ if (this->mode == PatchMode::PreText) {
+ ASSERT(image_size == total_program_size);
+ std::memcpy(program_image.data(), m_patch_instructions.data(),
+ m_patch_instructions.size() * sizeof(u32));
+ } else {
+ program_image.resize(image_size + patch_size);
+ std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
+ m_patch_instructions.size() * sizeof(u32));
+ }
+ return true;
}
+
+ return false;
}
size_t Patcher::GetSectionSize() const noexcept {
@@ -322,7 +349,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
// Write the post-SVC trampoline address, which will jump back to the guest after restoring its
// state.
- m_trampolines.push_back({c.offset(), module_dest});
+ curr_patch->m_trampolines.push_back({c.offset(), module_dest});
// Host called this location. Save the return address so we can
// 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:
explicit Patcher();
~Patcher();
- void PatchText(const Kernel::PhysicalMemory& program_image,
+ bool PatchText(const Kernel::PhysicalMemory& program_image,
const Kernel::CodeSet::Segment& code);
- void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
+ bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
size_t GetSectionSize() const noexcept;
@@ -61,16 +61,16 @@ private:
private:
void BranchToPatch(uintptr_t module_dest) {
- m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
+ curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
}
void BranchToModule(uintptr_t module_dest) {
- m_branch_to_module_relocations.push_back({c.offset(), module_dest});
+ curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest});
c.dw(0);
}
void WriteModulePc(uintptr_t module_dest) {
- m_write_module_pc_relocations.push_back({c.offset(), module_dest});
+ curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest});
c.dx(0);
}
@@ -84,15 +84,22 @@ private:
uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
};
+ struct ModulePatch {
+ std::vector<Trampoline> m_trampolines;
+ std::vector<Relocation> m_branch_to_patch_relocations{};
+ std::vector<Relocation> m_branch_to_module_relocations{};
+ std::vector<Relocation> m_write_module_pc_relocations{};
+ std::vector<ModuleTextAddress> m_exclusives{};
+ };
+
oaknut::VectorCodeGenerator c;
- std::vector<Trampoline> m_trampolines;
- std::vector<Relocation> m_branch_to_patch_relocations{};
- std::vector<Relocation> m_branch_to_module_relocations{};
- std::vector<Relocation> m_write_module_pc_relocations{};
- std::vector<ModuleTextAddress> m_exclusives{};
oaknut::Label m_save_context{};
oaknut::Label m_load_context{};
PatchMode mode{PatchMode::None};
+ size_t total_program_size{};
+ size_t m_relocate_module_index{};
+ std::vector<ModulePatch> modules;
+ ModulePatch* curr_patch;
};
} // 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) {
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
#ifdef HAS_NCE
- if (this->IsApplication() && Settings::IsNceEnabled()) {
+ const auto& patch = code_set.PatchSegment();
+ if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
auto& buffer = m_kernel.System().DeviceMemory().buffer;
const auto& code = code_set.CodeSegment();
- const auto& patch = code_set.PatchSegment();
buffer.Protect(GetInteger(base_addr + code.addr), code.size,
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
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 @@
#include "core/arm/nce/patcher.h"
#endif
+#ifndef HAS_NCE
+namespace Core::NCE {
+class Patcher {};
+} // namespace Core::NCE
+#endif
+
namespace Loader {
+struct PatchCollection {
+ explicit PatchCollection(bool is_application_) : is_application{is_application_} {
+ module_patcher_indices.fill(-1);
+ patchers.emplace_back();
+ }
+
+ std::vector<Core::NCE::Patcher>* GetPatchers() {
+ if (is_application && Settings::IsNceEnabled()) {
+ return &patchers;
+ }
+ return nullptr;
+ }
+
+ size_t GetTotalPatchSize() const {
+ size_t total_size{};
+#ifdef HAS_NCE
+ for (auto& patcher : patchers) {
+ total_size += patcher.GetSectionSize();
+ }
+#endif
+ return total_size;
+ }
+
+ void SaveIndex(size_t module) {
+ module_patcher_indices[module] = static_cast<s32>(patchers.size() - 1);
+ }
+
+ s32 GetIndex(size_t module) const {
+ return module_patcher_indices[module];
+ }
+
+ s32 GetLastIndex() const {
+ return static_cast<s32>(patchers.size()) - 1;
+ }
+
+ bool is_application;
+ std::vector<Core::NCE::Patcher> patchers;
+ std::array<s32, 13> module_patcher_indices{};
+};
+
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
bool override_update_)
: AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) {
@@ -142,18 +188,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
std::size_t code_size{};
// Define an nce patch context for each potential module.
-#ifdef HAS_NCE
- std::array<Core::NCE::Patcher, 13> module_patchers;
-#endif
-
- const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
-#ifdef HAS_NCE
- if (is_application && Settings::IsNceEnabled()) {
- return &module_patchers[i];
- }
-#endif
- return nullptr;
- };
+ PatchCollection patch_ctx{is_application};
// Use the NSO module loader to figure out the code layout
for (size_t i = 0; i < static_modules.size(); i++) {
@@ -164,13 +199,14 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
- const auto tentative_next_load_addr =
- AppLoader_NSO::LoadModule(process, system, *module_file, code_size,
- should_pass_arguments, false, {}, GetPatcher(i));
+ const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
+ process, system, *module_file, code_size, should_pass_arguments, false, {},
+ patch_ctx.GetPatchers(), patch_ctx.GetLastIndex());
if (!tentative_next_load_addr) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
+ patch_ctx.SaveIndex(i);
code_size = *tentative_next_load_addr;
}
@@ -184,6 +220,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
return 0;
}();
+ // Add patch size to the total module size
+ code_size += patch_ctx.GetTotalPatchSize();
+
// Setup the process code layout
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
@@ -204,9 +243,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
const VAddr load_addr{next_load_addr};
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
- const auto tentative_next_load_addr =
- AppLoader_NSO::LoadModule(process, system, *module_file, load_addr,
- should_pass_arguments, true, pm, GetPatcher(i));
+ const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
+ process, system, *module_file, load_addr, should_pass_arguments, true, pm,
+ patch_ctx.GetPatchers(), patch_ctx.GetIndex(i));
if (!tentative_next_load_addr) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
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::
const FileSys::VfsFile& nso_file, VAddr load_base,
bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm,
- Core::NCE::Patcher* patch) {
+ std::vector<Core::NCE::Patcher>* patches,
+ s32 patch_index) {
if (nso_file.GetSize() < sizeof(NSOHeader)) {
return std::nullopt;
}
@@ -94,8 +95,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
// Allocate some space at the beginning if we are patching in PreText mode.
const size_t module_start = [&]() -> size_t {
#ifdef HAS_NCE
- if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
- return patch->GetSectionSize();
+ if (patches && load_into_process) {
+ auto* patch = &patches->operator[](patch_index);
+ if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
+ return patch->GetSectionSize();
+ }
}
#endif
return 0;
@@ -160,27 +164,24 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
#ifdef HAS_NCE
// If we are computing the process code layout and using nce backend, patch.
const auto& code = codeset.CodeSegment();
- if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) {
+ auto* patch = patches ? &patches->operator[](patch_index) : nullptr;
+ if (patch && !load_into_process) {
// Patch SVCs and MRS calls in the guest code
- patch->PatchText(program_image, code);
-
- // Add patch section size to the module size.
- image_size += static_cast<u32>(patch->GetSectionSize());
+ while (!patch->PatchText(program_image, code)) {
+ patch = &patches->emplace_back();
+ }
} else if (patch) {
// Relocate code patch and copy to the program_image.
- patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers());
-
- // Update patch section.
- auto& patch_segment = codeset.PatchSegment();
- patch_segment.addr =
- patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
- patch_segment.size = static_cast<u32>(patch->GetSectionSize());
-
- // Add patch section size to the module size. In PreText mode image_size
- // already contains the patch segment as part of module_start.
- if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) {
- image_size += patch_segment.size;
+ if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) {
+ // Update patch section.
+ auto& patch_segment = codeset.PatchSegment();
+ patch_segment.addr =
+ patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
+ patch_segment.size = static_cast<u32>(patch->GetSectionSize());
}
+
+ // Refresh image_size to take account the patch section if it was added by RelocateAndCopy
+ image_size = static_cast<u32>(program_image.size());
}
#endif
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:
const FileSys::VfsFile& nso_file, VAddr load_base,
bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm = {},
- Core::NCE::Patcher* patch = nullptr);
+ std::vector<Core::NCE::Patcher>* patches = nullptr,
+ s32 patch_index = -1);
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;