summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/patch_manager.cpp25
-rw-r--r--src/core/loader/nso.cpp101
-rw-r--r--src/core/loader/nso.h39
3 files changed, 92 insertions, 73 deletions
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 58884b4a0..e11217708 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -20,6 +20,7 @@
20#include "core/file_sys/vfs_vector.h" 20#include "core/file_sys/vfs_vector.h"
21#include "core/hle/service/filesystem/filesystem.h" 21#include "core/hle/service/filesystem/filesystem.h"
22#include "core/loader/loader.h" 22#include "core/loader/loader.h"
23#include "core/loader/nso.h"
23#include "core/settings.h" 24#include "core/settings.h"
24 25
25namespace FileSys { 26namespace FileSys {
@@ -32,14 +33,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
32 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", 33 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
33}; 34};
34 35
35struct NSOBuildHeader {
36 u32_le magic;
37 INSERT_PADDING_BYTES(0x3C);
38 std::array<u8, 0x20> build_id;
39 INSERT_PADDING_BYTES(0xA0);
40};
41static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
42
43std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 36std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
44 std::array<u8, sizeof(u32)> bytes{}; 37 std::array<u8, sizeof(u32)> bytes{};
45 bytes[0] = version % SINGLE_BYTE_MODULUS; 38 bytes[0] = version % SINGLE_BYTE_MODULUS;
@@ -163,14 +156,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
163} 156}
164 157
165std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 158std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
166 if (nso.size() < 0x100) 159 if (nso.size() < sizeof(Loader::NSOHeader)) {
167 return nso; 160 return nso;
161 }
168 162
169 NSOBuildHeader header; 163 Loader::NSOHeader header;
170 std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); 164 std::memcpy(&header, nso.data(), sizeof(header));
171 165
172 if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 166 if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
173 return nso; 167 return nso;
168 }
174 169
175 const auto build_id_raw = Common::HexArrayToString(header.build_id); 170 const auto build_id_raw = Common::HexArrayToString(header.build_id);
176 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 171 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 {
213 } 208 }
214 } 209 }
215 210
216 if (out.size() < 0x100) 211 if (out.size() < sizeof(Loader::NSOHeader)) {
217 return nso; 212 return nso;
218 std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); 213 }
214
215 std::memcpy(out.data(), &header, sizeof(header));
219 return out; 216 return out;
220} 217}
221 218
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 7494f8a28..714d85a59 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -21,36 +21,8 @@
21#include "core/settings.h" 21#include "core/settings.h"
22 22
23namespace Loader { 23namespace Loader {
24 24namespace {
25struct NsoSegmentHeader { 25struct MODHeader {
26 u32_le offset;
27 u32_le location;
28 u32_le size;
29 union {
30 u32_le alignment;
31 u32_le bss_size;
32 };
33};
34static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
35
36struct NsoHeader {
37 u32_le magic;
38 u32_le version;
39 INSERT_PADDING_WORDS(1);
40 u8 flags;
41 std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
42 std::array<u8, 0x20> build_id;
43 std::array<u32_le, 3> segments_compressed_size;
44
45 bool IsSegmentCompressed(size_t segment_num) const {
46 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
47 return ((flags >> segment_num) & 1);
48 }
49};
50static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
51static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
52
53struct ModHeader {
54 u32_le magic; 26 u32_le magic;
55 u32_le dynamic_offset; 27 u32_le dynamic_offset;
56 u32_le bss_start_offset; 28 u32_le bss_start_offset;
@@ -59,25 +31,10 @@ struct ModHeader {
59 u32_le eh_frame_hdr_end_offset; 31 u32_le eh_frame_hdr_end_offset;
60 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base 32 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
61}; 33};
62static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); 34static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
63
64AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
65
66FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
67 u32 magic = 0;
68 if (file->ReadObject(&magic) != sizeof(magic)) {
69 return FileType::Error;
70 }
71
72 if (Common::MakeMagic('N', 'S', 'O', '0') != magic) {
73 return FileType::Error;
74 }
75
76 return FileType::NSO;
77}
78 35
79static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, 36std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
80 const NsoSegmentHeader& header) { 37 const NSOSegmentHeader& header) {
81 std::vector<u8> uncompressed_data(header.size); 38 std::vector<u8> uncompressed_data(header.size);
82 const int bytes_uncompressed = 39 const int bytes_uncompressed =
83 LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), 40 LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
@@ -91,23 +48,47 @@ static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
91 return uncompressed_data; 48 return uncompressed_data;
92} 49}
93 50
94static constexpr u32 PageAlignSize(u32 size) { 51constexpr u32 PageAlignSize(u32 size) {
95 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 52 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
96} 53}
54} // Anonymous namespace
55
56bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
57 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
58 return ((flags >> segment_num) & 1) != 0;
59}
60
61AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
62
63FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
64 u32 magic = 0;
65 if (file->ReadObject(&magic) != sizeof(magic)) {
66 return FileType::Error;
67 }
68
69 if (Common::MakeMagic('N', 'S', 'O', '0') != magic) {
70 return FileType::Error;
71 }
72
73 return FileType::NSO;
74}
97 75
98std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, 76std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
99 const FileSys::VfsFile& file, VAddr load_base, 77 const FileSys::VfsFile& file, VAddr load_base,
100 bool should_pass_arguments, 78 bool should_pass_arguments,
101 std::optional<FileSys::PatchManager> pm) { 79 std::optional<FileSys::PatchManager> pm) {
102 if (file.GetSize() < sizeof(NsoHeader)) 80 if (file.GetSize() < sizeof(NSOHeader)) {
103 return {}; 81 return {};
82 }
104 83
105 NsoHeader nso_header{}; 84 NSOHeader nso_header{};
106 if (sizeof(NsoHeader) != file.ReadObject(&nso_header)) 85 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
107 return {}; 86 return {};
87 }
108 88
109 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 89 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
110 return {}; 90 return {};
91 }
111 92
112 // Build program image 93 // Build program image
113 Kernel::CodeSet codeset; 94 Kernel::CodeSet codeset;
@@ -143,10 +124,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
143 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); 124 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
144 125
145 // Read MOD header 126 // Read MOD header
146 ModHeader mod_header{}; 127 MODHeader mod_header{};
147 // Default .bss to size in segment header if MOD0 section doesn't exist 128 // Default .bss to size in segment header if MOD0 section doesn't exist
148 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; 129 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
149 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); 130 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
150 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 131 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
151 if (has_mod_header) { 132 if (has_mod_header) {
152 // Resize program image to include .bss section and page align each section 133 // 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,
158 139
159 // Apply patches if necessary 140 // Apply patches if necessary
160 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 141 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
161 std::vector<u8> pi_header(program_image.size() + 0x100); 142 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
162 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 143 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
163 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 144 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
145 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
146 program_image.end());
164 147
165 pi_header = pm->PatchNSO(pi_header); 148 pi_header = pm->PatchNSO(pi_header);
166 149
167 std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); 150 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
168 } 151 }
169 152
170 // Apply cheats if they exist and the program has a valid title ID 153 // Apply cheats if they exist and the program has a valid title ID
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 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <optional> 8#include <optional>
9#include <type_traits>
8#include "common/common_types.h" 10#include "common/common_types.h"
9#include "common/swap.h" 11#include "common/swap.h"
10#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
@@ -16,6 +18,43 @@ class Process;
16 18
17namespace Loader { 19namespace Loader {
18 20
21struct NSOSegmentHeader {
22 u32_le offset;
23 u32_le location;
24 u32_le size;
25 union {
26 u32_le alignment;
27 u32_le bss_size;
28 };
29};
30static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
31
32struct NSOHeader {
33 using SHA256Hash = std::array<u8, 0x20>;
34
35 struct RODataRelativeExtent {
36 u32_le data_offset;
37 u32_le size;
38 };
39
40 u32_le magic;
41 u32_le version;
42 u32 reserved;
43 u32_le flags;
44 std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
45 std::array<u8, 0x20> build_id;
46 std::array<u32_le, 3> segments_compressed_size;
47 std::array<u8, 0x1C> padding;
48 RODataRelativeExtent api_info_extent;
49 RODataRelativeExtent dynstr_extent;
50 RODataRelativeExtent dynsyn_extent;
51 std::array<SHA256Hash, 3> segment_hashes;
52
53 bool IsSegmentCompressed(size_t segment_num) const;
54};
55static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
56static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
57
19constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; 58constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
20 59
21struct NSOArgumentHeader { 60struct NSOArgumentHeader {