summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/loader/linker.cpp151
-rw-r--r--src/core/loader/linker.h37
-rw-r--r--src/core/loader/loader.cpp6
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp173
-rw-r--r--src/core/loader/nro.h45
-rw-r--r--src/core/loader/nso.cpp143
-rw-r--r--src/core/loader/nso.h20
9 files changed, 434 insertions, 146 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f8bc1fdd0..33ce8dc81 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -174,8 +174,10 @@ set(SRCS
174 hw/y2r.cpp 174 hw/y2r.cpp
175 loader/3dsx.cpp 175 loader/3dsx.cpp
176 loader/elf.cpp 176 loader/elf.cpp
177 loader/linker.cpp
177 loader/loader.cpp 178 loader/loader.cpp
178 loader/ncch.cpp 179 loader/ncch.cpp
180 loader/nro.cpp
179 loader/nso.cpp 181 loader/nso.cpp
180 loader/smdh.cpp 182 loader/smdh.cpp
181 tracer/recorder.cpp 183 tracer/recorder.cpp
@@ -374,8 +376,10 @@ set(HEADERS
374 hw/y2r.h 376 hw/y2r.h
375 loader/3dsx.h 377 loader/3dsx.h
376 loader/elf.h 378 loader/elf.h
379 loader/linker.h
377 loader/loader.h 380 loader/loader.h
378 loader/ncch.h 381 loader/ncch.h
382 loader/nro.h
379 loader/nso.h 383 loader/nso.h
380 loader/smdh.h 384 loader/smdh.h
381 tracer/recorder.h 385 tracer/recorder.h
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
new file mode 100644
index 000000000..a265b9315
--- /dev/null
+++ b/src/core/loader/linker.cpp
@@ -0,0 +1,151 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "common/common_funcs.h"
8#include "common/logging/log.h"
9#include "common/swap.h"
10#include "core/loader/linker.h"
11#include "core/memory.h"
12
13namespace Loader {
14
15enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
16
17enum DynamicType : u32 {
18 DT_NULL = 0,
19 DT_PLTRELSZ = 2,
20 DT_STRTAB = 5,
21 DT_SYMTAB = 6,
22 DT_RELA = 7,
23 DT_RELASZ = 8,
24 DT_STRSZ = 10,
25 DT_JMPREL = 23,
26};
27
28struct Elf64_Rela {
29 u64_le offset;
30 RelocationType type;
31 u32_le symbol;
32 s64_le addend;
33};
34static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
35
36struct Elf64_Dyn {
37 u64_le tag;
38 u64_le value;
39};
40static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
41
42struct Elf64_Sym {
43 u32_le name;
44 INSERT_PADDING_BYTES(0x2);
45 u16_le shndx;
46 u64_le value;
47 u64_le size;
48};
49static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
50
51void Linker::WriteRelocations(std::vector<u8>& program_image,
52 const std::vector<Symbol>& symbols, u64 relocation_offset,
53 u64 size, bool is_jump_relocation, VAddr load_base) {
54 for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
55 Elf64_Rela rela;
56 std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
57
58 const Symbol& symbol = symbols[rela.symbol];
59 switch (rela.type) {
60 case RelocationType::RELATIVE: {
61 const u64 value = load_base + rela.addend;
62 if (!symbol.name.empty()) {
63 exports[symbol.name] = value;
64 }
65 std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
66 break;
67 }
68 case RelocationType::JUMP_SLOT:
69 case RelocationType::GLOB_DAT:
70 if (!symbol.value) {
71 imports[symbol.name] = {rela.offset + load_base, 0};
72 } else {
73 exports[symbol.name] = symbol.value;
74 std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
75 }
76 break;
77 case RelocationType::ABS64:
78 if (!symbol.value) {
79 imports[symbol.name] = {rela.offset + load_base, rela.addend};
80 } else {
81 const u64 value = symbol.value + rela.addend;
82 exports[symbol.name] = value;
83 std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
84 }
85 break;
86 default:
87 LOG_CRITICAL(Loader, "Unknown relocation type: %d", rela.type);
88 break;
89 }
90 }
91}
92
93void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset,
94 VAddr load_base) {
95 std::map<u64, u64> dynamic;
96 while (dynamic_section_offset < program_image.size()) {
97 Elf64_Dyn dyn;
98 std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
99 dynamic_section_offset += sizeof(Elf64_Dyn);
100
101 if (dyn.tag == DT_NULL) {
102 break;
103 }
104 dynamic[dyn.tag] = dyn.value;
105 }
106
107 u64 offset = dynamic[DT_SYMTAB];
108 std::vector<Symbol> symbols;
109 while (offset < program_image.size()) {
110 Elf64_Sym sym;
111 std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
112 offset += sizeof(Elf64_Sym);
113
114 if (sym.name >= dynamic[DT_STRSZ]) {
115 break;
116 }
117
118 std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
119 if (sym.value) {
120 exports[name] = load_base + sym.value;
121 symbols.emplace_back(std::move(name), load_base + sym.value);
122 } else {
123 symbols.emplace_back(std::move(name), 0);
124 }
125 }
126
127 if (dynamic.find(DT_RELA) != dynamic.end()) {
128 WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], false,
129 load_base);
130 }
131
132 if (dynamic.find(DT_JMPREL) != dynamic.end()) {
133 WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], true,
134 load_base);
135 }
136}
137
138void Linker::ResolveImports() {
139 // Resolve imports
140 for (const auto& import : imports) {
141 const auto& search = exports.find(import.first);
142 if (search != exports.end()) {
143 Memory::Write64(import.second.ea, search->second + import.second.addend);
144 }
145 else {
146 LOG_ERROR(Loader, "Unresolved import: %s", import.first.c_str());
147 }
148 }
149}
150
151} // namespace Loader
diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h
new file mode 100644
index 000000000..d18155f0d
--- /dev/null
+++ b/src/core/loader/linker.h
@@ -0,0 +1,37 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <string>
9#include "common/common_types.h"
10
11namespace Loader {
12
13class Linker {
14protected:
15 struct Symbol {
16 Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
17 std::string name;
18 u64 value;
19 };
20
21 struct Import {
22 VAddr ea;
23 s64 addend;
24 };
25
26 void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
27 u64 relocation_offset, u64 size, bool is_jump_relocation,
28 VAddr load_base);
29 void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
30
31 void ResolveImports();
32
33 std::map<std::string, Import> imports;
34 std::map<std::string, VAddr> exports;
35};
36
37} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index d309e7c9e..d96b9f1f0 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -10,6 +10,7 @@
10#include "core/loader/3dsx.h" 10#include "core/loader/3dsx.h"
11#include "core/loader/elf.h" 11#include "core/loader/elf.h"
12#include "core/loader/ncch.h" 12#include "core/loader/ncch.h"
13#include "core/loader/nro.h"
13#include "core/loader/nso.h" 14#include "core/loader/nso.h"
14 15
15//////////////////////////////////////////////////////////////////////////////////////////////////// 16////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -34,6 +35,7 @@ FileType IdentifyFile(FileUtil::IOFile& file) {
34 CHECK_TYPE(ELF) 35 CHECK_TYPE(ELF)
35 CHECK_TYPE(NCCH) 36 CHECK_TYPE(NCCH)
36 CHECK_TYPE(NSO) 37 CHECK_TYPE(NSO)
38 CHECK_TYPE(NRO)
37 39
38#undef CHECK_TYPE 40#undef CHECK_TYPE
39 41
@@ -121,6 +123,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp
121 case FileType::NSO: 123 case FileType::NSO:
122 return std::make_unique<AppLoader_NSO>(std::move(file), filename, filepath); 124 return std::make_unique<AppLoader_NSO>(std::move(file), filename, filepath);
123 125
126 // NX NRO file format.
127 case FileType::NRO:
128 return std::make_unique<AppLoader_NRO>(std::move(file), filename, filepath);
129
124 default: 130 default:
125 return nullptr; 131 return nullptr;
126 } 132 }
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 79a3bd498..ac26f31fa 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -33,6 +33,7 @@ enum class FileType {
33 ELF, 33 ELF,
34 THREEDSX, // 3DSX 34 THREEDSX, // 3DSX
35 NSO, 35 NSO,
36 NRO,
36}; 37};
37 38
38/** 39/**
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
new file mode 100644
index 000000000..ed638e1fa
--- /dev/null
+++ b/src/core/loader/nro.cpp
@@ -0,0 +1,173 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "common/logging/log.h"
8#include "common/swap.h"
9#include "core/hle/kernel/process.h"
10#include "core/hle/kernel/resource_limit.h"
11#include "core/loader/nro.h"
12#include "core/memory.h"
13
14namespace Loader {
15
16struct NroSegmentHeader {
17 u32_le offset;
18 u32_le size;
19};
20static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect size.");
21
22struct NroHeader {
23 INSERT_PADDING_BYTES(0x4);
24 u32_le module_header_offset;
25 INSERT_PADDING_BYTES(0x8);
26 u32_le magic;
27 INSERT_PADDING_BYTES(0x4);
28 u32_le file_size;
29 INSERT_PADDING_BYTES(0x4);
30 std::array<NroSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
31 u32_le bss_size;
32 INSERT_PADDING_BYTES(0x44);
33};
34static_assert(sizeof(NroHeader) == 0x80, "NroHeader has incorrect size.");
35
36struct ModHeader {
37 u32_le magic;
38 u32_le dynamic_offset;
39 u32_le bss_start_offset;
40 u32_le bss_end_offset;
41 u32_le unwind_start_offset;
42 u32_le unwind_end_offset;
43 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
44};
45static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
46
47FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file) {
48 // Read NSO header
49 NroHeader nro_header{};
50 file.Seek(0, SEEK_SET);
51 if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
52 return FileType::Error;
53 }
54 if (nro_header.magic == MakeMagic('N', 'R', 'O', '0')) {
55 return FileType::NRO;
56 }
57 return FileType::Error;
58}
59
60static constexpr u32 PageAlignSize(u32 size) {
61 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
62}
63
64static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NroSegmentHeader& header) {
65 std::vector<u8> data;
66 data.resize(header.size);
67
68 file.Seek(header.offset + sizeof(NroHeader), SEEK_SET);
69 size_t bytes_read{file.ReadBytes(data.data(), header.size)};
70 if (header.size != PageAlignSize(static_cast<u32>(bytes_read))) {
71 LOG_CRITICAL(Loader, "Failed to read NRO segment bytes", header.size);
72 return {};
73 }
74
75 return data;
76}
77
78VAddr AppLoader_NRO::GetEntryPoint(VAddr load_base) const {
79 // Find nnMain function, set entrypoint to that address
80 const auto& search = exports.find("nnMain");
81 if (search != exports.end()) {
82 return load_base + search->second;
83 }
84 const VAddr entry_point{load_base + sizeof(NroHeader)};
85 LOG_ERROR(Loader, "Unable to find entrypoint, defaulting to: 0x%llx", entry_point);
86 return entry_point;
87}
88
89bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
90 FileUtil::IOFile file(path, "rb");
91 if (!file.IsOpen()) {
92 return {};
93 }
94
95 // Read NSO header
96 NroHeader nro_header{};
97 file.Seek(0, SEEK_SET);
98 if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
99 return {};
100 }
101 if (nro_header.magic != MakeMagic('N', 'R', 'O', '0')) {
102 return {};
103 }
104
105 // Build program image
106 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0);
107 std::vector<u8> program_image;
108 program_image.resize(PageAlignSize(nro_header.file_size + nro_header.bss_size));
109 file.Seek(0, SEEK_SET);
110 file.ReadBytes(program_image.data(), nro_header.file_size);
111
112 for (int i = 0; i < nro_header.segments.size(); ++i) {
113 codeset->segments[i].addr = nro_header.segments[i].offset;
114 codeset->segments[i].offset = nro_header.segments[i].offset;
115 codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size);
116 }
117
118 // Read MOD header
119 ModHeader mod_header{};
120 u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist
121 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
122 sizeof(ModHeader));
123 const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')};
124 if (has_mod_header) {
125 // Resize program image to include .bss section and page align each section
126 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
127 codeset->data.size += bss_size;
128 }
129 program_image.resize(PageAlignSize(static_cast<u32>(program_image.size()) + bss_size));
130
131 // Relocate symbols if there was a proper MOD header - This must happen after the image has been
132 // loaded into memory
133 if (has_mod_header) {
134 Relocate(program_image, nro_header.module_header_offset + mod_header.dynamic_offset,
135 load_base);
136 }
137
138 // Load codeset for current process
139 codeset->name = path;
140 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
141 Kernel::g_current_process->LoadModule(codeset, load_base);
142
143 return true;
144}
145
146ResultStatus AppLoader_NRO::Load() {
147 if (is_loaded) {
148 return ResultStatus::ErrorAlreadyLoaded;
149 }
150 if (!file.IsOpen()) {
151 return ResultStatus::Error;
152 }
153
154 // Load and relocate "main" and "sdk" NSO
155 static constexpr VAddr main_base{0x10000000};
156 Kernel::g_current_process = Kernel::Process::Create("main");
157 if (!LoadNro(filepath, main_base)) {
158 return ResultStatus::ErrorInvalidFormat;
159 }
160
161 Kernel::g_current_process->svc_access_mask.set();
162 Kernel::g_current_process->address_mappings = default_address_mappings;
163 Kernel::g_current_process->resource_limit =
164 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
165 Kernel::g_current_process->Run(GetEntryPoint(main_base), 48, Kernel::DEFAULT_STACK_SIZE);
166
167 ResolveImports();
168
169 is_loaded = true;
170 return ResultStatus::Success;
171}
172
173} // namespace Loader
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
new file mode 100644
index 000000000..d145b68d5
--- /dev/null
+++ b/src/core/loader/nro.h
@@ -0,0 +1,45 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <string>
9#include "common/common_types.h"
10#include "common/file_util.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/loader/linker.h"
13#include "core/loader/loader.h"
14
15namespace Loader {
16
17/// Loads an NRO file
18class AppLoader_NRO final : public AppLoader, Linker {
19public:
20 AppLoader_NRO(FileUtil::IOFile&& file, std::string filename, std::string filepath)
21 : AppLoader(std::move(file)), filename(std::move(filename)), filepath(std::move(filepath)) {
22 }
23
24 /**
25 * Returns the type of the file
26 * @param file FileUtil::IOFile open file
27 * @return FileType found, or FileType::Error if this loader doesn't know it
28 */
29 static FileType IdentifyType(FileUtil::IOFile& file);
30
31 FileType GetFileType() override {
32 return IdentifyType(file);
33 }
34
35 ResultStatus Load() override;
36
37private:
38 VAddr GetEntryPoint(VAddr load_base) const;
39 bool LoadNro(const std::string& path, VAddr load_base);
40
41 std::string filename;
42 std::string filepath;
43};
44
45} // namespace Loader
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;
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 16b41b6dc..431b960b1 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -9,12 +9,13 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/loader/linker.h"
12#include "core/loader/loader.h" 13#include "core/loader/loader.h"
13 14
14namespace Loader { 15namespace Loader {
15 16
16/// Loads an NSO file 17/// Loads an NSO file
17class AppLoader_NSO final : public AppLoader { 18class AppLoader_NSO final : public AppLoader, Linker {
18public: 19public:
19 AppLoader_NSO(FileUtil::IOFile&& file, std::string filename, std::string filepath) 20 AppLoader_NSO(FileUtil::IOFile&& file, std::string filename, std::string filepath)
20 : AppLoader(std::move(file)), filename(std::move(filename)), filepath(std::move(filepath)) { 21 : AppLoader(std::move(file)), filename(std::move(filename)), filepath(std::move(filepath)) {
@@ -34,25 +35,8 @@ public:
34 ResultStatus Load() override; 35 ResultStatus Load() override;
35 36
36private: 37private:
37 struct Symbol {
38 Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
39 std::string name;
40 u64 value;
41 };
42
43 struct Import {
44 VAddr ea;
45 s64 addend;
46 };
47
48 void WriteRelocations(const std::vector<Symbol>& symbols, VAddr load_base,
49 u64 relocation_offset, u64 size, bool is_jump_relocation);
50 VAddr GetEntryPoint(VAddr load_base) const; 38 VAddr GetEntryPoint(VAddr load_base) const;
51 bool LoadNso(const std::string& path, VAddr load_base); 39 bool LoadNso(const std::string& path, VAddr load_base);
52 void Relocate(VAddr load_base, VAddr dynamic_section_addr);
53
54 std::map<std::string, Import> imports;
55 std::map<std::string, VAddr> exports;
56 40
57 std::string filename; 41 std::string filename;
58 std::string filepath; 42 std::string filepath;