From 06a08de68a95506a9cbe4633c58f6bdb5ded0c70 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 28 Feb 2026 01:06:33 +0100 Subject: [PATCH] [core/loader] prevent program_image reallocations in NSO+KIP loading methods (#3639) also changes some methods to std::span<> as well, but mainly std::vector<> in the NSO/KIP loading stuff is not needed to be memcpy'ed and memmove'd around this should save a marginal amount of loading time (RDR1) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3639 Reviewed-by: DraVee Reviewed-by: Maufeat Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/CMakeLists.txt | 1 - src/core/arm/nce/patcher.cpp | 9 +- src/core/arm/nce/patcher.h | 7 +- src/core/file_sys/kernel_executable.cpp | 9 +- src/core/file_sys/kernel_executable.h | 10 +- src/core/hle/kernel/code_set.h | 5 +- src/core/hle/kernel/physical_memory.h | 23 ----- .../hle/service/am/frontend/applet_error.cpp | 5 +- .../am/frontend/applet_software_keyboard.cpp | 19 ++-- .../am/frontend/applet_software_keyboard.h | 19 ++-- .../am/frontend/applet_web_browser.cpp | 13 ++- src/core/hle/service/lm/lm.cpp | 10 +- .../service/ns/platform_service_manager.cpp | 3 +- src/core/hle/service/ro/ro.cpp | 28 +++--- src/core/hle/service/ro/ro_types.h | 51 +--------- src/core/loader/kip.cpp | 19 +--- src/core/loader/nro.cpp | 4 +- src/core/loader/nso.cpp | 99 ++++++++----------- 18 files changed, 122 insertions(+), 212 deletions(-) delete mode 100644 src/core/hle/kernel/physical_memory.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b3071d94b2..08d6e73688 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -342,7 +342,6 @@ add_library(core STATIC hle/kernel/message_buffer.h hle/kernel/physical_core.cpp hle/kernel/physical_core.h - hle/kernel/physical_memory.h hle/kernel/slab_helpers.h hle/kernel/svc.cpp hle/kernel/svc.h diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index bd8e4fd7fb..ea77166645 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -4,6 +4,7 @@ #include #include #include "common/arm64/native_clock.h" +#include "common/alignment.h" #include "common/literals.h" #include "core/arm/nce/arm_nce.h" #include "core/arm/nce/guest_context.h" @@ -46,8 +47,7 @@ Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_pre) { Patcher::~Patcher() = default; -bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image, - const Kernel::CodeSet::Segment& code) { +bool Patcher::PatchText(std::span 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(); @@ -175,10 +175,7 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image, return true; } -bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, - const Kernel::CodeSet::Segment& code, - Kernel::PhysicalMemory& program_image, - EntryTrampolines* out_trampolines) { +bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector& program_image, EntryTrampolines* out_trampolines) { const size_t patch_size = GetSectionSize(); const size_t pre_patch_size = GetPreSectionSize(); diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h index 303cf68ec6..499c98c901 100644 --- a/src/core/arm/nce/patcher.h +++ b/src/core/arm/nce/patcher.h @@ -14,7 +14,6 @@ #include "common/settings.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_typed_address.h" -#include "core/hle/kernel/physical_memory.h" #include using ModuleID = std::array; // NSO build ID struct PatchCacheKey { @@ -56,10 +55,8 @@ public: } explicit Patcher(); ~Patcher(); - bool PatchText(const Kernel::PhysicalMemory& program_image, - const Kernel::CodeSet::Segment& code); - bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, - Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); + bool PatchText(std::span program_image, const Kernel::CodeSet::Segment& code); + bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector& program_image, EntryTrampolines* out_trampolines); size_t GetSectionSize() const noexcept; size_t GetPreSectionSize() const noexcept; diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index b84492d30a..dea05f840f 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -160,15 +163,15 @@ u32 KIP::GetMainThreadCpuCore() const { return header.default_core; } -const std::vector& KIP::GetTextSection() const { +std::span KIP::GetTextSection() const { return decompressed_sections[0]; } -const std::vector& KIP::GetRODataSection() const { +std::span KIP::GetRODataSection() const { return decompressed_sections[1]; } -const std::vector& KIP::GetDataSection() const { +std::span KIP::GetDataSection() const { return decompressed_sections[2]; } diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index 928ba2d99f..ac4b1acaf4 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include +#include #include #include @@ -70,9 +74,9 @@ public: u32 GetMainThreadStackSize() const; u32 GetMainThreadCpuCore() const; - const std::vector& GetTextSection() const; - const std::vector& GetRODataSection() const; - const std::vector& GetDataSection() const; + std::span GetTextSection() const; + std::span GetRODataSection() const; + std::span GetDataSection() const; u32 GetTextOffset() const; u32 GetRODataOffset() const; diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h index 1416fc52b1..88bc7f5e66 100644 --- a/src/core/hle/kernel/code_set.h +++ b/src/core/hle/kernel/code_set.h @@ -7,9 +7,9 @@ #pragma once #include +#include #include "core/hle/kernel/k_typed_address.h" -#include "core/hle/kernel/physical_memory.h" namespace Kernel { @@ -97,8 +97,7 @@ struct CodeSet final { #endif /// The overall data that backs this code set. - Kernel::PhysicalMemory memory; - + std::vector memory; /// The segments that comprise this code set. std::array segments; diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h deleted file mode 100644 index 253fa45636..0000000000 --- a/src/core/hle/kernel/physical_memory.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/alignment.h" - -namespace Kernel { - -// This encapsulation serves 2 purposes: -// - First, to encapsulate host physical memory under a single type and set an -// standard for managing it. -// - Second to ensure all host backing memory used is aligned to 256 bytes due -// to strict alignment restrictions on GPU memory. - -using PhysicalMemoryVector = std::vector>; -class PhysicalMemory final : public PhysicalMemoryVector { - using PhysicalMemoryVector::PhysicalMemoryVector; -}; - -} // namespace Kernel diff --git a/src/core/hle/service/am/frontend/applet_error.cpp b/src/core/hle/service/am/frontend/applet_error.cpp index 34ec7013b5..1c530aa324 100644 --- a/src/core/hle/service/am/frontend/applet_error.cpp +++ b/src/core/hle/service/am/frontend/applet_error.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -93,7 +96,7 @@ union Error::ErrorArguments { namespace { template -void CopyArgumentData(const std::vector& data, T& variable) { +void CopyArgumentData(std::span data, T& variable) { ASSERT(data.size() >= sizeof(T)); std::memcpy(&variable, data.data(), sizeof(T)); } diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp index d1bc030182..fe07d72912 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -775,7 +778,7 @@ Result SoftwareKeyboard::RequestExit() { // Inline Software Keyboard Requests -void SoftwareKeyboard::RequestFinalize(const std::vector& request_data) { +void SoftwareKeyboard::RequestFinalize(std::span request_data) { LOG_DEBUG(Service_AM, "Processing Request: Finalize"); ChangeState(SwkbdState::NotInitialized); @@ -783,17 +786,17 @@ void SoftwareKeyboard::RequestFinalize(const std::vector& request_data) { ExitKeyboard(); } -void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector& request_data) { +void SoftwareKeyboard::RequestSetUserWordInfo(std::span request_data) { LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); ReplyReleasedUserWordInfo(); } -void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector& request_data) { +void SoftwareKeyboard::RequestSetCustomizeDic(std::span request_data) { LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); } -void SoftwareKeyboard::RequestCalc(const std::vector& request_data) { +void SoftwareKeyboard::RequestCalc(std::span request_data) { LOG_DEBUG(Service_AM, "Processing Request: Calc"); ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon)); @@ -930,17 +933,17 @@ void SoftwareKeyboard::RequestCalcNew() { } } -void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector& request_data) { +void SoftwareKeyboard::RequestSetCustomizedDictionaries(std::span request_data) { LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); } -void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector& request_data) { +void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(std::span request_data) { LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); ReplyUnsetCustomizedDictionaries(); } -void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector& request_data) { +void SoftwareKeyboard::RequestSetChangedStringV2Flag(std::span request_data) { LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); @@ -948,7 +951,7 @@ void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector& requ std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); } -void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector& request_data) { +void SoftwareKeyboard::RequestSetMovedCursorV2Flag(std::span request_data) { LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h index 2a7d01b965..a3e4150080 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard.h +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -124,16 +127,16 @@ private: // Inline Software Keyboard Requests - void RequestFinalize(const std::vector& request_data); - void RequestSetUserWordInfo(const std::vector& request_data); - void RequestSetCustomizeDic(const std::vector& request_data); - void RequestCalc(const std::vector& request_data); + void RequestFinalize(std::span request_data); + void RequestSetUserWordInfo(std::span request_data); + void RequestSetCustomizeDic(std::span request_data); + void RequestCalc(std::span request_data); void RequestCalcOld(); void RequestCalcNew(); - void RequestSetCustomizedDictionaries(const std::vector& request_data); - void RequestUnsetCustomizedDictionaries(const std::vector& request_data); - void RequestSetChangedStringV2Flag(const std::vector& request_data); - void RequestSetMovedCursorV2Flag(const std::vector& request_data); + void RequestSetCustomizedDictionaries(std::span request_data); + void RequestUnsetCustomizedDictionaries(std::span request_data); + void RequestSetChangedStringV2Flag(std::span request_data); + void RequestSetMovedCursorV2Flag(std::span request_data); // Inline Software Keyboard Replies diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp index 206f2fd495..98e3b9c66d 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.cpp +++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -34,22 +34,21 @@ namespace Service::AM::Frontend { namespace { template -void ParseRawValue(T& value, const std::vector& data) { +void ParseRawValue(T& value, std::span data) { static_assert(std::is_trivially_copyable_v, "It's undefined behavior to use memcpy with non-trivially copyable objects"); std::memcpy(&value, data.data(), data.size()); } template -T ParseRawValue(const std::vector& data) { +T ParseRawValue(std::span data) { T value; ParseRawValue(value, data); return value; } -std::string ParseStringValue(const std::vector& data) { - return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast(data.data()), - data.size()); +std::string ParseStringValue(std::span data) { + return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast(data.data()), data.size()); } std::string GetMainURL(const std::string& url) { @@ -72,7 +71,7 @@ std::string ResolveURL(const std::string& url) { return url.substr(0, index) + "lp1" + url.substr(index + 1); } -WebArgInputTLVMap ReadWebArgs(const std::vector& web_arg, WebArgHeader& web_arg_header) { +WebArgInputTLVMap ReadWebArgs(std::span web_arg, WebArgHeader& web_arg_header) { std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); if (web_arg.size() == sizeof(WebArgHeader)) { diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 2fda8a3b90..0ff5ac0440 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -164,7 +164,7 @@ private: rb.Push(ResultSuccess); } - u64 ReadLeb128(const std::vector& data, std::size_t& offset) { + u64 ReadLeb128(std::span data, std::size_t& offset) { u64 result{}; u32 shift{}; @@ -180,7 +180,7 @@ private: return result; } - std::optional ReadString(const std::vector& data, std::size_t& offset, + std::optional ReadString(std::span data, std::size_t& offset, std::size_t length) { if (length == 0) { return std::nullopt; @@ -193,7 +193,7 @@ private: return output; } - u32_le ReadAsU32(const std::vector& data, std::size_t& offset, std::size_t length) { + u32_le ReadAsU32(std::span data, std::size_t& offset, std::size_t length) { ASSERT(length == sizeof(u32)); u32_le output{}; std::memcpy(&output, data.data() + offset, sizeof(u32)); @@ -201,7 +201,7 @@ private: return output; } - u64_le ReadAsU64(const std::vector& data, std::size_t& offset, std::size_t length) { + u64_le ReadAsU64(std::span data, std::size_t& offset, std::size_t length) { ASSERT(length == sizeof(u64)); u64_le output{}; std::memcpy(&output, data.data() + offset, sizeof(u64)); @@ -209,7 +209,7 @@ private: return output; } - void ParseLog(const LogPacketHeaderEntry entry, const std::vector& log_data) { + void ParseLog(const LogPacketHeaderEntry entry, std::span log_data) { // Possible entries std::optional text_log; std::optional line_number; diff --git a/src/core/hle/service/ns/platform_service_manager.cpp b/src/core/hle/service/ns/platform_service_manager.cpp index ec9f64945d..e91e6451a8 100644 --- a/src/core/hle/service/ns/platform_service_manager.cpp +++ b/src/core/hle/service/ns/platform_service_manager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -21,7 +21,6 @@ #include "core/file_sys/system_archive/system_archive.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/physical_memory.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/platform_service_manager.h" diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index 041c0dbd87..cd50e69ce1 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -176,11 +176,9 @@ struct ProcessContext { // Calculate hash. Sha256Hash hash; { - const u64 size = nro_header->GetSize(); - + const u64 size = nro_header->m_size; std::vector nro_data(size); m_process->GetMemory().ReadBlock(base_address, nro_data.data(), size); - u32 hash_len = 0; EVP_Digest(nro_data.data(), nro_data.size(), hash.data(), &hash_len, EVP_sha256(), nullptr); } @@ -204,9 +202,7 @@ struct ProcessContext { R_THROW(RO::ResultNotAuthorized); } - Result ValidateNro(ModuleId* out_module_id, u64* out_rx_size, u64* out_ro_size, - u64* out_rw_size, u64 base_address, u64 expected_nro_size, - u64 expected_bss_size) { + Result ValidateNro(ModuleId* out_module_id, u64* out_rx_size, u64* out_ro_size, u64* out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) { // Ensure we have a process to work on. R_UNLESS(m_process != nullptr, RO::ResultInvalidProcess); @@ -215,17 +211,17 @@ struct ProcessContext { m_process->GetMemory().ReadBlock(base_address, std::addressof(header), sizeof(header)); // Validate header. - R_UNLESS(header.IsMagicValid(), RO::ResultInvalidNro); + R_UNLESS(header.m_magic == NRO_HEADER_MAGIC, RO::ResultInvalidNro); // Read sizes from header. - const u64 nro_size = header.GetSize(); - const u64 text_ofs = header.GetTextOffset(); - const u64 text_size = header.GetTextSize(); - const u64 ro_ofs = header.GetRoOffset(); - const u64 ro_size = header.GetRoSize(); - const u64 rw_ofs = header.GetRwOffset(); - const u64 rw_size = header.GetRwSize(); - const u64 bss_size = header.GetBssSize(); + const u64 nro_size = header.m_size; + const u64 text_ofs = header.m_text_offset; + const u64 text_size = header.m_text_size; + const u64 ro_ofs = header.m_ro_offset; + const u64 ro_size = header.m_ro_size; + const u64 rw_ofs = header.m_rw_offset; + const u64 rw_size = header.m_rw_size; + const u64 bss_size = header.m_bss_size; // Validate sizes meet expected. R_UNLESS(nro_size == expected_nro_size, RO::ResultInvalidNro); @@ -251,7 +247,7 @@ struct ProcessContext { R_TRY(this->ValidateHasNroHash(base_address, std::addressof(header))); // Check if NRO has already been loaded. - const ModuleId* module_id = header.GetModuleId(); + const ModuleId* module_id = std::addressof(header.m_module_id); R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), RO::ResultAlreadyLoaded); // Apply patches to NRO. diff --git a/src/core/hle/service/ro/ro_types.h b/src/core/hle/service/ro/ro_types.h index 624d52ee5f..50b424395a 100644 --- a/src/core/hle/service/ro/ro_types.h +++ b/src/core/hle/service/ro/ro_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -112,52 +115,8 @@ private: }; static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader has wrong size"); -class NroHeader { -public: - static constexpr u32 Magic = Common::MakeMagic('N', 'R', 'O', '0'); - -public: - bool IsMagicValid() const { - return m_magic == Magic; - } - - u32 GetSize() const { - return m_size; - } - - u32 GetTextOffset() const { - return m_text_offset; - } - - u32 GetTextSize() const { - return m_text_size; - } - - u32 GetRoOffset() const { - return m_ro_offset; - } - - u32 GetRoSize() const { - return m_ro_size; - } - - u32 GetRwOffset() const { - return m_rw_offset; - } - - u32 GetRwSize() const { - return m_rw_size; - } - - u32 GetBssSize() const { - return m_bss_size; - } - - const ModuleId* GetModuleId() const { - return std::addressof(m_module_id); - } - -private: +static constexpr u32 NRO_HEADER_MAGIC = Common::MakeMagic('N', 'R', 'O', '0'); +struct NroHeader { u32 m_entrypoint_insn; u32 m_mod_offset; INSERT_PADDING_BYTES_NOINIT(0x8); diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index b61ce80bae..db6c98c5a3 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -75,21 +75,16 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, kip->GetKernelCapabilities()); Kernel::CodeSet codeset; - Kernel::PhysicalMemory program_image; - - const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment, const std::vector& data, u32 offset) { + codeset.memory.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); + const auto load_segment = [&codeset](Kernel::CodeSet::Segment& segment, std::span data, u32 offset) { segment.addr = offset; segment.offset = offset; segment.size = PageAlignSize(u32(data.size())); - program_image.resize(offset + data.size()); - std::memcpy(program_image.data() + offset, data.data(), data.size()); + std::memcpy(codeset.memory.data() + offset, data.data(), data.size()); }; - load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset()); load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset()); load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset()); - - program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); codeset.DataSegment().size += kip->GetBSSSize(); // TODO: this is bad form of ASLR, it sucks @@ -98,13 +93,9 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, : std::rand()) * 0x734287f27) & 0xfff000; // Setup the process code layout - if (process - .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0, aslr_offset, false) - .IsError()) { + if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) { return {ResultStatus::ErrorNotInitialized, {}}; } - - codeset.memory = std::move(program_image); const VAddr base_address = GetInteger(process.GetEntryPoint()); process.LoadModule(std::move(codeset), base_address); diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index a3cfa31be7..f5a77a726f 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -160,7 +160,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, } // Build program image - Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size)); + std::vector program_image(PageAlignSize(nro_header.file_size)); std::memcpy(program_image.data(), data.data(), program_image.size()); if (program_image.size() != PageAlignSize(nro_header.file_size)) { return {}; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 458df110b6..f62b1f4957 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -40,17 +41,6 @@ struct MODHeader { }; static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); -std::vector DecompressSegment(const std::vector& compressed_data, - const NSOSegmentHeader& header) { - std::vector uncompressed_data = - Common::Compression::DecompressDataLZ4(compressed_data, header.size); - - ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size, - uncompressed_data.size()); - - return uncompressed_data; -} - constexpr u32 PageAlignSize(u32 size) { return static_cast((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } @@ -76,24 +66,16 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) { return FileType::NSO; } -std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, - const FileSys::VfsFile& nso_file, VAddr load_base, - bool should_pass_arguments, bool load_into_process, - std::optional pm, - std::vector* patches, - s32 patch_index) { - if (nso_file.GetSize() < sizeof(NSOHeader)) { +std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional pm, std::vector* patches, s32 patch_index) { + if (nso_file.GetSize() < sizeof(NSOHeader)) return std::nullopt; - } - NSOHeader nso_header{}; - if (sizeof(NSOHeader) != nso_file.ReadObject(&nso_header)) { + if (sizeof(NSOHeader) != nso_file.ReadObject(&nso_header)) return std::nullopt; - } - - if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { + if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) + return std::nullopt; + if (nso_header.segments.empty()) return std::nullopt; - } // Allocate some space at the beginning if we are patching in PreText mode. const size_t module_start = [&]() -> size_t { @@ -110,42 +92,44 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: return 0; }(); - // Build program image + auto const last_segment_it = &nso_header.segments[nso_header.segments.size() - 1]; + // Build program image directly in codeset memory :) Kernel::CodeSet codeset; - Kernel::PhysicalMemory program_image; - for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { - std::vector data = nso_file.ReadBytes(nso_header.segments_compressed_size[i], - nso_header.segments[i].offset); - if (nso_header.IsSegmentCompressed(i)) { - data = DecompressSegment(data, nso_header.segments[i]); + codeset.memory.resize(module_start + last_segment_it->location + last_segment_it->size); + { + std::vector compressed_data(*std::ranges::max_element(nso_header.segments_compressed_size)); + std::vector decompressed_size(std::ranges::max_element(nso_header.segments, [](auto const& a, auto const& b) { + return a.size < b.size; + })->size); + for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { + nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset); + if (nso_header.IsSegmentCompressed(i)) { + int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]); + ASSERT(r == int(nso_header.segments[i].size)); + std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size); + } else { + std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size); + } + codeset.segments[i].addr = module_start + nso_header.segments[i].location; + codeset.segments[i].offset = module_start + nso_header.segments[i].location; + codeset.segments[i].size = nso_header.segments[i].size; } - program_image.resize(module_start + nso_header.segments[i].location + - static_cast(data.size())); - std::memcpy(program_image.data() + module_start + nso_header.segments[i].location, - data.data(), data.size()); - codeset.segments[i].addr = module_start + nso_header.segments[i].location; - codeset.segments[i].offset = module_start + nso_header.segments[i].location; - codeset.segments[i].size = nso_header.segments[i].size; } if (should_pass_arguments && !Settings::values.program_args.GetValue().empty()) { const auto arg_data{Settings::values.program_args.GetValue()}; codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; - NSOArgumentHeader args_header{ - NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast(arg_data.size()), {}}; - const auto end_offset = program_image.size(); - program_image.resize(static_cast(program_image.size()) + - NSO_ARGUMENT_DATA_ALLOCATION_SIZE); - std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); - std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), - arg_data.size()); + NSOArgumentHeader args_header{NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast(arg_data.size()), {}}; + const auto end_offset = codeset.memory.size(); + codeset.memory.resize(u32(codeset.memory.size()) + NSO_ARGUMENT_DATA_ALLOCATION_SIZE); + std::memcpy(codeset.memory.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); + std::memcpy(codeset.memory.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), arg_data.size()); } codeset.DataSegment().size += nso_header.segments[2].bss_size; - u32 image_size{ - PageAlignSize(static_cast(program_image.size()) + nso_header.segments[2].bss_size)}; - program_image.resize(image_size); + u32 image_size = PageAlignSize(u32(codeset.memory.size()) + nso_header.segments[2].bss_size); + codeset.memory.resize(image_size); for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { codeset.segments[i].size = PageAlignSize(codeset.segments[i].size); @@ -154,8 +138,7 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: // Apply patches if necessary const auto name = nso_file.GetName(); if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { - std::span patchable_section(program_image.data() + module_start, - program_image.size() - module_start); + std::span patchable_section(codeset.memory.data() + module_start, codeset.memory.size() - module_start); std::vector pi_header(sizeof(NSOHeader) + patchable_section.size()); std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); std::memcpy(pi_header.data() + sizeof(NSOHeader), patchable_section.data(), @@ -174,15 +157,15 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: //Set module ID using build_id from the NSO header patch->SetModuleID(nso_header.build_id); // Patch SVCs and MRS calls in the guest code - while (!patch->PatchText(program_image, code)) { + while (!patch->PatchText(codeset.memory, code)) { patch = &patches->emplace_back(); patch->SetModuleID(nso_header.build_id); // In case the patcher is changed for big modules, the new patcher should also have the build_id } } else if (patch) { - // Relocate code patch and copy to the program_image. + // Relocate code patch and copy to the program image. // Save size before RelocateAndCopy (which may resize) - const size_t size_before_relocate = program_image.size(); - if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) { + const size_t size_before_relocate = codeset.memory.size(); + if (patch->RelocateAndCopy(load_base, code, codeset.memory, &process.GetPostHandlers())) { // Update patch section. auto& patch_segment = codeset.PatchSegment(); auto& post_patch_segment = codeset.PostPatchSegment(); @@ -203,7 +186,7 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } // Refresh image_size to take account the patch section if it was added by RelocateAndCopy - image_size = static_cast(program_image.size()); + image_size = static_cast(codeset.memory.size()); } #endif @@ -234,9 +217,7 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } // Load codeset for current process - codeset.memory = std::move(program_image); process.LoadModule(std::move(codeset), load_base); - return load_base + image_size; }