diff options
| author | lizzie <lizzie@eden-emu.dev> | 2026-02-28 01:06:33 +0100 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-02-28 01:06:33 +0100 |
| commit | 06a08de68a95506a9cbe4633c58f6bdb5ded0c70 (patch) | |
| tree | 122a2d66da07e0fdbdbf3c466c1cf88d4d4b4d01 /src/core/loader/nso.cpp | |
| parent | f8712e50e6860616b4ea86d57a9b09cd99aad728 (diff) | |
[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 <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3639
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
Diffstat (limited to 'src/core/loader/nso.cpp')
| -rw-r--r-- | src/core/loader/nso.cpp | 99 |
1 files changed, 40 insertions, 59 deletions
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 <algorithm> #include <cinttypes> #include <cstring> #include <vector> @@ -40,17 +41,6 @@ struct MODHeader { }; static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); -std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, - const NSOSegmentHeader& header) { - std::vector<u8> 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<u32>((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<VAddr> 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<FileSys::PatchManager> pm, - std::vector<Core::NCE::Patcher>* patches, - s32 patch_index) { - if (nso_file.GetSize() < sizeof(NSOHeader)) { +std::optional<VAddr> 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<FileSys::PatchManager> pm, std::vector<Core::NCE::Patcher>* 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<VAddr> 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<u8> 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<u8> compressed_data(*std::ranges::max_element(nso_header.segments_compressed_size)); + std::vector<u8> 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<u32>(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<u32_le>(arg_data.size()), {}}; - const auto end_offset = program_image.size(); - program_image.resize(static_cast<u32>(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<u32_le>(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<u32>(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<VAddr> 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<u8> patchable_section(program_image.data() + module_start, - program_image.size() - module_start); + std::span<u8> patchable_section(codeset.memory.data() + module_start, codeset.memory.size() - module_start); std::vector<u8> 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<VAddr> 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<VAddr> 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<u32>(program_image.size()); + image_size = static_cast<u32>(codeset.memory.size()); } #endif @@ -234,9 +217,7 @@ std::optional<VAddr> 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; } |
