summaryrefslogtreecommitdiff
path: root/src/core/loader/nso.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2017-10-05 23:30:08 -0400
committerGravatar bunnei2017-10-05 23:30:08 -0400
commit33ea53094cc1f34c27ca295472f01f8dd09a300b (patch)
treec45a8eefd68222c390b28ab2dfba3d787c4634ac /src/core/loader/nso.cpp
parentnso: Fixes to support homebrew NSOs without a MOD header. (diff)
downloadyuzu-33ea53094cc1f34c27ca295472f01f8dd09a300b.tar.gz
yuzu-33ea53094cc1f34c27ca295472f01f8dd09a300b.tar.xz
yuzu-33ea53094cc1f34c27ca295472f01f8dd09a300b.zip
loader: Add support for NRO, as well as various fixes and shared linker.
Diffstat (limited to 'src/core/loader/nso.cpp')
-rw-r--r--src/core/loader/nso.cpp143
1 files changed, 15 insertions, 128 deletions
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 0155dec82..4d885fef7 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -14,19 +14,6 @@
14 14
15namespace Loader { 15namespace Loader {
16 16
17enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
18
19enum DynamicType : u32 {
20 DT_NULL = 0,
21 DT_PLTRELSZ = 2,
22 DT_STRTAB = 5,
23 DT_SYMTAB = 6,
24 DT_RELA = 7,
25 DT_RELASZ = 8,
26 DT_STRSZ = 10,
27 DT_JMPREL = 23,
28};
29
30struct NsoSegmentHeader { 17struct NsoSegmentHeader {
31 u32_le offset; 18 u32_le offset;
32 u32_le location; 19 u32_le location;
@@ -46,8 +33,6 @@ struct NsoHeader {
46static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); 33static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
47 34
48struct ModHeader { 35struct ModHeader {
49 INSERT_PADDING_BYTES(0x4);
50 u32_le offset_to_start; // Always 8
51 u32_le magic; 36 u32_le magic;
52 u32_le dynamic_offset; 37 u32_le dynamic_offset;
53 u32_le bss_start_offset; 38 u32_le bss_start_offset;
@@ -56,7 +41,7 @@ struct ModHeader {
56 u32_le eh_frame_hdr_end_offset; 41 u32_le eh_frame_hdr_end_offset;
57 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base 42 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
58}; 43};
59static_assert(sizeof(ModHeader) == 0x24, "ModHeader has incorrect size."); 44static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
60 45
61FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file) { 46FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file) {
62 u32 magic = 0; 47 u32 magic = 0;
@@ -95,101 +80,6 @@ static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeade
95 return uncompressed_data; 80 return uncompressed_data;
96} 81}
97 82
98void AppLoader_NSO::WriteRelocations(const std::vector<Symbol>& symbols, VAddr load_base,
99 u64 relocation_offset, u64 size, bool is_jump_relocation) {
100 for (u64 i = 0; i < size; i += 0x18) {
101 VAddr addr = load_base + relocation_offset + i;
102 u64 offset = Memory::Read64(addr);
103 u64 info = Memory::Read64(addr + 8);
104 u64 addend_unsigned = Memory::Read64(addr + 16);
105 s64 addend{};
106 std::memcpy(&addend, &addend_unsigned, sizeof(u64));
107
108 RelocationType rtype = static_cast<RelocationType>(info & 0xFFFFFFFF);
109 u32 rsym = static_cast<u32>(info >> 32);
110 VAddr ea = load_base + offset;
111
112 const Symbol& symbol = symbols[rsym];
113
114 switch (rtype) {
115 case RelocationType::RELATIVE:
116 if (!symbol.name.empty()) {
117 exports[symbol.name] = load_base + addend;
118 }
119 Memory::Write64(ea, load_base + addend);
120 break;
121 case RelocationType::JUMP_SLOT:
122 case RelocationType::GLOB_DAT:
123 if (!symbol.value) {
124 imports[symbol.name] = {ea, 0};
125 } else {
126 exports[symbol.name] = symbol.value;
127 Memory::Write64(ea, symbol.value);
128 }
129 break;
130 case RelocationType::ABS64:
131 if (!symbol.value) {
132 imports[symbol.name] = {ea, addend};
133 } else {
134 exports[symbol.name] = symbol.value + addend;
135 Memory::Write64(ea, symbol.value + addend);
136 }
137 break;
138 default:
139 LOG_CRITICAL(Loader, "Unknown relocation type: %d", rtype);
140 break;
141 }
142 }
143}
144
145void AppLoader_NSO::Relocate(VAddr load_base, VAddr dynamic_section_addr) {
146 std::map<u64, u64> dynamic;
147 while (1) {
148 u64 tag = Memory::Read64(dynamic_section_addr);
149 u64 value = Memory::Read64(dynamic_section_addr + 8);
150 dynamic_section_addr += 16;
151
152 if (tag == DT_NULL) {
153 break;
154 }
155 dynamic[tag] = value;
156 }
157
158 u64 strtabsize = dynamic[DT_STRSZ];
159 std::vector<u8> strtab;
160 strtab.resize(strtabsize);
161 Memory::ReadBlock(load_base + dynamic[DT_STRTAB], strtab.data(), strtabsize);
162
163 VAddr addr = load_base + dynamic[DT_SYMTAB];
164 std::vector<Symbol> symbols;
165 while (1) {
166 const u32 stname = Memory::Read32(addr);
167 const u16 stshndx = Memory::Read16(addr + 6);
168 const u64 stvalue = Memory::Read64(addr + 8);
169 addr += 24;
170
171 if (stname >= strtabsize) {
172 break;
173 }
174
175 std::string name = reinterpret_cast<char*>(&strtab[stname]);
176 if (stvalue) {
177 exports[name] = load_base + stvalue;
178 symbols.emplace_back(std::move(name), load_base + stvalue);
179 } else {
180 symbols.emplace_back(std::move(name), 0);
181 }
182 }
183
184 if (dynamic.find(DT_RELA) != dynamic.end()) {
185 WriteRelocations(symbols, load_base, dynamic[DT_RELA], dynamic[DT_RELASZ], false);
186 }
187
188 if (dynamic.find(DT_JMPREL) != dynamic.end()) {
189 WriteRelocations(symbols, load_base, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], true);
190 }
191}
192
193VAddr AppLoader_NSO::GetEntryPoint(VAddr load_base) const { 83VAddr AppLoader_NSO::GetEntryPoint(VAddr load_base) const {
194 // Find nnMain function, set entrypoint to that address 84 // Find nnMain function, set entrypoint to that address
195 const auto& search = exports.find("nnMain"); 85 const auto& search = exports.find("nnMain");
@@ -233,10 +123,14 @@ bool AppLoader_NSO::LoadNso(const std::string& path, VAddr load_base) {
233 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); 123 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
234 } 124 }
235 125
126 // MOD header pointer is at .text offset + 4
127 u32 module_offset;
128 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
129
236 // Read MOD header 130 // Read MOD header
237 ModHeader mod_header{}; 131 ModHeader mod_header{};
238 u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist 132 u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist
239 std::memcpy(&mod_header, program_image.data(), sizeof(ModHeader)); 133 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
240 const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')}; 134 const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')};
241 if (has_mod_header) { 135 if (has_mod_header) {
242 // Resize program image to include .bss section and page align each section 136 // Resize program image to include .bss section and page align each section
@@ -245,16 +139,17 @@ bool AppLoader_NSO::LoadNso(const std::string& path, VAddr load_base) {
245 } 139 }
246 program_image.resize(PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)); 140 program_image.resize(PageAlignSize(static_cast<u32>(program_image.size()) + bss_size));
247 141
142 // Relocate symbols if there was a proper MOD header - This must happen after the image has been
143 // loaded into memory
144 if (has_mod_header) {
145 Relocate(program_image, module_offset + mod_header.dynamic_offset, load_base);
146 }
147
248 // Load codeset for current process 148 // Load codeset for current process
249 codeset->name = path; 149 codeset->name = path;
250 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 150 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
251 Kernel::g_current_process->LoadModule(codeset, load_base); 151 Kernel::g_current_process->LoadModule(codeset, load_base);
252 152
253 // Relocate symbols if there was a proper MOD header - This must happen after the image has been
254 // loaded into memory
255 if (has_mod_header) {
256 Relocate(load_base, load_base + mod_header.offset_to_start + mod_header.dynamic_offset);
257 }
258 return true; 153 return true;
259} 154}
260 155
@@ -267,13 +162,13 @@ ResultStatus AppLoader_NSO::Load() {
267 } 162 }
268 163
269 // Load and relocate "main" and "sdk" NSO 164 // Load and relocate "main" and "sdk" NSO
270 static constexpr VAddr main_base{0x10000000}; 165 static constexpr VAddr main_base{0x710000000};
271 Kernel::g_current_process = Kernel::Process::Create("main"); 166 Kernel::g_current_process = Kernel::Process::Create("main");
272 if (!LoadNso(filepath, main_base)) { 167 if (!LoadNso(filepath, main_base)) {
273 return ResultStatus::ErrorInvalidFormat; 168 return ResultStatus::ErrorInvalidFormat;
274 } 169 }
275 const std::string sdkpath = filepath.substr(0, filepath.find_last_of("/\\")) + "/sdk"; 170 const std::string sdkpath = filepath.substr(0, filepath.find_last_of("/\\")) + "/sdk";
276 if (!LoadNso(sdkpath, 0x20000000)) { 171 if (!LoadNso(sdkpath, 0x720000000)) {
277 LOG_WARNING(Loader, "failed to find SDK NSO"); 172 LOG_WARNING(Loader, "failed to find SDK NSO");
278 } 173 }
279 174
@@ -283,15 +178,7 @@ ResultStatus AppLoader_NSO::Load() {
283 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 178 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
284 Kernel::g_current_process->Run(GetEntryPoint(main_base), 48, Kernel::DEFAULT_STACK_SIZE); 179 Kernel::g_current_process->Run(GetEntryPoint(main_base), 48, Kernel::DEFAULT_STACK_SIZE);
285 180
286 // Resolve imports 181 ResolveImports();
287 for (const auto& import : imports) {
288 const auto& search = exports.find(import.first);
289 if (search != exports.end()) {
290 Memory::Write64(import.second.ea, search->second + import.second.addend);
291 } else {
292 LOG_ERROR(Loader, "Unresolved import: %s", import.first.c_str());
293 }
294 }
295 182
296 is_loaded = true; 183 is_loaded = true;
297 return ResultStatus::Success; 184 return ResultStatus::Success;