Merge pull request #2280 from lioncash/nso

loader/nso: Minor refactoring
master
bunnei 2019-03-23 13:46:09 +07:00 committed by GitHub
commit 6af322a347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 74 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,14 +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() < 0x100) 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);
@ -213,9 +208,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
} }
} }
if (out.size() < 0x100) 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;
} }

@ -21,36 +21,8 @@
#include "core/settings.h" #include "core/settings.h"
namespace Loader { namespace Loader {
namespace {
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 {
u32_le magic;
u32_le version;
INSERT_PADDING_WORDS(1);
u8 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;
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) == 0x6c, "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;
@ -59,7 +31,32 @@ 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.");
std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NSOSegmentHeader& header) {
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed =
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()),
static_cast<int>(compressed_data.size()), header.size);
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
} // Anonymous namespace
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)) {}
@ -76,38 +73,22 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::NSO; return FileType::NSO;
} }
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NsoSegmentHeader& header) {
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed =
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()),
static_cast<int>(compressed_data.size()), header.size);
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, 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;
@ -143,10 +124,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
@ -158,13 +139,15 @@ 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(sizeof(NSOHeader) + program_image.size());
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
program_image.end());
pi_header = pm->PatchNSO(pi_header); pi_header = pm->PatchNSO(pi_header);
std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
} }
// Apply cheats if they exist and the program has a valid title ID // Apply cheats if they exist and the program has a valid title ID

@ -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 {