forked from suyu/suyu
file_sys/patch_manager: Deduplicate NSO header
This source file was utilizing its own version of the NSO header. Instead of keeping this around, we can have the patch manager also use the version of the header that we have defined in loader/nso.h
This commit is contained in:
parent
90e27ea003
commit
1cf90f4570
3 changed files with 65 additions and 64 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs_vector.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "core/loader/nso.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
@ -32,14 +33,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
|
||||||
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
|
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NSOBuildHeader {
|
|
||||||
u32_le magic;
|
|
||||||
INSERT_PADDING_BYTES(0x3C);
|
|
||||||
std::array<u8, 0x20> build_id;
|
|
||||||
INSERT_PADDING_BYTES(0xA0);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
|
|
||||||
|
|
||||||
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
|
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
|
||||||
std::array<u8, sizeof(u32)> bytes{};
|
std::array<u8, sizeof(u32)> bytes{};
|
||||||
bytes[0] = version % SINGLE_BYTE_MODULUS;
|
bytes[0] = version % SINGLE_BYTE_MODULUS;
|
||||||
|
@ -163,15 +156,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||||
if (nso.size() < sizeof(NSOBuildHeader)) {
|
if (nso.size() < sizeof(Loader::NSOHeader)) {
|
||||||
return nso;
|
return nso;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSOBuildHeader header;
|
Loader::NSOHeader header;
|
||||||
std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
|
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;
|
return nso;
|
||||||
|
}
|
||||||
|
|
||||||
const auto build_id_raw = Common::HexArrayToString(header.build_id);
|
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);
|
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||||
|
@ -214,11 +208,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out.size() < sizeof(NSOBuildHeader)) {
|
if (out.size() < sizeof(Loader::NSOHeader)) {
|
||||||
return nso;
|
return nso;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
|
std::memcpy(out.data(), &header, sizeof(header));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,47 +22,7 @@
|
||||||
|
|
||||||
namespace Loader {
|
namespace Loader {
|
||||||
|
|
||||||
struct NsoSegmentHeader {
|
struct MODHeader {
|
||||||
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<u8, 0x20>;
|
|
||||||
|
|
||||||
struct RODataRelativeExtent {
|
|
||||||
u32 data_offset;
|
|
||||||
u32 size;
|
|
||||||
};
|
|
||||||
|
|
||||||
u32_le magic;
|
|
||||||
u32_le version;
|
|
||||||
u32 reserved;
|
|
||||||
u32_le flags;
|
|
||||||
std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
|
||||||
std::array<u8, 0x20> build_id;
|
|
||||||
std::array<u32_le, 3> segments_compressed_size;
|
|
||||||
std::array<u8, 0x1C> padding;
|
|
||||||
RODataRelativeExtent api_info_extent;
|
|
||||||
RODataRelativeExtent dynstr_extent;
|
|
||||||
RODataRelativeExtent dynsyn_extent;
|
|
||||||
std::array<SHA256Hash, 3> 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>, "NsoHeader isn't trivially copyable.");
|
|
||||||
|
|
||||||
struct ModHeader {
|
|
||||||
u32_le magic;
|
u32_le magic;
|
||||||
u32_le dynamic_offset;
|
u32_le dynamic_offset;
|
||||||
u32_le bss_start_offset;
|
u32_le bss_start_offset;
|
||||||
|
@ -71,7 +31,12 @@ struct ModHeader {
|
||||||
u32_le eh_frame_hdr_end_offset;
|
u32_le eh_frame_hdr_end_offset;
|
||||||
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
|
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)) {}
|
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<u8> DecompressSegment(const std::vector<u8>& compressed_data,
|
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
|
||||||
const NsoSegmentHeader& header) {
|
const NSOSegmentHeader& header) {
|
||||||
std::vector<u8> uncompressed_data(header.size);
|
std::vector<u8> uncompressed_data(header.size);
|
||||||
const int bytes_uncompressed =
|
const int bytes_uncompressed =
|
||||||
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
|
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
|
||||||
|
@ -111,15 +76,18 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||||
const FileSys::VfsFile& file, VAddr load_base,
|
const FileSys::VfsFile& file, VAddr load_base,
|
||||||
bool should_pass_arguments,
|
bool should_pass_arguments,
|
||||||
std::optional<FileSys::PatchManager> pm) {
|
std::optional<FileSys::PatchManager> pm) {
|
||||||
if (file.GetSize() < sizeof(NsoHeader))
|
if (file.GetSize() < sizeof(NSOHeader)) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
NsoHeader nso_header{};
|
NSOHeader nso_header{};
|
||||||
if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
|
if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
|
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Build program image
|
// Build program image
|
||||||
Kernel::CodeSet codeset;
|
Kernel::CodeSet codeset;
|
||||||
|
@ -155,10 +123,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||||
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
|
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
|
||||||
|
|
||||||
// Read MOD header
|
// Read MOD header
|
||||||
ModHeader mod_header{};
|
MODHeader mod_header{};
|
||||||
// Default .bss to size in segment header if MOD0 section doesn't exist
|
// Default .bss to size in segment header if MOD0 section doesn't exist
|
||||||
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
|
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')};
|
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
|
||||||
if (has_mod_header) {
|
if (has_mod_header) {
|
||||||
// Resize program image to include .bss section and page align each section
|
// Resize program image to include .bss section and page align each section
|
||||||
|
@ -171,7 +139,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||||
// Apply patches if necessary
|
// Apply patches if necessary
|
||||||
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
|
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
|
||||||
std::vector<u8> pi_header(program_image.size() + 0x100);
|
std::vector<u8> 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());
|
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
|
||||||
|
|
||||||
pi_header = pm->PatchNSO(pi_header);
|
pi_header = pm->PatchNSO(pi_header);
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
@ -16,6 +18,43 @@ class Process;
|
||||||
|
|
||||||
namespace Loader {
|
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<u8, 0x20>;
|
||||||
|
|
||||||
|
struct RODataRelativeExtent {
|
||||||
|
u32_le data_offset;
|
||||||
|
u32_le size;
|
||||||
|
};
|
||||||
|
|
||||||
|
u32_le magic;
|
||||||
|
u32_le version;
|
||||||
|
u32 reserved;
|
||||||
|
u32_le flags;
|
||||||
|
std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
||||||
|
std::array<u8, 0x20> build_id;
|
||||||
|
std::array<u32_le, 3> segments_compressed_size;
|
||||||
|
std::array<u8, 0x1C> padding;
|
||||||
|
RODataRelativeExtent api_info_extent;
|
||||||
|
RODataRelativeExtent dynstr_extent;
|
||||||
|
RODataRelativeExtent dynsyn_extent;
|
||||||
|
std::array<SHA256Hash, 3> 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>, "NSOHeader must be trivially copyable.");
|
||||||
|
|
||||||
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
||||||
|
|
||||||
struct NSOArgumentHeader {
|
struct NSOArgumentHeader {
|
||||||
|
|
Loading…
Reference in a new issue