Merge NCE relocate diagnostics for device testing

This commit is contained in:
bdm
2026-07-01 17:27:13 +08:00
2 changed files with 79 additions and 5 deletions
+48 -4
View File
@@ -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<ptrdiff_t>(MaxRelativeBranch);
constexpr ptrdiff_t MaxRelativeBranchOffset =
static_cast<ptrdiff_t>(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<u32>{reinterpret_cast<u32*>(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<ptrdiff_t>(patch_size) -
static_cast<ptrdiff_t>(rel.module_offset);
} else {
rc.B(total_program_size - rel.module_offset + rel.patch_offset);
branch_offset = static_cast<ptrdiff_t>(total_program_size) -
static_cast<ptrdiff_t>(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<ptrdiff_t>(patch_size) - rel.patch_offset +
static_cast<ptrdiff_t>(rel.module_offset);
} else {
rc.B(rel.module_offset - total_program_size - rel.patch_offset);
branch_offset = static_cast<ptrdiff_t>(rel.module_offset) -
static_cast<ptrdiff_t>(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));
+31 -1
View File
@@ -3,6 +3,7 @@
#include <cinttypes>
#include <cstring>
#include <exception>
#include <vector>
#include "common/common_funcs.h"
@@ -172,7 +173,36 @@ std::optional<VAddr> 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 =