/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/wasm/SymbolTable.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- SymbolTable.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 "SymbolTable.h" |
10 | | #include "Config.h" |
11 | | #include "InputChunks.h" |
12 | | #include "InputEvent.h" |
13 | | #include "InputGlobal.h" |
14 | | #include "WriterUtils.h" |
15 | | #include "lld/Common/ErrorHandler.h" |
16 | | #include "lld/Common/Memory.h" |
17 | | #include "llvm/ADT/SetVector.h" |
18 | | |
19 | | #define DEBUG_TYPE "lld" |
20 | | |
21 | | using namespace llvm; |
22 | | using namespace llvm::wasm; |
23 | | using namespace llvm::object; |
24 | | using namespace lld; |
25 | | using namespace lld::wasm; |
26 | | |
27 | | SymbolTable *lld::wasm::symtab; |
28 | | |
29 | 327 | void SymbolTable::addFile(InputFile *file) { |
30 | 327 | log("Processing: " + toString(file)); |
31 | 327 | |
32 | 327 | // .a file |
33 | 327 | if (auto *f = dyn_cast<ArchiveFile>(file)) { |
34 | 16 | f->parse(); |
35 | 16 | return; |
36 | 16 | } |
37 | 311 | |
38 | 311 | // .so file |
39 | 311 | if (auto *f = dyn_cast<SharedFile>(file)) { |
40 | 1 | sharedFiles.push_back(f); |
41 | 1 | return; |
42 | 1 | } |
43 | 310 | |
44 | 310 | if (config->trace) |
45 | 2 | message(toString(file)); |
46 | 310 | |
47 | 310 | // LLVM bitcode file |
48 | 310 | if (auto *f = dyn_cast<BitcodeFile>(file)) { |
49 | 43 | f->parse(); |
50 | 43 | bitcodeFiles.push_back(f); |
51 | 43 | return; |
52 | 43 | } |
53 | 267 | |
54 | 267 | // Regular object file |
55 | 267 | auto *f = cast<ObjFile>(file); |
56 | 267 | f->parse(false); |
57 | 267 | objectFiles.push_back(f); |
58 | 267 | } |
59 | | |
60 | | // This function is where all the optimizations of link-time |
61 | | // optimization happens. When LTO is in use, some input files are |
62 | | // not in native object file format but in the LLVM bitcode format. |
63 | | // This function compiles bitcode files into a few big native files |
64 | | // using LLVM functions and replaces bitcode symbols with the results. |
65 | | // Because all bitcode files that the program consists of are passed |
66 | | // to the compiler at once, it can do whole-program optimization. |
67 | 199 | void SymbolTable::addCombinedLTOObject() { |
68 | 199 | if (bitcodeFiles.empty()) |
69 | 168 | return; |
70 | 31 | |
71 | 31 | // Compile bitcode files and replace bitcode symbols. |
72 | 31 | lto.reset(new BitcodeCompiler); |
73 | 31 | for (BitcodeFile *f : bitcodeFiles) |
74 | 42 | lto->add(*f); |
75 | 31 | |
76 | 48 | for (StringRef filename : lto->compile()) { |
77 | 48 | auto *obj = make<ObjFile>(MemoryBufferRef(filename, "lto.tmp"), ""); |
78 | 48 | obj->parse(true); |
79 | 48 | objectFiles.push_back(obj); |
80 | 48 | } |
81 | 31 | } |
82 | | |
83 | 861 | Symbol *SymbolTable::find(StringRef name) { |
84 | 861 | auto it = symMap.find(CachedHashStringRef(name)); |
85 | 861 | if (it == symMap.end() || it->second == -1283 ) |
86 | 578 | return nullptr; |
87 | 283 | return symVector[it->second]; |
88 | 283 | } |
89 | | |
90 | 8 | void SymbolTable::replace(StringRef name, Symbol* sym) { |
91 | 8 | auto it = symMap.find(CachedHashStringRef(name)); |
92 | 8 | symVector[it->second] = sym; |
93 | 8 | } |
94 | | |
95 | 1.62k | std::pair<Symbol *, bool> SymbolTable::insertName(StringRef name) { |
96 | 1.62k | bool trace = false; |
97 | 1.62k | auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()}); |
98 | 1.62k | int &symIndex = p.first->second; |
99 | 1.62k | bool isNew = p.second; |
100 | 1.62k | if (symIndex == -1) { |
101 | 5 | symIndex = symVector.size(); |
102 | 5 | trace = true; |
103 | 5 | isNew = true; |
104 | 5 | } |
105 | 1.62k | |
106 | 1.62k | if (!isNew) |
107 | 191 | return {symVector[symIndex], false}; |
108 | 1.42k | |
109 | 1.42k | Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); |
110 | 1.42k | sym->isUsedInRegularObj = false; |
111 | 1.42k | sym->canInline = true; |
112 | 1.42k | sym->traced = trace; |
113 | 1.42k | symVector.emplace_back(sym); |
114 | 1.42k | return {sym, true}; |
115 | 1.42k | } |
116 | | |
117 | | std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, |
118 | 888 | const InputFile *file) { |
119 | 888 | Symbol *s; |
120 | 888 | bool wasInserted; |
121 | 888 | std::tie(s, wasInserted) = insertName(name); |
122 | 888 | |
123 | 888 | if (!file || file->kind() == InputFile::ObjectKind866 ) |
124 | 823 | s->isUsedInRegularObj = true; |
125 | 888 | |
126 | 888 | return {s, wasInserted}; |
127 | 888 | } |
128 | | |
129 | | static void reportTypeError(const Symbol *existing, const InputFile *file, |
130 | 1 | llvm::wasm::WasmSymbolType type) { |
131 | 1 | error("symbol type mismatch: " + toString(*existing) + "\n>>> defined as " + |
132 | 1 | toString(existing->getWasmType()) + " in " + |
133 | 1 | toString(existing->getFile()) + "\n>>> defined as " + toString(type) + |
134 | 1 | " in " + toString(file)); |
135 | 1 | } |
136 | | |
137 | | // Check the type of new symbol matches that of the symbol is replacing. |
138 | | // Returns true if the function types match, false is there is a singature |
139 | | // mismatch. |
140 | | static bool signatureMatches(FunctionSymbol *existing, |
141 | 93 | const WasmSignature *newSig) { |
142 | 93 | const WasmSignature *oldSig = existing->signature; |
143 | 93 | |
144 | 93 | // If either function is missing a signature (this happend for bitcode |
145 | 93 | // symbols) then assume they match. Any mismatch will be reported later |
146 | 93 | // when the LTO objects are added. |
147 | 93 | if (!newSig || !oldSig92 ) |
148 | 40 | return true; |
149 | 53 | |
150 | 53 | return *newSig == *oldSig; |
151 | 53 | } |
152 | | |
153 | | static void checkGlobalType(const Symbol *existing, const InputFile *file, |
154 | 8 | const WasmGlobalType *newType) { |
155 | 8 | if (!isa<GlobalSymbol>(existing)) { |
156 | 0 | reportTypeError(existing, file, WASM_SYMBOL_TYPE_GLOBAL); |
157 | 0 | return; |
158 | 0 | } |
159 | 8 | |
160 | 8 | const WasmGlobalType *oldType = cast<GlobalSymbol>(existing)->getGlobalType(); |
161 | 8 | if (*newType != *oldType) { |
162 | 0 | error("Global type mismatch: " + existing->getName() + "\n>>> defined as " + |
163 | 0 | toString(*oldType) + " in " + toString(existing->getFile()) + |
164 | 0 | "\n>>> defined as " + toString(*newType) + " in " + toString(file)); |
165 | 0 | } |
166 | 8 | } |
167 | | |
168 | | static void checkEventType(const Symbol *existing, const InputFile *file, |
169 | | const WasmEventType *newType, |
170 | 1 | const WasmSignature *newSig) { |
171 | 1 | auto existingEvent = dyn_cast<EventSymbol>(existing); |
172 | 1 | if (!isa<EventSymbol>(existing)) { |
173 | 0 | reportTypeError(existing, file, WASM_SYMBOL_TYPE_EVENT); |
174 | 0 | return; |
175 | 0 | } |
176 | 1 | |
177 | 1 | const WasmEventType *oldType = cast<EventSymbol>(existing)->getEventType(); |
178 | 1 | const WasmSignature *oldSig = existingEvent->signature; |
179 | 1 | if (newType->Attribute != oldType->Attribute) |
180 | 0 | error("Event type mismatch: " + existing->getName() + "\n>>> defined as " + |
181 | 0 | toString(*oldType) + " in " + toString(existing->getFile()) + |
182 | 0 | "\n>>> defined as " + toString(*newType) + " in " + toString(file)); |
183 | 1 | if (*newSig != *oldSig) |
184 | 0 | warn("Event signature mismatch: " + existing->getName() + |
185 | 0 | "\n>>> defined as " + toString(*oldSig) + " in " + |
186 | 0 | toString(existing->getFile()) + "\n>>> defined as " + |
187 | 0 | toString(*newSig) + " in " + toString(file)); |
188 | 1 | } |
189 | | |
190 | 14 | static void checkDataType(const Symbol *existing, const InputFile *file) { |
191 | 14 | if (!isa<DataSymbol>(existing)) |
192 | 0 | reportTypeError(existing, file, WASM_SYMBOL_TYPE_DATA); |
193 | 14 | } |
194 | | |
195 | | DefinedFunction *SymbolTable::addSyntheticFunction(StringRef name, |
196 | | uint32_t flags, |
197 | 229 | InputFunction *function) { |
198 | 229 | LLVM_DEBUG(dbgs() << "addSyntheticFunction: " << name << "\n"); |
199 | 229 | assert(!find(name)); |
200 | 229 | syntheticFunctions.emplace_back(function); |
201 | 229 | return replaceSymbol<DefinedFunction>(insertName(name).first, name, |
202 | 229 | flags, nullptr, function); |
203 | 229 | } |
204 | | |
205 | | // Adds an optional, linker generated, data symbols. The symbol will only be |
206 | | // added if there is an undefine reference to it, or if it is explictly exported |
207 | | // via the --export flag. Otherwise we don't add the symbol and return nullptr. |
208 | | DefinedData *SymbolTable::addOptionalDataSymbol(StringRef name, uint32_t value, |
209 | 568 | uint32_t flags) { |
210 | 568 | Symbol *s = find(name); |
211 | 568 | if (!s && (566 config->exportAll566 || config->exportedSymbols.count(name) != 0559 )) |
212 | 15 | s = insertName(name).first; |
213 | 553 | else if (!s || s->isDefined()2 ) |
214 | 551 | return nullptr; |
215 | 17 | LLVM_DEBUG(dbgs() << "addOptionalDataSymbol: " << name << "\n"); |
216 | 17 | auto *rtn = replaceSymbol<DefinedData>(s, name, flags); |
217 | 17 | rtn->setVirtualAddress(value); |
218 | 17 | rtn->referenced = true; |
219 | 17 | return rtn; |
220 | 17 | } |
221 | | |
222 | | DefinedData *SymbolTable::addSyntheticDataSymbol(StringRef name, |
223 | 194 | uint32_t flags) { |
224 | 194 | LLVM_DEBUG(dbgs() << "addSyntheticDataSymbol: " << name << "\n"); |
225 | 194 | assert(!find(name)); |
226 | 194 | return replaceSymbol<DefinedData>(insertName(name).first, name, flags); |
227 | 194 | } |
228 | | |
229 | | DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags, |
230 | 242 | InputGlobal *global) { |
231 | 242 | LLVM_DEBUG(dbgs() << "addSyntheticGlobal: " << name << " -> " << global |
232 | 242 | << "\n"); |
233 | 242 | assert(!find(name)); |
234 | 242 | syntheticGlobals.emplace_back(global); |
235 | 242 | return replaceSymbol<DefinedGlobal>(insertName(name).first, name, flags, |
236 | 242 | nullptr, global); |
237 | 242 | } |
238 | | |
239 | | static bool shouldReplace(const Symbol *existing, InputFile *newFile, |
240 | 93 | uint32_t newFlags) { |
241 | 93 | // If existing symbol is undefined, replace it. |
242 | 93 | if (!existing->isDefined()) { |
243 | 84 | LLVM_DEBUG(dbgs() << "resolving existing undefined symbol: " |
244 | 84 | << existing->getName() << "\n"); |
245 | 84 | return true; |
246 | 84 | } |
247 | 9 | |
248 | 9 | // Now we have two defined symbols. If the new one is weak, we can ignore it. |
249 | 9 | if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) { |
250 | 6 | LLVM_DEBUG(dbgs() << "existing symbol takes precedence\n"); |
251 | 6 | return false; |
252 | 6 | } |
253 | 3 | |
254 | 3 | // If the existing symbol is weak, we should replace it. |
255 | 3 | if (existing->isWeak()) { |
256 | 0 | LLVM_DEBUG(dbgs() << "replacing existing weak symbol\n"); |
257 | 0 | return true; |
258 | 0 | } |
259 | 3 | |
260 | 3 | // Neither symbol is week. They conflict. |
261 | 3 | error("duplicate symbol: " + toString(*existing) + "\n>>> defined in " + |
262 | 3 | toString(existing->getFile()) + "\n>>> defined in " + |
263 | 3 | toString(newFile)); |
264 | 3 | return true; |
265 | 3 | } |
266 | | |
267 | | Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags, |
268 | | InputFile *file, |
269 | 543 | InputFunction *function) { |
270 | 543 | LLVM_DEBUG(dbgs() << "addDefinedFunction: " << name << " [" |
271 | 543 | << (function ? toString(function->signature) : "none") |
272 | 543 | << "]\n"); |
273 | 543 | Symbol *s; |
274 | 543 | bool wasInserted; |
275 | 543 | std::tie(s, wasInserted) = insert(name, file); |
276 | 543 | |
277 | 543 | auto replaceSym = [&](Symbol *sym) { |
278 | 538 | // If the new defined function doesn't have signture (i.e. bitcode |
279 | 538 | // functions) but the old symbol does, then preserve the old signature |
280 | 538 | const WasmSignature *oldSig = s->getSignature(); |
281 | 538 | auto* newSym = replaceSymbol<DefinedFunction>(sym, name, flags, file, function); |
282 | 538 | if (!newSym->signature) |
283 | 45 | newSym->signature = oldSig; |
284 | 538 | }; |
285 | 543 | |
286 | 543 | if (wasInserted || s->isLazy()109 ) { |
287 | 451 | replaceSym(s); |
288 | 451 | return s; |
289 | 451 | } |
290 | 92 | |
291 | 92 | auto existingFunction = dyn_cast<FunctionSymbol>(s); |
292 | 92 | if (!existingFunction) { |
293 | 1 | reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION); |
294 | 1 | return s; |
295 | 1 | } |
296 | 91 | |
297 | 91 | bool checkSig = true; |
298 | 91 | if (auto ud = dyn_cast<UndefinedFunction>(existingFunction)) |
299 | 84 | checkSig = ud->isCalledDirectly; |
300 | 91 | |
301 | 91 | if (checkSig && function86 && !signatureMatches(existingFunction, &function->signature)74 ) { |
302 | 8 | Symbol* variant; |
303 | 8 | if (getFunctionVariant(s, &function->signature, file, &variant)) |
304 | 7 | // New variant, always replace |
305 | 7 | replaceSym(variant); |
306 | 1 | else if (shouldReplace(s, file, flags)) |
307 | 0 | // Variant already exists, replace it after checking shouldReplace |
308 | 0 | replaceSym(variant); |
309 | 8 | |
310 | 8 | // This variant we found take the place in the symbol table as the primary |
311 | 8 | // variant. |
312 | 8 | replace(name, variant); |
313 | 8 | return variant; |
314 | 8 | } |
315 | 83 | |
316 | 83 | // Existing function with matching signature. |
317 | 83 | if (shouldReplace(s, file, flags)) |
318 | 80 | replaceSym(s); |
319 | 83 | |
320 | 83 | return s; |
321 | 83 | } |
322 | | |
323 | | Symbol *SymbolTable::addDefinedData(StringRef name, uint32_t flags, |
324 | | InputFile *file, InputSegment *segment, |
325 | 151 | uint32_t address, uint32_t size) { |
326 | 151 | LLVM_DEBUG(dbgs() << "addDefinedData:" << name << " addr:" << address |
327 | 151 | << "\n"); |
328 | 151 | Symbol *s; |
329 | 151 | bool wasInserted; |
330 | 151 | std::tie(s, wasInserted) = insert(name, file); |
331 | 151 | |
332 | 151 | auto replaceSym = [&]() { |
333 | 150 | replaceSymbol<DefinedData>(s, name, flags, file, segment, address, size); |
334 | 150 | }; |
335 | 151 | |
336 | 151 | if (wasInserted || s->isLazy()8 ) { |
337 | 143 | replaceSym(); |
338 | 143 | return s; |
339 | 143 | } |
340 | 8 | |
341 | 8 | checkDataType(s, file); |
342 | 8 | |
343 | 8 | if (shouldReplace(s, file, flags)) |
344 | 7 | replaceSym(); |
345 | 8 | return s; |
346 | 8 | } |
347 | | |
348 | | Symbol *SymbolTable::addDefinedGlobal(StringRef name, uint32_t flags, |
349 | 4 | InputFile *file, InputGlobal *global) { |
350 | 4 | LLVM_DEBUG(dbgs() << "addDefinedGlobal:" << name << "\n"); |
351 | 4 | |
352 | 4 | Symbol *s; |
353 | 4 | bool wasInserted; |
354 | 4 | std::tie(s, wasInserted) = insert(name, file); |
355 | 4 | |
356 | 4 | auto replaceSym = [&]() { |
357 | 4 | replaceSymbol<DefinedGlobal>(s, name, flags, file, global); |
358 | 4 | }; |
359 | 4 | |
360 | 4 | if (wasInserted || s->isLazy()0 ) { |
361 | 4 | replaceSym(); |
362 | 4 | return s; |
363 | 4 | } |
364 | 0 | |
365 | 0 | checkGlobalType(s, file, &global->getType()); |
366 | 0 |
|
367 | 0 | if (shouldReplace(s, file, flags)) |
368 | 0 | replaceSym(); |
369 | 0 | return s; |
370 | 0 | } |
371 | | |
372 | | Symbol *SymbolTable::addDefinedEvent(StringRef name, uint32_t flags, |
373 | 2 | InputFile *file, InputEvent *event) { |
374 | 2 | LLVM_DEBUG(dbgs() << "addDefinedEvent:" << name << "\n"); |
375 | 2 | |
376 | 2 | Symbol *s; |
377 | 2 | bool wasInserted; |
378 | 2 | std::tie(s, wasInserted) = insert(name, file); |
379 | 2 | |
380 | 2 | auto replaceSym = [&]() { |
381 | 1 | replaceSymbol<DefinedEvent>(s, name, flags, file, event); |
382 | 1 | }; |
383 | 2 | |
384 | 2 | if (wasInserted || s->isLazy()1 ) { |
385 | 1 | replaceSym(); |
386 | 1 | return s; |
387 | 1 | } |
388 | 1 | |
389 | 1 | checkEventType(s, file, &event->getType(), &event->signature); |
390 | 1 | |
391 | 1 | if (shouldReplace(s, file, flags)) |
392 | 0 | replaceSym(); |
393 | 1 | return s; |
394 | 1 | } |
395 | | |
396 | | Symbol *SymbolTable::addUndefinedFunction(StringRef name, StringRef importName, |
397 | | StringRef importModule, |
398 | | uint32_t flags, InputFile *file, |
399 | | const WasmSignature *sig, |
400 | 124 | bool isCalledDirectly) { |
401 | 124 | LLVM_DEBUG(dbgs() << "addUndefinedFunction: " << name << " [" |
402 | 124 | << (sig ? toString(*sig) : "none") |
403 | 124 | << "] IsCalledDirectly:" << isCalledDirectly << "\n"); |
404 | 124 | |
405 | 124 | Symbol *s; |
406 | 124 | bool wasInserted; |
407 | 124 | std::tie(s, wasInserted) = insert(name, file); |
408 | 124 | if (s->traced) |
409 | 2 | printTraceSymbolUndefined(name, file); |
410 | 124 | |
411 | 124 | auto replaceSym = [&]() { |
412 | 88 | replaceSymbol<UndefinedFunction>(s, name, importName, importModule, flags, |
413 | 88 | file, sig, isCalledDirectly); |
414 | 88 | }; |
415 | 124 | |
416 | 124 | if (wasInserted) |
417 | 88 | replaceSym(); |
418 | 36 | else if (auto *lazy = dyn_cast<LazySymbol>(s)) |
419 | 9 | lazy->fetch(); |
420 | 27 | else { |
421 | 27 | auto existingFunction = dyn_cast<FunctionSymbol>(s); |
422 | 27 | if (!existingFunction) { |
423 | 0 | reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION); |
424 | 0 | return s; |
425 | 0 | } |
426 | 27 | if (!existingFunction->signature && sig5 ) |
427 | 5 | existingFunction->signature = sig; |
428 | 27 | if (isCalledDirectly && !signatureMatches(existingFunction, sig)19 ) |
429 | 0 | if (getFunctionVariant(s, sig, file, &s)) |
430 | 0 | replaceSym(); |
431 | 27 | } |
432 | 124 | |
433 | 124 | return s; |
434 | 124 | } |
435 | | |
436 | | Symbol *SymbolTable::addUndefinedData(StringRef name, uint32_t flags, |
437 | 26 | InputFile *file) { |
438 | 26 | LLVM_DEBUG(dbgs() << "addUndefinedData: " << name << "\n"); |
439 | 26 | |
440 | 26 | Symbol *s; |
441 | 26 | bool wasInserted; |
442 | 26 | std::tie(s, wasInserted) = insert(name, file); |
443 | 26 | if (s->traced) |
444 | 0 | printTraceSymbolUndefined(name, file); |
445 | 26 | |
446 | 26 | if (wasInserted) |
447 | 18 | replaceSymbol<UndefinedData>(s, name, flags, file); |
448 | 8 | else if (auto *lazy = dyn_cast<LazySymbol>(s)) |
449 | 0 | lazy->fetch(); |
450 | 8 | else if (s->isDefined()) |
451 | 6 | checkDataType(s, file); |
452 | 26 | return s; |
453 | 26 | } |
454 | | |
455 | | Symbol *SymbolTable::addUndefinedGlobal(StringRef name, StringRef importName, |
456 | | StringRef importModule, uint32_t flags, |
457 | | InputFile *file, |
458 | 38 | const WasmGlobalType *type) { |
459 | 38 | LLVM_DEBUG(dbgs() << "addUndefinedGlobal: " << name << "\n"); |
460 | 38 | |
461 | 38 | Symbol *s; |
462 | 38 | bool wasInserted; |
463 | 38 | std::tie(s, wasInserted) = insert(name, file); |
464 | 38 | if (s->traced) |
465 | 0 | printTraceSymbolUndefined(name, file); |
466 | 38 | |
467 | 38 | if (wasInserted) |
468 | 26 | replaceSymbol<UndefinedGlobal>(s, name, importName, importModule, flags, |
469 | 26 | file, type); |
470 | 12 | else if (auto *lazy = dyn_cast<LazySymbol>(s)) |
471 | 0 | lazy->fetch(); |
472 | 12 | else if (s->isDefined()) |
473 | 8 | checkGlobalType(s, file, type); |
474 | 38 | return s; |
475 | 38 | } |
476 | | |
477 | 52 | void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) { |
478 | 52 | LLVM_DEBUG(dbgs() << "addLazy: " << sym->getName() << "\n"); |
479 | 52 | StringRef name = sym->getName(); |
480 | 52 | |
481 | 52 | Symbol *s; |
482 | 52 | bool wasInserted; |
483 | 52 | std::tie(s, wasInserted) = insertName(name); |
484 | 52 | |
485 | 52 | if (wasInserted) { |
486 | 35 | replaceSymbol<LazySymbol>(s, name, 0, file, *sym); |
487 | 35 | return; |
488 | 35 | } |
489 | 17 | |
490 | 17 | if (!s->isUndefined()) |
491 | 13 | return; |
492 | 4 | |
493 | 4 | // The existing symbol is undefined, load a new one from the archive, |
494 | 4 | // unless the the existing symbol is weak in which case replace the undefined |
495 | 4 | // symbols with a LazySymbol. |
496 | 4 | if (s->isWeak()) { |
497 | 1 | const WasmSignature *oldSig = nullptr; |
498 | 1 | // In the case of an UndefinedFunction we need to preserve the expected |
499 | 1 | // signature. |
500 | 1 | if (auto *f = dyn_cast<UndefinedFunction>(s)) |
501 | 1 | oldSig = f->signature; |
502 | 1 | LLVM_DEBUG(dbgs() << "replacing existing weak undefined symbol\n"); |
503 | 1 | auto newSym = replaceSymbol<LazySymbol>(s, name, WASM_SYMBOL_BINDING_WEAK, |
504 | 1 | file, *sym); |
505 | 1 | newSym->signature = oldSig; |
506 | 1 | return; |
507 | 1 | } |
508 | 3 | |
509 | 3 | LLVM_DEBUG(dbgs() << "replacing existing undefined\n"); |
510 | 3 | file->addMember(sym); |
511 | 3 | } |
512 | | |
513 | 7 | bool SymbolTable::addComdat(StringRef name) { |
514 | 7 | return comdatGroups.insert(CachedHashStringRef(name)).second; |
515 | 7 | } |
516 | | |
517 | | // The new signature doesn't match. Create a variant to the symbol with the |
518 | | // signature encoded in the name and return that instead. These symbols are |
519 | | // then unified later in handleSymbolVariants. |
520 | | bool SymbolTable::getFunctionVariant(Symbol* sym, const WasmSignature *sig, |
521 | 8 | const InputFile *file, Symbol **out) { |
522 | 8 | LLVM_DEBUG(dbgs() << "getFunctionVariant: " << sym->getName() << " -> " |
523 | 8 | << " " << toString(*sig) << "\n"); |
524 | 8 | Symbol *variant = nullptr; |
525 | 8 | |
526 | 8 | // Linear search through symbol variants. Should never be more than two |
527 | 8 | // or three entries here. |
528 | 8 | auto &variants = symVariants[CachedHashStringRef(sym->getName())]; |
529 | 8 | if (variants.empty()) |
530 | 7 | variants.push_back(sym); |
531 | 8 | |
532 | 8 | for (Symbol* v : variants) { |
533 | 8 | if (*v->getSignature() == *sig) { |
534 | 1 | variant = v; |
535 | 1 | break; |
536 | 1 | } |
537 | 8 | } |
538 | 8 | |
539 | 8 | bool wasAdded = !variant; |
540 | 8 | if (wasAdded) { |
541 | 7 | // Create a new variant; |
542 | 7 | LLVM_DEBUG(dbgs() << "added new variant\n"); |
543 | 7 | variant = reinterpret_cast<Symbol *>(make<SymbolUnion>()); |
544 | 7 | variants.push_back(variant); |
545 | 7 | } else { |
546 | 1 | LLVM_DEBUG(dbgs() << "variant already exists: " << toString(*variant) << "\n"); |
547 | 1 | assert(*variant->getSignature() == *sig); |
548 | 1 | } |
549 | 8 | |
550 | 8 | *out = variant; |
551 | 8 | return wasAdded; |
552 | 8 | } |
553 | | |
554 | | // Set a flag for --trace-symbol so that we can print out a log message |
555 | | // if a new symbol with the same name is inserted into the symbol table. |
556 | 5 | void SymbolTable::trace(StringRef name) { |
557 | 5 | symMap.insert({CachedHashStringRef(name), -1}); |
558 | 5 | } |
559 | | |
560 | 2 | void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { |
561 | 2 | // Swap symbols as instructed by -wrap. |
562 | 2 | int &origIdx = symMap[CachedHashStringRef(sym->getName())]; |
563 | 2 | int &realIdx= symMap[CachedHashStringRef(real->getName())]; |
564 | 2 | int &wrapIdx = symMap[CachedHashStringRef(wrap->getName())]; |
565 | 2 | LLVM_DEBUG(dbgs() << "wrap: " << sym->getName() << "\n"); |
566 | 2 | |
567 | 2 | // Anyone looking up __real symbols should get the original |
568 | 2 | realIdx = origIdx; |
569 | 2 | // Anyone looking up the original should get the __wrap symbol |
570 | 2 | origIdx = wrapIdx; |
571 | 2 | } |
572 | | |
573 | | static const uint8_t unreachableFn[] = { |
574 | | 0x03 /* ULEB length */, 0x00 /* ULEB num locals */, |
575 | | 0x00 /* opcode unreachable */, 0x0b /* opcode end */ |
576 | | }; |
577 | | |
578 | | // Replace the given symbol body with an unreachable function. |
579 | | // This is used by handleWeakUndefines in order to generate a callable |
580 | | // equivalent of an undefined function and also handleSymbolVariants for |
581 | | // undefined functions that don't match the signature of the definition. |
582 | | InputFunction *SymbolTable::replaceWithUnreachable(Symbol *sym, |
583 | | const WasmSignature &sig, |
584 | 16 | StringRef debugName) { |
585 | 16 | auto *func = make<SyntheticFunction>(sig, sym->getName(), debugName); |
586 | 16 | func->setBody(unreachableFn); |
587 | 16 | syntheticFunctions.emplace_back(func); |
588 | 16 | replaceSymbol<DefinedFunction>(sym, sym->getName(), sym->getFlags(), nullptr, |
589 | 16 | func); |
590 | 16 | return func; |
591 | 16 | } |
592 | | |
593 | | // For weak undefined functions, there may be "call" instructions that reference |
594 | | // the symbol. In this case, we need to synthesise a dummy/stub function that |
595 | | // will abort at runtime, so that relocations can still provided an operand to |
596 | | // the call instruction that passes Wasm validation. |
597 | 175 | void SymbolTable::handleWeakUndefines() { |
598 | 1.11k | for (Symbol *sym : getSymbols()) { |
599 | 1.11k | if (!sym->isUndefWeak()) |
600 | 1.10k | continue; |
601 | 10 | |
602 | 10 | const WasmSignature *sig = sym->getSignature(); |
603 | 10 | if (!sig) { |
604 | 1 | // It is possible for undefined functions not to have a signature (eg. if |
605 | 1 | // added via "--undefined"), but weak undefined ones do have a signature. |
606 | 1 | // Lazy symbols may not be functions and therefore Sig can still be null |
607 | 1 | // in some circumstantce. |
608 | 1 | assert(!isa<FunctionSymbol>(sym)); |
609 | 1 | continue; |
610 | 1 | } |
611 | 9 | |
612 | 9 | // Add a synthetic dummy for weak undefined functions. These dummies will |
613 | 9 | // be GC'd if not used as the target of any "call" instructions. |
614 | 9 | StringRef debugName = saver.save("undefined:" + toString(*sym)); |
615 | 9 | InputFunction* func = replaceWithUnreachable(sym, *sig, debugName); |
616 | 9 | // Ensure it compares equal to the null pointer, and so that table relocs |
617 | 9 | // don't pull in the stub body (only call-operand relocs should do that). |
618 | 9 | func->setTableIndex(0); |
619 | 9 | // Hide our dummy to prevent export. |
620 | 9 | sym->setHidden(true); |
621 | 9 | } |
622 | 175 | } |
623 | | |
624 | | static void reportFunctionSignatureMismatch(StringRef symName, |
625 | | FunctionSymbol *a, |
626 | 7 | FunctionSymbol *b, bool isError) { |
627 | 7 | std::string msg = ("function signature mismatch: " + symName + |
628 | 7 | "\n>>> defined as " + toString(*a->signature) + " in " + |
629 | 7 | toString(a->getFile()) + "\n>>> defined as " + |
630 | 7 | toString(*b->signature) + " in " + toString(b->getFile())) |
631 | 7 | .str(); |
632 | 7 | if (isError) |
633 | 0 | error(msg); |
634 | 7 | else |
635 | 7 | warn(msg); |
636 | 7 | } |
637 | | |
638 | | // Remove any variant symbols that were created due to function signature |
639 | | // mismatches. |
640 | 198 | void SymbolTable::handleSymbolVariants() { |
641 | 198 | for (auto pair : symVariants) { |
642 | 7 | // Push the initial symbol onto the list of variants. |
643 | 7 | StringRef symName = pair.first.val(); |
644 | 7 | std::vector<Symbol *> &variants = pair.second; |
645 | 7 | |
646 | | #ifndef NDEBUG |
647 | | LLVM_DEBUG(dbgs() << "symbol with (" << variants.size() |
648 | | << ") variants: " << symName << "\n"); |
649 | | for (auto *s: variants) { |
650 | | auto *f = cast<FunctionSymbol>(s); |
651 | | LLVM_DEBUG(dbgs() << " variant: " + f->getName() << " " |
652 | | << toString(*f->signature) << "\n"); |
653 | | } |
654 | | #endif |
655 | | |
656 | 7 | // Find the one definition. |
657 | 7 | DefinedFunction *defined = nullptr; |
658 | 14 | for (auto *symbol : variants) { |
659 | 14 | if (auto f = dyn_cast<DefinedFunction>(symbol)) { |
660 | 7 | defined = f; |
661 | 7 | break; |
662 | 7 | } |
663 | 14 | } |
664 | 7 | |
665 | 7 | // If there are no definitions, and the undefined symbols disagree on |
666 | 7 | // the signature, there is not we can do since we don't know which one |
667 | 7 | // to use as the signature on the import. |
668 | 7 | if (!defined) { |
669 | 0 | reportFunctionSignatureMismatch(symName, |
670 | 0 | cast<FunctionSymbol>(variants[0]), |
671 | 0 | cast<FunctionSymbol>(variants[1]), true); |
672 | 0 | return; |
673 | 0 | } |
674 | 7 | |
675 | 14 | for (auto *symbol : variants)7 { |
676 | 14 | if (symbol != defined) { |
677 | 7 | auto *f = cast<FunctionSymbol>(symbol); |
678 | 7 | reportFunctionSignatureMismatch(symName, f, defined, false); |
679 | 7 | StringRef debugName = saver.save("unreachable:" + toString(*f)); |
680 | 7 | replaceWithUnreachable(f, *f->signature, debugName); |
681 | 7 | } |
682 | 14 | } |
683 | 7 | } |
684 | 198 | } |