diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index efc572c72..fd21d3ad1 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -20,6 +20,7 @@ #include "core/file_sys/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "core/loader/nso.h" #include "core/settings.h" namespace FileSys { @@ -32,14 +33,6 @@ constexpr std::array EXEFS_FILE_NAMES{ "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", }; -struct NSOBuildHeader { - u32_le magic; - INSERT_PADDING_BYTES(0x3C); - std::array build_id; - INSERT_PADDING_BYTES(0xA0); -}; -static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size."); - std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { std::array bytes{}; bytes[0] = version % SINGLE_BYTE_MODULUS; @@ -163,15 +156,16 @@ std::vector PatchManager::CollectPatches(const std::vector PatchManager::PatchNSO(const std::vector& nso) const { - if (nso.size() < sizeof(NSOBuildHeader)) { + if (nso.size() < sizeof(Loader::NSOHeader)) { return nso; } - NSOBuildHeader header; - std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); + Loader::NSOHeader header; + std::memcpy(&header, nso.data(), sizeof(header)); - if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) + if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { return nso; + } const auto build_id_raw = Common::HexArrayToString(header.build_id); const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); @@ -214,11 +208,11 @@ std::vector PatchManager::PatchNSO(const std::vector& nso) const { } } - if (out.size() < sizeof(NSOBuildHeader)) { + if (out.size() < sizeof(Loader::NSOHeader)) { return nso; } - std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); + std::memcpy(out.data(), &header, sizeof(header)); return out; } diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index a52104792..262eaeaee 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -22,47 +22,7 @@ namespace Loader { -struct NsoSegmentHeader { - u32_le offset; - u32_le location; - u32_le size; - union { - u32_le alignment; - u32_le bss_size; - }; -}; -static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size."); - -struct NsoHeader { - using SHA256Hash = std::array; - - struct RODataRelativeExtent { - u32 data_offset; - u32 size; - }; - - u32_le magic; - u32_le version; - u32 reserved; - u32_le flags; - std::array segments; // Text, RoData, Data (in that order) - std::array build_id; - std::array segments_compressed_size; - std::array padding; - RODataRelativeExtent api_info_extent; - RODataRelativeExtent dynstr_extent; - RODataRelativeExtent dynsyn_extent; - std::array segment_hashes; - - bool IsSegmentCompressed(size_t segment_num) const { - ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); - return ((flags >> segment_num) & 1); - } -}; -static_assert(sizeof(NsoHeader) == 0x100, "NsoHeader has incorrect size."); -static_assert(std::is_trivially_copyable_v, "NsoHeader isn't trivially copyable."); - -struct ModHeader { +struct MODHeader { u32_le magic; u32_le dynamic_offset; u32_le bss_start_offset; @@ -71,7 +31,12 @@ struct ModHeader { u32_le eh_frame_hdr_end_offset; u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base }; -static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); +static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); + +bool NSOHeader::IsSegmentCompressed(size_t segment_num) const { + ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); + return ((flags >> segment_num) & 1) != 0; +} AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} @@ -89,7 +54,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { } static std::vector DecompressSegment(const std::vector& compressed_data, - const NsoSegmentHeader& header) { + const NSOSegmentHeader& header) { std::vector uncompressed_data(header.size); const int bytes_uncompressed = LZ4_decompress_safe(reinterpret_cast(compressed_data.data()), @@ -111,15 +76,18 @@ std::optional AppLoader_NSO::LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, VAddr load_base, bool should_pass_arguments, std::optional pm) { - if (file.GetSize() < sizeof(NsoHeader)) + if (file.GetSize() < sizeof(NSOHeader)) { return {}; + } - NsoHeader nso_header{}; - if (sizeof(NsoHeader) != file.ReadObject(&nso_header)) + NSOHeader nso_header{}; + if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { return {}; + } - if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) + if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { return {}; + } // Build program image Kernel::CodeSet codeset; @@ -155,10 +123,10 @@ std::optional AppLoader_NSO::LoadModule(Kernel::Process& process, std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); // Read MOD header - ModHeader mod_header{}; + MODHeader mod_header{}; // Default .bss to size in segment header if MOD0 section doesn't exist u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; - std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); + std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader)); const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; if (has_mod_header) { // Resize program image to include .bss section and page align each section @@ -171,7 +139,7 @@ std::optional AppLoader_NSO::LoadModule(Kernel::Process& process, // Apply patches if necessary if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { std::vector pi_header(program_image.size() + 0x100); - std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); + std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); pi_header = pm->PatchNSO(pi_header); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 167c8a694..4674c3724 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -4,7 +4,9 @@ #pragma once +#include #include +#include #include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/patch_manager.h" @@ -16,6 +18,43 @@ class Process; namespace Loader { +struct NSOSegmentHeader { + u32_le offset; + u32_le location; + u32_le size; + union { + u32_le alignment; + u32_le bss_size; + }; +}; +static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size."); + +struct NSOHeader { + using SHA256Hash = std::array; + + struct RODataRelativeExtent { + u32_le data_offset; + u32_le size; + }; + + u32_le magic; + u32_le version; + u32 reserved; + u32_le flags; + std::array segments; // Text, RoData, Data (in that order) + std::array build_id; + std::array segments_compressed_size; + std::array padding; + RODataRelativeExtent api_info_extent; + RODataRelativeExtent dynstr_extent; + RODataRelativeExtent dynsyn_extent; + std::array segment_hashes; + + bool IsSegmentCompressed(size_t segment_num) const; +}; +static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size."); +static_assert(std::is_trivially_copyable_v, "NSOHeader must be trivially copyable."); + constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; struct NSOArgumentHeader {