diff options
Diffstat (limited to 'src/core/loader/nso.cpp')
| -rw-r--r-- | src/core/loader/nso.cpp | 68 |
1 files changed, 18 insertions, 50 deletions
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 @@ | |||
| 22 | 22 | ||
| 23 | namespace Loader { | 23 | namespace Loader { |
| 24 | 24 | ||
| 25 | struct NsoSegmentHeader { | 25 | struct 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 | }; | ||
| 34 | static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size."); | ||
| 35 | |||
| 36 | struct NsoHeader { | ||
| 37 | using SHA256Hash = std::array<u8, 0x20>; | ||
| 38 | |||
| 39 | struct RODataRelativeExtent { | ||
| 40 | u32 data_offset; | ||
| 41 | u32 size; | ||
| 42 | }; | ||
| 43 | |||
| 44 | u32_le magic; | ||
| 45 | u32_le version; | ||
| 46 | u32 reserved; | ||
| 47 | u32_le flags; | ||
| 48 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) | ||
| 49 | std::array<u8, 0x20> build_id; | ||
| 50 | std::array<u32_le, 3> segments_compressed_size; | ||
| 51 | std::array<u8, 0x1C> padding; | ||
| 52 | RODataRelativeExtent api_info_extent; | ||
| 53 | RODataRelativeExtent dynstr_extent; | ||
| 54 | RODataRelativeExtent dynsyn_extent; | ||
| 55 | std::array<SHA256Hash, 3> segment_hashes; | ||
| 56 | |||
| 57 | bool IsSegmentCompressed(size_t segment_num) const { | ||
| 58 | ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); | ||
| 59 | return ((flags >> segment_num) & 1); | ||
| 60 | } | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(NsoHeader) == 0x100, "NsoHeader has incorrect size."); | ||
| 63 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); | ||
| 64 | |||
| 65 | struct ModHeader { | ||
| 66 | u32_le magic; | 26 | u32_le magic; |
| 67 | u32_le dynamic_offset; | 27 | u32_le dynamic_offset; |
| 68 | u32_le bss_start_offset; | 28 | u32_le bss_start_offset; |
| @@ -71,7 +31,12 @@ struct ModHeader { | |||
| 71 | u32_le eh_frame_hdr_end_offset; | 31 | u32_le eh_frame_hdr_end_offset; |
| 72 | 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 |
| 73 | }; | 33 | }; |
| 74 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 34 | static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); |
| 35 | |||
| 36 | bool NSOHeader::IsSegmentCompressed(size_t segment_num) const { | ||
| 37 | ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); | ||
| 38 | return ((flags >> segment_num) & 1) != 0; | ||
| 39 | } | ||
| 75 | 40 | ||
| 76 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} | 41 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} |
| 77 | 42 | ||
| @@ -89,7 +54,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 89 | } | 54 | } |
| 90 | 55 | ||
| 91 | static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | 56 | static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, |
| 92 | const NsoSegmentHeader& header) { | 57 | const NSOSegmentHeader& header) { |
| 93 | std::vector<u8> uncompressed_data(header.size); | 58 | std::vector<u8> uncompressed_data(header.size); |
| 94 | const int bytes_uncompressed = | 59 | const int bytes_uncompressed = |
| 95 | LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), | 60 | LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), |
| @@ -111,15 +76,18 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 111 | const FileSys::VfsFile& file, VAddr load_base, | 76 | const FileSys::VfsFile& file, VAddr load_base, |
| 112 | bool should_pass_arguments, | 77 | bool should_pass_arguments, |
| 113 | std::optional<FileSys::PatchManager> pm) { | 78 | std::optional<FileSys::PatchManager> pm) { |
| 114 | if (file.GetSize() < sizeof(NsoHeader)) | 79 | if (file.GetSize() < sizeof(NSOHeader)) { |
| 115 | return {}; | 80 | return {}; |
| 81 | } | ||
| 116 | 82 | ||
| 117 | NsoHeader nso_header{}; | 83 | NSOHeader nso_header{}; |
| 118 | if (sizeof(NsoHeader) != file.ReadObject(&nso_header)) | 84 | if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { |
| 119 | return {}; | 85 | return {}; |
| 86 | } | ||
| 120 | 87 | ||
| 121 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | 88 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { |
| 122 | return {}; | 89 | return {}; |
| 90 | } | ||
| 123 | 91 | ||
| 124 | // Build program image | 92 | // Build program image |
| 125 | Kernel::CodeSet codeset; | 93 | Kernel::CodeSet codeset; |
| @@ -155,10 +123,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 155 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | 123 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); |
| 156 | 124 | ||
| 157 | // Read MOD header | 125 | // Read MOD header |
| 158 | ModHeader mod_header{}; | 126 | MODHeader mod_header{}; |
| 159 | // Default .bss to size in segment header if MOD0 section doesn't exist | 127 | // Default .bss to size in segment header if MOD0 section doesn't exist |
| 160 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; | 128 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; |
| 161 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); | 129 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader)); |
| 162 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | 130 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; |
| 163 | if (has_mod_header) { | 131 | if (has_mod_header) { |
| 164 | // Resize program image to include .bss section and page align each section | 132 | // 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, | |||
| 171 | // Apply patches if necessary | 139 | // Apply patches if necessary |
| 172 | if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { | 140 | if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { |
| 173 | std::vector<u8> pi_header(program_image.size() + 0x100); | 141 | std::vector<u8> pi_header(program_image.size() + 0x100); |
| 174 | std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); | 142 | std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); |
| 175 | std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); | 143 | std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); |
| 176 | 144 | ||
| 177 | pi_header = pm->PatchNSO(pi_header); | 145 | pi_header = pm->PatchNSO(pi_header); |