From af909f6b99c4b38967c3018d075df0564cc4ab0e Mon Sep 17 00:00:00 2001 From: bdm Date: Wed, 1 Jul 2026 07:43:27 +0800 Subject: [PATCH] feat: enhance NSO module relocation logging and error handling --- src/core/arm/nce/patcher.cpp | 52 +++++++++++++++++++++++++++++++++--- src/core/loader/nso.cpp | 32 +++++++++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index 8ddfaebf10..9789c208dd 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -4,6 +4,7 @@ #include "common/arm64/native_clock.h" #include "common/bit_cast.h" #include "common/literals.h" +#include "common/logging.h" #include "core/arm/nce/arm_nce.h" #include "core/arm/nce/guest_context.h" #include "core/arm/nce/instructions.h" @@ -20,6 +21,9 @@ using namespace oaknut::util; using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; constexpr size_t MaxRelativeBranch = 128_MiB; +constexpr ptrdiff_t MinRelativeBranchOffset = -static_cast(MaxRelativeBranch); +constexpr ptrdiff_t MaxRelativeBranchOffset = + static_cast(MaxRelativeBranch) - sizeof(u32); constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); Patcher::Patcher() : c(m_patch_instructions) { @@ -131,22 +135,54 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const auto text_words = std::span{reinterpret_cast(text.data()), text.size() / sizeof(u32)}; + const auto IsValidBranchOffset = [](ptrdiff_t offset) { + return offset >= MinRelativeBranchOffset && offset <= MaxRelativeBranchOffset && + offset % sizeof(u32) == 0; + }; + + const auto LogBranchRelocation = [&](const char* kind, ptrdiff_t branch_offset, + const Relocation& rel) { + if (IsValidBranchOffset(branch_offset)) { + return; + } + + LOG_CRITICAL(Core_ARM, + "NCE branch relocation is outside AArch64 B range: kind={}, mode={}, " + "relocate_module={}, modules={}, branch_offset={:#x}, valid_range=[{:#x}, " + "{:#x}], patch_offset={:#x}, module_offset={:#x}, patch_size={:#x}, " + "image_size={:#x}, total_program_size={:#x}, load_base={:#x}", + kind, mode, m_relocate_module_index, modules.size(), branch_offset, + MinRelativeBranchOffset, MaxRelativeBranchOffset, rel.patch_offset, + rel.module_offset, patch_size, image_size, total_program_size, + GetInteger(load_base)); + }; + const auto ApplyBranchToPatchRelocation = [&](u32* target, const Relocation& rel) { oaknut::CodeGenerator rc{target}; + ptrdiff_t branch_offset; if (mode == PatchMode::PreText) { - rc.B(rel.patch_offset - patch_size - rel.module_offset); + branch_offset = rel.patch_offset - static_cast(patch_size) - + static_cast(rel.module_offset); } else { - rc.B(total_program_size - rel.module_offset + rel.patch_offset); + branch_offset = static_cast(total_program_size) - + static_cast(rel.module_offset) + rel.patch_offset; } + LogBranchRelocation("module_to_patch", branch_offset, rel); + rc.B(branch_offset); }; const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) { oaknut::CodeGenerator rc{target}; + ptrdiff_t branch_offset; if (mode == PatchMode::PreText) { - rc.B(patch_size - rel.patch_offset + rel.module_offset); + branch_offset = static_cast(patch_size) - rel.patch_offset + + static_cast(rel.module_offset); } else { - rc.B(rel.module_offset - total_program_size - rel.patch_offset); + branch_offset = static_cast(rel.module_offset) - + static_cast(total_program_size) - rel.patch_offset; } + LogBranchRelocation("patch_to_module", branch_offset, rel); + rc.B(branch_offset); }; const auto RebasePatch = [&](ptrdiff_t patch_offset) { @@ -198,6 +234,14 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, // Only copy to the program image of the last module if (m_relocate_module_index == modules.size()) { if (this->mode == PatchMode::PreText) { + if (image_size != total_program_size) { + LOG_CRITICAL(Core_ARM, + "NCE PreText final copy size mismatch: image_size={:#x}, " + "total_program_size={:#x}, patch_size={:#x}, relocate_module={}, " + "modules={}, load_base={:#x}", + image_size, total_program_size, patch_size, m_relocate_module_index, + modules.size(), GetInteger(load_base)); + } ASSERT(image_size == total_program_size); std::memcpy(program_image.data(), m_patch_instructions.data(), m_patch_instructions.size() * sizeof(u32)); diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index fc82561ac1..c716d130c3 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "common/common_funcs.h" @@ -172,7 +173,36 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } } else if (patch) { // Relocate code patch and copy to the program_image. - if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) { + const auto build_id = Common::HexToString(nso_header.build_id); + LOG_INFO(Loader, + "NCE relocating NSO module: name={}, build_id={}, patch_index={}, mode={}, " + "load_base={:#x}, image_size={:#x}, code_offset={:#x}, code_size={:#x}, " + "patch_section_size={:#x}", + name, build_id, patch_index, patch->GetPatchMode(), load_base, + program_image.size(), code.offset, code.size, patch->GetSectionSize()); + + bool copied_patch_section; + try { + copied_patch_section = + patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); + } catch (const std::exception& ex) { + LOG_CRITICAL(Loader, + "NCE failed while relocating NSO module: name={}, build_id={}, " + "patch_index={}, mode={}, load_base={:#x}, image_size={:#x}, " + "code_offset={:#x}, code_size={:#x}, patch_section_size={:#x}, " + "exception={}", + name, build_id, patch_index, patch->GetPatchMode(), load_base, + program_image.size(), code.offset, code.size, patch->GetSectionSize(), + ex.what()); + throw; + } + + LOG_INFO(Loader, + "NCE relocated NSO module: name={}, build_id={}, patch_index={}, " + "copied_patch_section={}, final_image_size={:#x}", + name, build_id, patch_index, copied_patch_section, program_image.size()); + + if (copied_patch_section) { // Update patch section. auto& patch_segment = codeset.PatchSegment(); patch_segment.addr =