1 | //===- Relocations.cpp ----------------------------------------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Relocations.h" |
10 | |
11 | #include "InputChunks.h" |
12 | #include "OutputSegment.h" |
13 | #include "SymbolTable.h" |
14 | #include "SyntheticSections.h" |
15 | |
16 | using namespace llvm; |
17 | using namespace llvm::wasm; |
18 | |
19 | namespace lld::wasm { |
20 | |
21 | static bool requiresGOTAccess(const Symbol *sym) { |
22 | if (sym->isShared()) |
23 | return true; |
24 | if (!ctx.isPic && |
25 | config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic) |
26 | return false; |
27 | if (sym->isHidden() || sym->isLocal()) |
28 | return false; |
29 | // With `-Bsymbolic` (or when building an executable) as don't need to use |
30 | // the GOT for symbols that are defined within the current module. |
31 | if (sym->isDefined() && (!config->shared || config->bsymbolic)) |
32 | return false; |
33 | return true; |
34 | } |
35 | |
36 | static bool allowUndefined(const Symbol* sym) { |
37 | // Symbols that are explicitly imported are always allowed to be undefined at |
38 | // link time. |
39 | if (sym->isImported()) |
40 | return true; |
41 | if (isa<UndefinedFunction>(Val: sym) && config->importUndefined) |
42 | return true; |
43 | |
44 | return config->allowUndefinedSymbols.count(Key: sym->getName()) != 0; |
45 | } |
46 | |
47 | static void reportUndefined(ObjFile *file, Symbol *sym) { |
48 | if (!allowUndefined(sym)) { |
49 | switch (config->unresolvedSymbols) { |
50 | case UnresolvedPolicy::ReportError: |
51 | error(msg: toString(file) + ": undefined symbol: "+ toString(sym: *sym)); |
52 | break; |
53 | case UnresolvedPolicy::Warn: |
54 | warn(msg: toString(file) + ": undefined symbol: "+ toString(sym: *sym)); |
55 | break; |
56 | case UnresolvedPolicy::Ignore: |
57 | LLVM_DEBUG(dbgs() << "ignoring undefined symbol: "+ toString(*sym) + |
58 | "\n"); |
59 | break; |
60 | case UnresolvedPolicy::ImportDynamic: |
61 | break; |
62 | } |
63 | |
64 | if (auto *f = dyn_cast<UndefinedFunction>(Val: sym)) { |
65 | if (!f->stubFunction && |
66 | config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic && |
67 | !config->importUndefined) { |
68 | f->stubFunction = symtab->createUndefinedStub(sig: *f->getSignature()); |
69 | f->stubFunction->markLive(); |
70 | // Mark the function itself as a stub which prevents it from being |
71 | // assigned a table entry. |
72 | f->isStub = true; |
73 | } |
74 | } |
75 | } |
76 | } |
77 | |
78 | static void addGOTEntry(Symbol *sym) { |
79 | if (requiresGOTAccess(sym)) |
80 | out.importSec->addGOTEntry(sym); |
81 | else |
82 | out.globalSec->addInternalGOTEntry(sym); |
83 | } |
84 | |
85 | void scanRelocations(InputChunk *chunk) { |
86 | if (!chunk->live) |
87 | return; |
88 | ObjFile *file = chunk->file; |
89 | ArrayRef<WasmSignature> types = file->getWasmObj()->types(); |
90 | for (const WasmRelocation &reloc : chunk->getRelocations()) { |
91 | if (reloc.Type == R_WASM_TYPE_INDEX_LEB) { |
92 | // Mark target type as live |
93 | file->typeMap[reloc.Index] = |
94 | out.typeSec->registerType(sig: types[reloc.Index]); |
95 | file->typeIsUsed[reloc.Index] = true; |
96 | continue; |
97 | } |
98 | |
99 | // Other relocation types all have a corresponding symbol |
100 | Symbol *sym = file->getSymbols()[reloc.Index]; |
101 | |
102 | switch (reloc.Type) { |
103 | case R_WASM_TABLE_INDEX_I32: |
104 | case R_WASM_TABLE_INDEX_I64: |
105 | case R_WASM_TABLE_INDEX_SLEB: |
106 | case R_WASM_TABLE_INDEX_SLEB64: |
107 | case R_WASM_TABLE_INDEX_REL_SLEB: |
108 | case R_WASM_TABLE_INDEX_REL_SLEB64: |
109 | if (requiresGOTAccess(sym)) |
110 | break; |
111 | out.elemSec->addEntry(sym: cast<FunctionSymbol>(Val: sym)); |
112 | break; |
113 | case R_WASM_GLOBAL_INDEX_LEB: |
114 | case R_WASM_GLOBAL_INDEX_I32: |
115 | if (!isa<GlobalSymbol>(Val: sym)) |
116 | addGOTEntry(sym); |
117 | break; |
118 | case R_WASM_MEMORY_ADDR_TLS_SLEB: |
119 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
120 | if (!sym->isDefined()) { |
121 | error(msg: toString(file) + ": relocation "+ relocTypeToString(relocType: reloc.Type) + |
122 | " cannot be used against an undefined symbol `"+ toString(sym: *sym) + |
123 | "`"); |
124 | } |
125 | // In single-threaded builds TLS is lowered away and TLS data can be |
126 | // merged with normal data and allowing TLS relocation in non-TLS |
127 | // segments. |
128 | if (config->sharedMemory) { |
129 | if (!sym->isTLS()) { |
130 | error(msg: toString(file) + ": relocation "+ |
131 | relocTypeToString(relocType: reloc.Type) + |
132 | " cannot be used against non-TLS symbol `"+ toString(sym: *sym) + |
133 | "`"); |
134 | } |
135 | if (auto *D = dyn_cast<DefinedData>(Val: sym)) { |
136 | if (!D->segment->outputSeg->isTLS()) { |
137 | error(msg: toString(file) + ": relocation "+ |
138 | relocTypeToString(relocType: reloc.Type) + " cannot be used against `"+ |
139 | toString(sym: *sym) + |
140 | "` in non-TLS section: "+ D->segment->outputSeg->name); |
141 | } |
142 | } |
143 | } |
144 | break; |
145 | } |
146 | |
147 | if (ctx.isPic || |
148 | (sym->isUndefined() && |
149 | config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) { |
150 | switch (reloc.Type) { |
151 | case R_WASM_TABLE_INDEX_SLEB: |
152 | case R_WASM_TABLE_INDEX_SLEB64: |
153 | case R_WASM_MEMORY_ADDR_SLEB: |
154 | case R_WASM_MEMORY_ADDR_LEB: |
155 | case R_WASM_MEMORY_ADDR_SLEB64: |
156 | case R_WASM_MEMORY_ADDR_LEB64: |
157 | // Certain relocation types can't be used when building PIC output, |
158 | // since they would require absolute symbol addresses at link time. |
159 | error(msg: toString(file) + ": relocation "+ relocTypeToString(relocType: reloc.Type) + |
160 | " cannot be used against symbol `"+ toString(sym: *sym) + |
161 | "`; recompile with -fPIC"); |
162 | break; |
163 | case R_WASM_TABLE_INDEX_I32: |
164 | case R_WASM_TABLE_INDEX_I64: |
165 | case R_WASM_MEMORY_ADDR_I32: |
166 | case R_WASM_MEMORY_ADDR_I64: |
167 | // These relocation types are only present in the data section and |
168 | // will be converted into code by `generateRelocationCode`. This |
169 | // code requires the symbols to have GOT entries. |
170 | if (requiresGOTAccess(sym)) |
171 | addGOTEntry(sym); |
172 | break; |
173 | } |
174 | } |
175 | |
176 | if (sym->isUndefined() && !config->relocatable && !sym->isWeak()) { |
177 | // Report undefined symbols |
178 | reportUndefined(file, sym); |
179 | } |
180 | } |
181 | } |
182 | |
183 | } // namespace lld::wasm |
184 |