From 3f78512c83fca84a36bb450a10043b368830f8b0 Mon Sep 17 00:00:00 2001 From: xbzk Date: Wed, 30 Sep 2026 00:58:29 +0200 Subject: [PATCH] [ro] add TLS patching for runtime NROs (#4492) - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- Aimed to fix Biomutant which stucks in a bootloop due to guest being unable to reach correct thread local storage (TLS) This fixes runtime NRO loading by properly patching the guest TLS (TPIDR_EL0) path. Steps: -Read the loaded NRO image into a temporary buffer. -Run the NCE patcher on it. -Produce a patch section / relocated code. -Map that patch section as executable memory in the target process. Game still has intermittent visual issues which can be solved with GPU level accurate, but that causes a huge performance drop in exchange. Someone else will resume this one. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4492 Reviewed-by: CamilleLaVey Reviewed-by: lizzie --- src/core/hle/service/ro/ro.cpp | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index 13de9f96c8..b847811fe5 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -16,6 +16,10 @@ #include "core/hle/service/ro/ro_types.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" +#ifdef HAS_NCE +#include "core/arm/nce/patcher.h" +#include "core/hle/kernel/k_shared_memory.h" +#endif namespace Service::RO { @@ -386,7 +390,7 @@ public: R_SUCCEED(); } - Result MapManualLoadModuleMemory(u64* out_address, size_t context_id, u64 nro_address, + Result MapManualLoadModuleMemory(Kernel::KernelCore& kernel, u64* out_address, size_t context_id, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { // Get context. ProcessContext* context = this->GetContextById(context_id); @@ -421,7 +425,31 @@ public: R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size)); +#ifdef HAS_NCE + if (Settings::IsNceEnabled()) { + auto* process = context->GetProcess(); + auto& memory = process->GetMemory(); + std::vector image(total_size); + memory.ReadBlock(nro_info->base_address, image.data(), rx_size); + + Kernel::CodeSet::Segment code{.size = static_cast(rx_size)}; + Core::NCE::Patcher patch; + patch.PatchText(image, code); + patch.RelocateAndCopy(nro_info->base_address, code, image, nullptr); + + const u64 patch_address = nro_info->base_address + total_size; + const size_t patch_size = patch.GetSectionSize(); + constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute; + + auto* patch_memory = Kernel::KSharedMemory::Create(kernel); + R_TRY(patch_memory->Initialize(kernel, kernel.System().DeviceMemory(), process, permission, permission, patch_size)); + std::memcpy(patch_memory->GetPointer(), image.data() + total_size, patch_size); + R_TRY(process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size)); + R_TRY(patch_memory->Map(*process, patch_address, patch_size, permission)); + memory.WriteBlock(nro_info->base_address, image.data(), rx_size); + } +#endif // Set NRO perms. R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size)); @@ -531,7 +559,7 @@ public: Result MapManualLoadModuleMemory(Out out_load_address, ClientProcessId client_pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); - R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, + R_RETURN(m_ro->MapManualLoadModuleMemory(system.Kernel(), out_load_address.Get(), m_context_id, nro_address, nro_size, bss_address, bss_size)); }