summaryrefslogtreecommitdiff
path: root/src/core/loader/linker.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/linker.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/linker.cpp')
-rw-r--r--src/core/loader/linker.cpp151
1 files changed, 151 insertions, 0 deletions
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