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 "InputElement.h"
13#include "WriterUtils.h"
14#include "lld/Common/CommonLinkerContext.h"
15#include <optional>
16
17#define DEBUG_TYPE "lld"
18
19using namespace llvm;
20using namespace llvm::wasm;
21using namespace llvm::object;
22
23namespace lld::wasm {
24SymbolTable *symtab;
25
26void SymbolTable::addFile(InputFile *file, StringRef symName) {
27 log(msg: "Processing: " + toString(file));
28
29 // Lazy object file
30 if (file->lazy) {
31 if (auto *f = dyn_cast<BitcodeFile>(Val: file)) {
32 ctx.lazyBitcodeFiles.push_back(Elt: f);
33 f->parseLazy();
34 } else {
35 cast<ObjFile>(Val: file)->parseLazy();
36 }
37 return;
38 }
39
40 // .so file
41 if (auto *f = dyn_cast<SharedFile>(Val: file)) {
42 // If we are not reporting undefined symbols that we don't actually
43 // parse the shared library symbol table.
44 f->parse();
45 ctx.sharedFiles.push_back(Elt: f);
46 return;
47 }
48
49 // stub file
50 if (auto *f = dyn_cast<StubFile>(Val: file)) {
51 f->parse();
52 ctx.stubFiles.push_back(Elt: f);
53 return;
54 }
55
56 if (ctx.arg.trace)
57 message(msg: toString(file));
58
59 // LLVM bitcode file
60 if (auto *f = dyn_cast<BitcodeFile>(Val: file)) {
61 // This order, first adding to `bitcodeFiles` and then parsing is necessary.
62 // See https://github.com/llvm/llvm-project/pull/73095
63 ctx.bitcodeFiles.push_back(Elt: f);
64 f->parse(symName);
65 return;
66 }
67
68 // Regular object file
69 auto *f = cast<ObjFile>(Val: file);
70 f->parse(ignoreComdats: false);
71 ctx.objectFiles.push_back(Elt: f);
72}
73
74// This function is where all the optimizations of link-time
75// optimization happens. When LTO is in use, some input files are
76// not in native object file format but in the LLVM bitcode format.
77// This function compiles bitcode files into a few big native files
78// using LLVM functions and replaces bitcode symbols with the results.
79// Because all bitcode files that the program consists of are passed
80// to the compiler at once, it can do whole-program optimization.
81void SymbolTable::compileBitcodeFiles() {
82 // Prevent further LTO objects being included
83 BitcodeFile::doneLTO = true;
84
85 // Collect the bitcode library functions that are not safe to call because
86 // they were not yet brought in the link. (Such symbols are lazy.)
87 llvm::BumpPtrAllocator alloc;
88 llvm::StringSaver saver(alloc);
89 SmallVector<StringRef> bitcodeLibFuncs;
90 if (!ctx.bitcodeFiles.empty()) {
91 // Triple must be captured before the bitcode is moved into the compiler.
92 // Note that the below assumes that the set of possible libfuncs is
93 // equivalent for all bitcode translation units.
94 llvm::Triple tt =
95 llvm::Triple(ctx.bitcodeFiles.front()->obj->getTargetTriple());
96 for (StringRef libFunc : llvm::lto::LTO::getLibFuncSymbols(TT: tt, Saver&: saver)) {
97 if (Symbol *sym = find(name: libFunc)) {
98 if (auto *lazy = dyn_cast<LazySymbol>(Val: sym)) {
99 if (isa<BitcodeFile>(Val: lazy->getFile()))
100 bitcodeLibFuncs.push_back(Elt: libFunc);
101 }
102 }
103 }
104 }
105
106 // Compile bitcode files and replace bitcode symbols.
107 lto.reset(p: new BitcodeCompiler);
108 lto->setBitcodeLibFuncs(bitcodeLibFuncs);
109
110 for (BitcodeFile *f : ctx.bitcodeFiles)
111 lto->add(f&: *f);
112
113 for (auto &file : lto->compile()) {
114 auto *obj = cast<ObjFile>(Val: file);
115 obj->parse(ignoreComdats: true);
116 ctx.objectFiles.push_back(Elt: obj);
117 }
118}
119
120Symbol *SymbolTable::find(StringRef name) {
121 auto it = symMap.find(Val: CachedHashStringRef(name));
122 if (it == symMap.end() || it->second == -1)
123 return nullptr;
124 return symVector[it->second];
125}
126
127void SymbolTable::replace(StringRef name, Symbol *sym) {
128 auto it = symMap.find(Val: CachedHashStringRef(name));
129 symVector[it->second] = sym;
130}
131
132std::pair<Symbol *, bool> SymbolTable::insertName(StringRef name) {
133 bool trace = false;
134 auto p = symMap.insert(KV: {CachedHashStringRef(name), (int)symVector.size()});
135 int &symIndex = p.first->second;
136 bool isNew = p.second;
137 if (symIndex == -1) {
138 symIndex = symVector.size();
139 trace = true;
140 isNew = true;
141 }
142
143 if (!isNew)
144 return {symVector[symIndex], false};
145
146 Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
147 sym->isUsedInRegularObj = false;
148 sym->canInline = true;
149 sym->traced = trace;
150 sym->forceExport = false;
151 sym->referenced = !ctx.arg.gcSections;
152 symVector.emplace_back(args&: sym);
153 return {sym, true};
154}
155
156std::pair<Symbol *, bool> SymbolTable::insert(StringRef name,
157 const InputFile *file) {
158 Symbol *s;
159 bool wasInserted;
160 std::tie(args&: s, args&: wasInserted) = insertName(name);
161
162 if (!file || file->kind() == InputFile::ObjectKind)
163 s->isUsedInRegularObj = true;
164
165 return {s, wasInserted};
166}
167
168static bool isBitcodeSymbol(const Symbol *symbol) {
169 return symbol->getFile() &&
170 symbol->getFile()->kind() == InputFile::BitcodeKind;
171}
172
173static void reportTypeError(const Symbol *existing, const InputFile *file,
174 llvm::wasm::WasmSymbolType type) {
175 error(msg: "symbol type mismatch: " + toString(sym: *existing) + "\n>>> defined as " +
176 toString(type: existing->getWasmType()) + " in " +
177 toString(file: existing->getFile()) + "\n>>> defined as " + toString(type) +
178 " in " + toString(file));
179}
180
181// Check the type of new symbol matches that of the symbol is replacing.
182// Returns true if the function types match, false is there is a signature
183// mismatch.
184static bool signatureMatches(FunctionSymbol *existing,
185 const WasmSignature *newSig) {
186 const WasmSignature *oldSig = existing->signature;
187
188 // If either function is missing a signature (this happens for bitcode
189 // symbols) then assume they match. Any mismatch will be reported later
190 // when the LTO objects are added.
191 if (!newSig || !oldSig)
192 return true;
193
194 return *newSig == *oldSig;
195}
196
197static void checkGlobalType(const Symbol *existing, const InputFile *file,
198 const WasmGlobalType *newType) {
199 if (!isa<GlobalSymbol>(Val: existing)) {
200 if (isBitcodeSymbol(symbol: existing))
201 return;
202 reportTypeError(existing, file, type: WASM_SYMBOL_TYPE_GLOBAL);
203 return;
204 }
205
206 const WasmGlobalType *oldType = cast<GlobalSymbol>(Val: existing)->getGlobalType();
207 if (*newType != *oldType) {
208 error(msg: "Global type mismatch: " + existing->getName() + "\n>>> defined as " +
209 toString(type: *oldType) + " in " + toString(file: existing->getFile()) +
210 "\n>>> defined as " + toString(type: *newType) + " in " + toString(file));
211 }
212}
213
214static void checkTagType(const Symbol *existing, const InputFile *file,
215 const WasmSignature *newSig) {
216 if (!isa<TagSymbol>(Val: existing)) {
217 if (isBitcodeSymbol(symbol: existing))
218 return;
219 reportTypeError(existing, file, type: WASM_SYMBOL_TYPE_TAG);
220 return;
221 }
222
223 const auto *existingTag = cast<TagSymbol>(Val: existing);
224
225 const WasmSignature *oldSig = existingTag->signature;
226 if (*newSig != *oldSig)
227 warn(msg: "Tag signature mismatch: " + existing->getName() +
228 "\n>>> defined as " + toString(sig: *oldSig) + " in " +
229 toString(file: existing->getFile()) + "\n>>> defined as " +
230 toString(sig: *newSig) + " in " + toString(file));
231}
232
233static void checkTableType(const Symbol *existing, const InputFile *file,
234 const WasmTableType *newType) {
235 if (!isa<TableSymbol>(Val: existing)) {
236 if (isBitcodeSymbol(symbol: existing))
237 return;
238 reportTypeError(existing, file, type: WASM_SYMBOL_TYPE_TABLE);
239 return;
240 }
241
242 const WasmTableType *oldType = cast<TableSymbol>(Val: existing)->getTableType();
243 if (newType->ElemType != oldType->ElemType) {
244 error(msg: "Table type mismatch: " + existing->getName() + "\n>>> defined as " +
245 toString(type: *oldType) + " in " + toString(file: existing->getFile()) +
246 "\n>>> defined as " + toString(type: *newType) + " in " + toString(file));
247 }
248 // FIXME: No assertions currently on the limits.
249}
250
251static void checkDataType(const Symbol *existing, const InputFile *file) {
252 if (!isa<DataSymbol>(Val: existing) && !isBitcodeSymbol(symbol: existing))
253 reportTypeError(existing, file, type: WASM_SYMBOL_TYPE_DATA);
254}
255
256DefinedFunction *SymbolTable::addSyntheticFunction(StringRef name,
257 uint32_t flags,
258 InputFunction *function) {
259 LLVM_DEBUG(dbgs() << "addSyntheticFunction: " << name << "\n");
260 assert(!find(name));
261 ctx.syntheticFunctions.emplace_back(Args&: function);
262 return replaceSymbol<DefinedFunction>(s: insertName(name).first, arg&: name, arg&: flags,
263 arg: nullptr, arg&: function);
264}
265
266// Adds an optional, linker generated, data symbol. The symbol will only be
267// added if there is an undefine reference to it, or if it is explicitly
268// exported via the --export flag. Otherwise we don't add the symbol and return
269// nullptr.
270DefinedData *SymbolTable::addOptionalDataSymbol(StringRef name,
271 uint64_t value) {
272 Symbol *s = find(name);
273 if (!s && (ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(key: name)))
274 s = insertName(name).first;
275 else if (!s || s->isDefined())
276 return nullptr;
277 LLVM_DEBUG(dbgs() << "addOptionalDataSymbol: " << name << "\n");
278 auto *rtn = replaceSymbol<DefinedData>(
279 s, arg&: name, arg: WASM_SYMBOL_VISIBILITY_HIDDEN | WASM_SYMBOL_ABSOLUTE);
280 rtn->setVA(value);
281 rtn->referenced = true;
282 return rtn;
283}
284
285DefinedData *SymbolTable::addSyntheticDataSymbol(StringRef name,
286 uint32_t flags) {
287 LLVM_DEBUG(dbgs() << "addSyntheticDataSymbol: " << name << "\n");
288 assert(!find(name));
289 return replaceSymbol<DefinedData>(s: insertName(name).first, arg&: name,
290 arg: flags | WASM_SYMBOL_ABSOLUTE);
291}
292
293DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags,
294 InputGlobal *global) {
295 LLVM_DEBUG(dbgs() << "addSyntheticGlobal: " << name << " -> " << global
296 << "\n");
297 assert(!find(name));
298 ctx.syntheticGlobals.emplace_back(Args&: global);
299 return replaceSymbol<DefinedGlobal>(s: insertName(name).first, arg&: name, arg&: flags,
300 arg: nullptr, arg&: global);
301}
302
303DefinedGlobal *SymbolTable::addOptionalGlobalSymbol(StringRef name,
304 InputGlobal *global) {
305 Symbol *s = find(name);
306 if (!s && (ctx.arg.exportAll || ctx.arg.exportedSymbols.contains(key: name)))
307 s = insertName(name).first;
308 else if (!s || s->isDefined())
309 return nullptr;
310 LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbol: " << name << " -> " << global
311 << "\n");
312 ctx.syntheticGlobals.emplace_back(Args&: global);
313 return replaceSymbol<DefinedGlobal>(s, arg&: name, arg: WASM_SYMBOL_VISIBILITY_HIDDEN,
314 arg: nullptr, arg&: global);
315}
316
317DefinedTable *SymbolTable::addSyntheticTable(StringRef name, uint32_t flags,
318 InputTable *table) {
319 LLVM_DEBUG(dbgs() << "addSyntheticTable: " << name << " -> " << table
320 << "\n");
321 Symbol *s = find(name);
322 assert(!s || s->isUndefined());
323 if (!s)
324 s = insertName(name).first;
325 ctx.syntheticTables.emplace_back(Args&: table);
326 return replaceSymbol<DefinedTable>(s, arg&: name, arg&: flags, arg: nullptr, arg&: table);
327}
328
329static bool shouldReplace(const Symbol *existing, InputFile *newFile,
330 uint32_t newFlags) {
331 // If existing symbol is undefined, replace it.
332 if (!existing->isDefined()) {
333 LLVM_DEBUG(dbgs() << "resolving existing undefined symbol: "
334 << existing->getName() << "\n");
335 return true;
336 }
337
338 // If existing symbol is common, it can be overridden by a strong definition.
339 if (existing->isCommon()) {
340 if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
341 LLVM_DEBUG(dbgs() << "existing common symbol " << existing->getName()
342 << " takes precedence over new weak\n");
343 return false;
344 }
345 LLVM_DEBUG(dbgs() << "replacing existing common symbol "
346 << existing->getName() << " with strong definition\n");
347 return true;
348 }
349
350 // Now we have two defined symbols. If the new one is weak, we can ignore it.
351 if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
352 LLVM_DEBUG(dbgs() << "existing symbol takes precedence\n");
353 return false;
354 }
355
356 // If the existing symbol is weak, we should replace it.
357 if (existing->isWeak()) {
358 LLVM_DEBUG(dbgs() << "replacing existing weak symbol\n");
359 return true;
360 }
361
362 // Similarly with shared symbols
363 if (existing->isShared()) {
364 LLVM_DEBUG(dbgs() << "replacing existing shared symbol\n");
365 return true;
366 }
367
368 // Neither symbol is week. They conflict.
369 if (ctx.arg.allowMultipleDefinition)
370 return false;
371
372 errorOrWarn(msg: "duplicate symbol: " + toString(sym: *existing) + "\n>>> defined in " +
373 toString(file: existing->getFile()) + "\n>>> defined in " +
374 toString(file: newFile));
375 return true;
376}
377
378static void reportFunctionSignatureMismatch(StringRef symName,
379 FunctionSymbol *sym,
380 const WasmSignature *signature,
381 InputFile *file,
382 bool isError = true) {
383 std::string msg =
384 ("function signature mismatch: " + symName + "\n>>> defined as " +
385 toString(sig: *sym->signature) + " in " + toString(file: sym->getFile()) +
386 "\n>>> defined as " + toString(sig: *signature) + " in " + toString(file))
387 .str();
388 if (isError)
389 error(msg);
390 else
391 warn(msg);
392}
393
394static void reportFunctionSignatureMismatch(StringRef symName,
395 FunctionSymbol *a,
396 FunctionSymbol *b,
397 bool isError = true) {
398 reportFunctionSignatureMismatch(symName, sym: a, signature: b->signature, file: b->getFile(),
399 isError);
400}
401
402Symbol *SymbolTable::addSharedTag(StringRef name, uint32_t flags,
403 InputFile *file, const WasmSignature *sig) {
404 LLVM_DEBUG(dbgs() << "addSharedTag: " << name << " [" << toString(*sig)
405 << "]\n");
406 Symbol *s;
407 bool wasInserted;
408 std::tie(args&: s, args&: wasInserted) = insert(name, file);
409
410 auto replaceSym = [&](Symbol *sym) {
411 replaceSymbol<SharedTagSymbol>(s: sym, arg&: name, arg&: flags, arg&: file, arg&: sig);
412 };
413
414 // same as addSharedFunction, but this is in its own function
415 if (wasInserted || s->isLazy()) {
416 replaceSym(s);
417 return s;
418 }
419
420 auto *existingTag = dyn_cast<TagSymbol>(Val: s);
421 if (!existingTag) {
422 reportTypeError(existing: s, file, type: WASM_SYMBOL_TYPE_TAG);
423 return s;
424 }
425
426 // Shared symbols should never replace locally-defined ones
427 if (s->isDefined())
428 return s;
429
430 // undefined existing sym
431 const WasmSignature *oldSig = existingTag->signature;
432 if (oldSig && sig && *oldSig != *sig)
433 error(msg: "Tag signature mismatch: " + name + "\n>>> defined as " +
434 toString(sig: *oldSig) + " in " + toString(file: existingTag->getFile()) +
435 "\n>>> defined as " + toString(sig: *sig) + " in " + toString(file));
436 replaceSym(s);
437 return s;
438}
439
440Symbol *SymbolTable::addSharedFunction(StringRef name, uint32_t flags,
441 InputFile *file,
442 const WasmSignature *sig) {
443 LLVM_DEBUG(dbgs() << "addSharedFunction: " << name << " [" << toString(*sig)
444 << "]\n");
445 Symbol *s;
446 bool wasInserted;
447 std::tie(args&: s, args&: wasInserted) = insert(name, file);
448
449 auto replaceSym = [&](Symbol *sym) {
450 replaceSymbol<SharedFunctionSymbol>(s: sym, arg&: name, arg&: flags, arg&: file, arg&: sig);
451 };
452
453 if (wasInserted || s->isLazy()) {
454 replaceSym(s);
455 return s;
456 }
457
458 auto existingFunction = dyn_cast<FunctionSymbol>(Val: s);
459 if (!existingFunction) {
460 reportTypeError(existing: s, file, type: WASM_SYMBOL_TYPE_FUNCTION);
461 return s;
462 }
463
464 // Shared symbols should never replace locally-defined ones
465 if (s->isDefined())
466 return s;
467
468 LLVM_DEBUG(dbgs() << "resolving existing undefined symbol: " << s->getName()
469 << "\n");
470
471 bool checkSig = true;
472 if (auto ud = dyn_cast<UndefinedFunction>(Val: existingFunction))
473 checkSig = ud->isCalledDirectly;
474
475 if (checkSig && !signatureMatches(existing: existingFunction, newSig: sig)) {
476 if (ctx.arg.shlibSigCheck) {
477 reportFunctionSignatureMismatch(symName: name, sym: existingFunction, signature: sig, file);
478 } else {
479 // With --no-shlib-sigcheck we ignore the signature of the function as
480 // defined by the shared library and instead use the signature as
481 // expected by the program being linked.
482 sig = existingFunction->signature;
483 }
484 }
485
486 replaceSym(s);
487 return s;
488}
489
490Symbol *SymbolTable::addSharedData(StringRef name, uint32_t flags,
491 InputFile *file) {
492 LLVM_DEBUG(dbgs() << "addSharedData: " << name << "\n");
493 Symbol *s;
494 bool wasInserted;
495 std::tie(args&: s, args&: wasInserted) = insert(name, file);
496
497 if (wasInserted || s->isLazy()) {
498 replaceSymbol<SharedData>(s, arg&: name, arg&: flags, arg&: file);
499 return s;
500 }
501
502 // Shared symbols should never replace locally-defined ones
503 if (s->isDefined())
504 return s;
505
506 checkDataType(existing: s, file);
507 replaceSymbol<SharedData>(s, arg&: name, arg&: flags, arg&: file);
508 return s;
509}
510
511Symbol *SymbolTable::addCommon(StringRef name, uint32_t flags, InputFile *file,
512 uint64_t size, uint32_t alignment) {
513 LLVM_DEBUG(dbgs() << "addCommon: " << name << " size:" << size
514 << " align:" << alignment << "\n");
515 auto val = insert(name, file);
516 Symbol *s = val.first;
517 bool wasInserted = val.second;
518
519 auto replaceSym = [&]() {
520 replaceSymbol<CommonSymbol>(s, arg&: name, arg&: flags, arg&: file, arg&: size, arg&: alignment);
521 };
522
523 if (wasInserted || s->isLazy()) {
524 replaceSym();
525 return s;
526 }
527
528 checkDataType(existing: s, file);
529
530 if (auto *existingCommon = dyn_cast<CommonSymbol>(Val: s)) {
531 uint64_t newSize = std::max(a: existingCommon->getSize(), b: size);
532 uint32_t newAlign = std::max(a: existingCommon->getAlignment(), b: alignment);
533 existingCommon->setCommon(s: newSize, a: newAlign);
534 existingCommon->flags |= flags & WASM_SYMBOL_NO_STRIP;
535 return s;
536 }
537
538 if (s->isDefined()) {
539 if (s->isWeak()) {
540 LLVM_DEBUG(
541 dbgs() << "replacing existing weak defined symbol with common\n");
542 replaceSym();
543 } else {
544 LLVM_DEBUG(
545 dbgs()
546 << "existing strong defined symbol takes precedence over common\n");
547 }
548 return s;
549 }
550
551 LLVM_DEBUG(dbgs() << "resolving existing undefined symbol with common\n");
552 replaceSym();
553 return s;
554}
555
556Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags,
557 InputFile *file,
558 InputFunction *function) {
559 LLVM_DEBUG(dbgs() << "addDefinedFunction: " << name << " ["
560 << (function ? toString(function->signature) : "none")
561 << "]\n");
562 Symbol *s;
563 bool wasInserted;
564 std::tie(args&: s, args&: wasInserted) = insert(name, file);
565
566 auto replaceSym = [&](Symbol *sym) {
567 // If the new defined function doesn't have signature (i.e. bitcode
568 // functions) but the old symbol does, then preserve the old signature
569 const WasmSignature *oldSig = s->getSignature();
570 auto *newSym =
571 replaceSymbol<DefinedFunction>(s: sym, arg&: name, arg&: flags, arg&: file, arg&: function);
572 if (!newSym->signature)
573 newSym->signature = oldSig;
574 };
575
576 if (wasInserted || s->isLazy()) {
577 replaceSym(s);
578 return s;
579 }
580
581 auto existingFunction = dyn_cast<FunctionSymbol>(Val: s);
582 if (!existingFunction) {
583 if (isBitcodeSymbol(symbol: s)) {
584 replaceSym(s);
585 return s;
586 }
587 reportTypeError(existing: s, file, type: WASM_SYMBOL_TYPE_FUNCTION);
588 return s;
589 }
590
591 bool checkSig = true;
592 if (auto ud = dyn_cast<UndefinedFunction>(Val: existingFunction))
593 checkSig = ud->isCalledDirectly;
594
595 if (checkSig && function &&
596 !signatureMatches(existing: existingFunction, newSig: &function->signature)) {
597 Symbol *variant;
598 if (getFunctionVariant(sym: s, sig: &function->signature, file, out: &variant))
599 // New variant, always replace
600 replaceSym(variant);
601 else if (shouldReplace(existing: s, newFile: file, newFlags: flags))
602 // Variant already exists, replace it after checking shouldReplace
603 replaceSym(variant);
604
605 // This variant we found take the place in the symbol table as the primary
606 // variant.
607 replace(name, sym: variant);
608 return variant;
609 }
610
611 // Existing function with matching signature.
612 if (shouldReplace(existing: s, newFile: file, newFlags: flags))
613 replaceSym(s);
614
615 return s;
616}
617
618Symbol *SymbolTable::addDefinedData(StringRef name, uint32_t flags,
619 InputFile *file, InputChunk *segment,
620 uint64_t address, uint64_t size) {
621 LLVM_DEBUG(dbgs() << "addDefinedData:" << name << " addr:" << address
622 << "\n");
623 Symbol *s;
624 bool wasInserted;
625 std::tie(args&: s, args&: wasInserted) = insert(name, file);
626
627 auto replaceSym = [&]() {
628 replaceSymbol<DefinedData>(s, arg&: name, arg&: flags, arg&: file, arg&: segment, arg&: address, arg&: size);
629 };
630
631 if (wasInserted || s->isLazy()) {
632 replaceSym();
633 return s;
634 }
635
636 checkDataType(existing: s, file);
637
638 if (shouldReplace(existing: s, newFile: file, newFlags: flags))
639 replaceSym();
640 return s;
641}
642
643Symbol *SymbolTable::addDefinedGlobal(StringRef name, uint32_t flags,
644 InputFile *file, InputGlobal *global) {
645 LLVM_DEBUG(dbgs() << "addDefinedGlobal:" << name << "\n");
646
647 Symbol *s;
648 bool wasInserted;
649 std::tie(args&: s, args&: wasInserted) = insert(name, file);
650
651 auto replaceSym = [&]() {
652 replaceSymbol<DefinedGlobal>(s, arg&: name, arg&: flags, arg&: file, arg&: global);
653 };
654
655 if (wasInserted || s->isLazy()) {
656 replaceSym();
657 return s;
658 }
659
660 checkGlobalType(existing: s, file, newType: &global->getType());
661
662 if (shouldReplace(existing: s, newFile: file, newFlags: flags))
663 replaceSym();
664 return s;
665}
666
667Symbol *SymbolTable::addDefinedTag(StringRef name, uint32_t flags,
668 InputFile *file, InputTag *tag) {
669 LLVM_DEBUG(dbgs() << "addDefinedTag:" << name << "\n");
670
671 Symbol *s;
672 bool wasInserted;
673 std::tie(args&: s, args&: wasInserted) = insert(name, file);
674
675 auto replaceSym = [&]() {
676 replaceSymbol<DefinedTag>(s, arg&: name, arg&: flags, arg&: file, arg&: tag);
677 };
678
679 if (wasInserted || s->isLazy()) {
680 replaceSym();
681 return s;
682 }
683
684 checkTagType(existing: s, file, newSig: &tag->signature);
685
686 if (shouldReplace(existing: s, newFile: file, newFlags: flags))
687 replaceSym();
688 return s;
689}
690
691Symbol *SymbolTable::addDefinedTable(StringRef name, uint32_t flags,
692 InputFile *file, InputTable *table) {
693 LLVM_DEBUG(dbgs() << "addDefinedTable:" << name << "\n");
694
695 Symbol *s;
696 bool wasInserted;
697 std::tie(args&: s, args&: wasInserted) = insert(name, file);
698
699 auto replaceSym = [&]() {
700 replaceSymbol<DefinedTable>(s, arg&: name, arg&: flags, arg&: file, arg&: table);
701 };
702
703 if (wasInserted || s->isLazy()) {
704 replaceSym();
705 return s;
706 }
707
708 checkTableType(existing: s, file, newType: &table->getType());
709
710 if (shouldReplace(existing: s, newFile: file, newFlags: flags))
711 replaceSym();
712 return s;
713}
714
715// This function get called when an undefined symbol is added, and there is
716// already an existing one in the symbols table. In this case we check that
717// custom 'import-module' and 'import-field' symbol attributes agree.
718// With LTO these attributes are not available when the bitcode is read and only
719// become available when the LTO object is read. In this case we silently
720// replace the empty attributes with the valid ones.
721static void
722updateExistingUndefined(Symbol *existing, uint32_t flags, InputFile *file,
723 std::optional<StringRef> importName = {},
724 std::optional<StringRef> importModule = {}) {
725 if (importName) {
726 if (!existing->importName)
727 existing->importName = importName;
728 if (existing->importName != importName)
729 error(msg: "import name mismatch for symbol: " + toString(sym: *existing) +
730 "\n>>> defined as " + *existing->importName + " in " +
731 toString(file: existing->getFile()) + "\n>>> defined as " + *importName +
732 " in " + toString(file));
733 }
734
735 if (importModule) {
736 if (!existing->importModule)
737 existing->importModule = importModule;
738 if (existing->importModule != importModule)
739 error(msg: "import module mismatch for symbol: " + toString(sym: *existing) +
740 "\n>>> defined as " + *existing->importModule + " in " +
741 toString(file: existing->getFile()) + "\n>>> defined as " +
742 *importModule + " in " + toString(file));
743 }
744
745 // Update symbol binding, if the existing symbol is weak
746 uint32_t binding = flags & WASM_SYMBOL_BINDING_MASK;
747 if (existing->isWeak() && binding != WASM_SYMBOL_BINDING_WEAK) {
748 existing->flags = (existing->flags & ~WASM_SYMBOL_BINDING_MASK) | binding;
749 }
750
751 // Certain flags such as NO_STRIP should be maintained if either old or
752 // new symbol is marked as such.
753 existing->flags |= flags & WASM_SYMBOL_NO_STRIP;
754}
755
756Symbol *SymbolTable::addUndefinedFunction(StringRef name,
757 std::optional<StringRef> importName,
758 std::optional<StringRef> importModule,
759 uint32_t flags, InputFile *file,
760 const WasmSignature *sig,
761 bool isCalledDirectly) {
762 LLVM_DEBUG(dbgs() << "addUndefinedFunction: " << name << " ["
763 << (sig ? toString(*sig) : "none")
764 << "] IsCalledDirectly:" << isCalledDirectly << " flags=0x"
765 << utohexstr(flags) << "\n");
766 assert(flags & WASM_SYMBOL_UNDEFINED);
767
768 Symbol *s;
769 bool wasInserted;
770 std::tie(args&: s, args&: wasInserted) = insert(name, file);
771 if (s->traced)
772 printTraceSymbolUndefined(name, file);
773
774 auto replaceSym = [&]() {
775 replaceSymbol<UndefinedFunction>(s, arg&: name, arg&: importName, arg&: importModule, arg&: flags,
776 arg&: file, arg&: sig, arg&: isCalledDirectly);
777 };
778
779 if (wasInserted) {
780 replaceSym();
781 } else if (auto *lazy = dyn_cast<LazySymbol>(Val: s)) {
782 if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
783 lazy->setWeak();
784 lazy->signature = sig;
785 } else {
786 lazy->extract();
787 if (!ctx.arg.whyExtract.empty())
788 ctx.whyExtractRecords.emplace_back(Args: toString(file), Args: s->getFile(), Args&: *s);
789 }
790 } else {
791 auto existingFunction = dyn_cast<FunctionSymbol>(Val: s);
792 if (!existingFunction) {
793 reportTypeError(existing: s, file, type: WASM_SYMBOL_TYPE_FUNCTION);
794 return s;
795 }
796 if (!existingFunction->signature && sig)
797 existingFunction->signature = sig;
798 auto *existingUndefined = dyn_cast<UndefinedFunction>(Val: existingFunction);
799 if (isCalledDirectly && !signatureMatches(existing: existingFunction, newSig: sig)) {
800 if (existingFunction->isShared()) {
801 // Special handling for when the existing function is a shared symbol
802 if (ctx.arg.shlibSigCheck) {
803 reportFunctionSignatureMismatch(symName: name, sym: existingFunction, signature: sig, file);
804 } else {
805 existingFunction->signature = sig;
806 }
807 }
808 // If the existing undefined functions is not called directly then let
809 // this one take precedence. Otherwise the existing function is either
810 // directly called or defined, in which case we need a function variant.
811 else if (existingUndefined && !existingUndefined->isCalledDirectly)
812 replaceSym();
813 else if (getFunctionVariant(sym: s, sig, file, out: &s))
814 replaceSym();
815 }
816 if (existingUndefined) {
817 updateExistingUndefined(existing: existingUndefined, flags, file, importName,
818 importModule);
819 if (isCalledDirectly)
820 existingUndefined->isCalledDirectly = true;
821 }
822 }
823
824 return s;
825}
826
827Symbol *SymbolTable::addUndefinedData(StringRef name, uint32_t flags,
828 InputFile *file) {
829 LLVM_DEBUG(dbgs() << "addUndefinedData: " << name << "\n");
830 assert(flags & WASM_SYMBOL_UNDEFINED);
831
832 Symbol *s;
833 bool wasInserted;
834 std::tie(args&: s, args&: wasInserted) = insert(name, file);
835 if (s->traced)
836 printTraceSymbolUndefined(name, file);
837
838 if (wasInserted) {
839 replaceSymbol<UndefinedData>(s, arg&: name, arg&: flags, arg&: file);
840 } else if (auto *lazy = dyn_cast<LazySymbol>(Val: s)) {
841 if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK)
842 lazy->setWeak();
843 else
844 lazy->extract();
845 } else if (s->isDefined()) {
846 checkDataType(existing: s, file);
847 } else {
848 updateExistingUndefined(existing: s, flags, file);
849 }
850 return s;
851}
852
853Symbol *SymbolTable::addUndefinedGlobal(StringRef name,
854 std::optional<StringRef> importName,
855 std::optional<StringRef> importModule,
856 uint32_t flags, InputFile *file,
857 const WasmGlobalType *type) {
858 LLVM_DEBUG(dbgs() << "addUndefinedGlobal: " << name << "\n");
859 assert(flags & WASM_SYMBOL_UNDEFINED);
860
861 Symbol *s;
862 bool wasInserted;
863 std::tie(args&: s, args&: wasInserted) = insert(name, file);
864 if (s->traced)
865 printTraceSymbolUndefined(name, file);
866
867 if (wasInserted)
868 replaceSymbol<UndefinedGlobal>(s, arg&: name, arg&: importName, arg&: importModule, arg&: flags,
869 arg&: file, arg&: type);
870 else if (auto *lazy = dyn_cast<LazySymbol>(Val: s))
871 lazy->extract();
872 else if (s->isDefined())
873 checkGlobalType(existing: s, file, newType: type);
874 else
875 updateExistingUndefined(existing: s, flags, file, importName, importModule);
876 return s;
877}
878
879Symbol *SymbolTable::addUndefinedTable(StringRef name,
880 std::optional<StringRef> importName,
881 std::optional<StringRef> importModule,
882 uint32_t flags, InputFile *file,
883 const WasmTableType *type) {
884 LLVM_DEBUG(dbgs() << "addUndefinedTable: " << name << "\n");
885 assert(flags & WASM_SYMBOL_UNDEFINED);
886
887 Symbol *s;
888 bool wasInserted;
889 std::tie(args&: s, args&: wasInserted) = insert(name, file);
890 if (s->traced)
891 printTraceSymbolUndefined(name, file);
892
893 if (wasInserted)
894 replaceSymbol<UndefinedTable>(s, arg&: name, arg&: importName, arg&: importModule, arg&: flags,
895 arg&: file, arg&: type);
896 else if (auto *lazy = dyn_cast<LazySymbol>(Val: s))
897 lazy->extract();
898 else if (s->isDefined())
899 checkTableType(existing: s, file, newType: type);
900 else
901 updateExistingUndefined(existing: s, flags, file, importName, importModule);
902 return s;
903}
904
905Symbol *SymbolTable::addUndefinedTag(StringRef name,
906 std::optional<StringRef> importName,
907 std::optional<StringRef> importModule,
908 uint32_t flags, InputFile *file,
909 const WasmSignature *sig) {
910 LLVM_DEBUG(dbgs() << "addUndefinedTag: " << name << "\n");
911 assert(flags & WASM_SYMBOL_UNDEFINED);
912
913 Symbol *s;
914 bool wasInserted;
915 std::tie(args&: s, args&: wasInserted) = insert(name, file);
916 if (s->traced)
917 printTraceSymbolUndefined(name, file);
918
919 if (wasInserted)
920 replaceSymbol<UndefinedTag>(s, arg&: name, arg&: importName, arg&: importModule, arg&: flags, arg&: file,
921 arg&: sig);
922 else if (auto *lazy = dyn_cast<LazySymbol>(Val: s))
923 lazy->extract();
924 else if (s->isDefined())
925 checkTagType(existing: s, file, newSig: sig);
926 else
927 updateExistingUndefined(existing: s, flags, file, importName, importModule);
928 return s;
929}
930
931TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
932 LLVM_DEBUG(llvm::dbgs() << "createUndefinedIndirectFunctionTable\n");
933 WasmLimits limits{.Flags: 0, .Minimum: 0, .Maximum: 0, .PageSize: 0}; // Set by the writer.
934 WasmTableType *type = make<WasmTableType>();
935 type->ElemType = ValType::FUNCREF;
936 type->Limits = limits;
937 uint32_t flags = ctx.arg.exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;
938 flags |= WASM_SYMBOL_UNDEFINED;
939 Symbol *sym =
940 addUndefinedTable(name, importName: name, importModule: defaultModule, flags, file: nullptr, type);
941 sym->markLive();
942 sym->forceExport = ctx.arg.exportTable;
943 return cast<TableSymbol>(Val: sym);
944}
945
946TableSymbol *SymbolTable::createDefinedIndirectFunctionTable(StringRef name) {
947 LLVM_DEBUG(llvm::dbgs() << "createDefinedIndirectFunctionTable\n");
948 const uint32_t invalidIndex = -1;
949 WasmLimits limits{.Flags: 0, .Minimum: 0, .Maximum: 0, .PageSize: 0}; // Set by the writer.
950 WasmTableType type{.ElemType: ValType::FUNCREF, .Limits: limits};
951 WasmTable desc{.Index: invalidIndex, .Type: type, .SymbolName: name};
952 InputTable *table = make<InputTable>(args&: desc, args: nullptr);
953 uint32_t flags = ctx.arg.exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;
954 TableSymbol *sym = addSyntheticTable(name, flags, table);
955 sym->markLive();
956 sym->forceExport = ctx.arg.exportTable;
957 return sym;
958}
959
960// Whether or not we need an indirect function table is usually a function of
961// whether an input declares a need for it. However sometimes it's possible for
962// no input to need the indirect function table, but then a late
963// addInternalGOTEntry causes a function to be allocated an address. In that
964// case address we synthesize a definition at the last minute.
965TableSymbol *SymbolTable::resolveIndirectFunctionTable(bool required) {
966 Symbol *existing = find(name: functionTableName);
967 if (existing) {
968 if (!isa<TableSymbol>(Val: existing)) {
969 error(msg: Twine("reserved symbol must be of type table: `") +
970 functionTableName + "`");
971 return nullptr;
972 }
973 if (existing->isDefined()) {
974 error(msg: Twine("reserved symbol must not be defined in input files: `") +
975 functionTableName + "`");
976 return nullptr;
977 }
978 }
979
980 if (ctx.arg.importTable) {
981 if (existing) {
982 existing->importModule = defaultModule;
983 existing->importName = functionTableName;
984 return cast<TableSymbol>(Val: existing);
985 }
986 if (required)
987 return createUndefinedIndirectFunctionTable(name: functionTableName);
988 } else if ((existing && existing->isLive()) || ctx.arg.exportTable ||
989 required) {
990 // A defined table is required. Either because the user request an exported
991 // table or because the table symbol is already live. The existing table is
992 // guaranteed to be undefined due to the check above.
993 return createDefinedIndirectFunctionTable(name: functionTableName);
994 }
995
996 // An indirect function table will only be present in the symbol table if
997 // needed by a reloc; if we get here, we don't need one.
998 return nullptr;
999}
1000
1001void SymbolTable::addLazy(StringRef name, InputFile *file) {
1002 LLVM_DEBUG(dbgs() << "addLazy: " << name << "\n");
1003
1004 Symbol *s;
1005 bool wasInserted;
1006 std::tie(args&: s, args&: wasInserted) = insertName(name);
1007
1008 if (wasInserted) {
1009 replaceSymbol<LazySymbol>(s, arg&: name, arg: 0, arg&: file);
1010 return;
1011 }
1012
1013 if (!s->isUndefined())
1014 return;
1015
1016 // The existing symbol is undefined, load a new one from the archive,
1017 // unless the existing symbol is weak in which case replace the undefined
1018 // symbols with a LazySymbol.
1019 if (s->isWeak()) {
1020 const WasmSignature *oldSig = nullptr;
1021 // In the case of an UndefinedFunction we need to preserve the expected
1022 // signature.
1023 if (auto *f = dyn_cast<UndefinedFunction>(Val: s))
1024 oldSig = f->signature;
1025 LLVM_DEBUG(dbgs() << "replacing existing weak undefined symbol\n");
1026 auto newSym =
1027 replaceSymbol<LazySymbol>(s, arg&: name, arg: WASM_SYMBOL_BINDING_WEAK, arg&: file);
1028 newSym->signature = oldSig;
1029 return;
1030 }
1031
1032 LLVM_DEBUG(dbgs() << "replacing existing undefined\n");
1033 const InputFile *oldFile = s->getFile();
1034 LazySymbol(name, 0, file).extract();
1035 if (!ctx.arg.whyExtract.empty())
1036 ctx.whyExtractRecords.emplace_back(Args: toString(file: oldFile), Args: s->getFile(), Args&: *s);
1037}
1038
1039bool SymbolTable::addComdat(StringRef name) {
1040 return comdatGroups.insert(V: CachedHashStringRef(name)).second;
1041}
1042
1043// The new signature doesn't match. Create a variant to the symbol with the
1044// signature encoded in the name and return that instead. These symbols are
1045// then unified later in handleSymbolVariants.
1046bool SymbolTable::getFunctionVariant(Symbol *sym, const WasmSignature *sig,
1047 const InputFile *file, Symbol **out) {
1048 LLVM_DEBUG(dbgs() << "getFunctionVariant: " << sym->getName() << " -> "
1049 << " " << toString(*sig) << "\n");
1050 Symbol *variant = nullptr;
1051
1052 // Linear search through symbol variants. Should never be more than two
1053 // or three entries here.
1054 auto &variants = symVariants[CachedHashStringRef(sym->getName())];
1055 if (variants.empty())
1056 variants.push_back(x: sym);
1057
1058 for (Symbol *v : variants) {
1059 if (*v->getSignature() == *sig) {
1060 variant = v;
1061 break;
1062 }
1063 }
1064
1065 bool wasAdded = !variant;
1066 if (wasAdded) {
1067 // Create a new variant;
1068 LLVM_DEBUG(dbgs() << "added new variant\n");
1069 variant = reinterpret_cast<Symbol *>(make<SymbolUnion>());
1070 variant->isUsedInRegularObj =
1071 !file || file->kind() == InputFile::ObjectKind;
1072 variant->canInline = true;
1073 variant->traced = false;
1074 variant->forceExport = false;
1075 variants.push_back(x: variant);
1076 } else {
1077 LLVM_DEBUG(dbgs() << "variant already exists: " << toString(*variant)
1078 << "\n");
1079 assert(*variant->getSignature() == *sig);
1080 }
1081
1082 *out = variant;
1083 return wasAdded;
1084}
1085
1086// Set a flag for --trace-symbol so that we can print out a log message
1087// if a new symbol with the same name is inserted into the symbol table.
1088void SymbolTable::trace(StringRef name) {
1089 symMap.insert(KV: {CachedHashStringRef(name), -1});
1090}
1091
1092void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
1093 // Swap symbols as instructed by -wrap.
1094 int &origIdx = symMap[CachedHashStringRef(sym->getName())];
1095 int &realIdx = symMap[CachedHashStringRef(real->getName())];
1096 int &wrapIdx = symMap[CachedHashStringRef(wrap->getName())];
1097 LLVM_DEBUG(dbgs() << "wrap: " << sym->getName() << "\n");
1098
1099 // Anyone looking up __real symbols should get the original
1100 realIdx = origIdx;
1101 // Anyone looking up the original should get the __wrap symbol
1102 origIdx = wrapIdx;
1103}
1104
1105static const uint8_t unreachableFn[] = {
1106 0x03 /* ULEB length */, 0x00 /* ULEB num locals */,
1107 0x00 /* opcode unreachable */, 0x0b /* opcode end */
1108};
1109
1110// Replace the given symbol body with an unreachable function.
1111// This is used by handleWeakUndefines in order to generate a callable
1112// equivalent of an undefined function and also handleSymbolVariants for
1113// undefined functions that don't match the signature of the definition.
1114InputFunction *SymbolTable::replaceWithUnreachable(Symbol *sym,
1115 const WasmSignature &sig,
1116 StringRef debugName) {
1117 auto *func = make<SyntheticFunction>(args: sig, args: sym->getName(), args&: debugName);
1118 func->setBody(unreachableFn);
1119 ctx.syntheticFunctions.emplace_back(Args&: func);
1120 // Mark new symbols as local. For relocatable output we don't want them
1121 // to be exported outside the object file.
1122 replaceSymbol<DefinedFunction>(s: sym, arg&: debugName, arg: WASM_SYMBOL_BINDING_LOCAL,
1123 arg: nullptr, arg&: func);
1124 // Ensure the stub function doesn't get a table entry. Its address
1125 // should always compare equal to the null pointer.
1126 sym->isStub = true;
1127 return func;
1128}
1129
1130void SymbolTable::replaceWithUndefined(Symbol *sym) {
1131 // Add a synthetic dummy for weak undefined functions. These dummies will
1132 // be GC'd if not used as the target of any "call" instructions.
1133 StringRef debugName = saver().save(S: "undefined_weak:" + toString(sym: *sym));
1134 replaceWithUnreachable(sym, sig: *sym->getSignature(), debugName);
1135 // Hide our dummy to prevent export.
1136 sym->setHidden(true);
1137}
1138
1139// For weak undefined functions, there may be "call" instructions that reference
1140// the symbol. In this case, we need to synthesise a dummy/stub function that
1141// will abort at runtime, so that relocations can still provided an operand to
1142// the call instruction that passes Wasm validation.
1143void SymbolTable::handleWeakUndefines() {
1144 for (Symbol *sym : symbols()) {
1145 if (sym->isUndefWeak() && sym->isUsedInRegularObj) {
1146 if (sym->getSignature()) {
1147 replaceWithUndefined(sym);
1148 } else {
1149 // It is possible for undefined functions not to have a signature (eg.
1150 // if added via "--undefined"), but weak undefined ones do have a
1151 // signature. Lazy symbols may not be functions and therefore Sig can
1152 // still be null in some circumstance.
1153 assert(!isa<FunctionSymbol>(sym));
1154 }
1155 }
1156 }
1157}
1158
1159DefinedFunction *SymbolTable::createUndefinedStub(const WasmSignature &sig) {
1160 if (auto it = stubFunctions.find(Val: sig); it != stubFunctions.end())
1161 return it->second;
1162 LLVM_DEBUG(dbgs() << "createUndefinedStub: " << toString(sig) << "\n");
1163 auto *sym = reinterpret_cast<DefinedFunction *>(make<SymbolUnion>());
1164 sym->isUsedInRegularObj = true;
1165 sym->canInline = true;
1166 sym->traced = false;
1167 sym->forceExport = false;
1168 sym->signature = &sig;
1169 replaceSymbol<DefinedFunction>(
1170 s: sym, arg: "undefined_stub", arg: WASM_SYMBOL_VISIBILITY_HIDDEN, arg: nullptr, arg: nullptr);
1171 replaceWithUnreachable(sym, sig, debugName: "undefined_stub");
1172 stubFunctions[sig] = sym;
1173 return sym;
1174}
1175
1176// Remove any variant symbols that were created due to function signature
1177// mismatches.
1178void SymbolTable::handleSymbolVariants() {
1179 for (auto pair : symVariants) {
1180 // Push the initial symbol onto the list of variants.
1181 StringRef symName = pair.first.val();
1182 std::vector<Symbol *> &variants = pair.second;
1183
1184#ifndef NDEBUG
1185 LLVM_DEBUG(dbgs() << "symbol with (" << variants.size()
1186 << ") variants: " << symName << "\n");
1187 for (auto *s : variants) {
1188 auto *f = cast<FunctionSymbol>(s);
1189 LLVM_DEBUG(dbgs() << " variant: " + f->getName() << " "
1190 << toString(*f->signature) << "\n");
1191 }
1192#endif
1193
1194 // Find the one definition.
1195 DefinedFunction *defined = nullptr;
1196 for (auto *symbol : variants) {
1197 if (auto f = dyn_cast<DefinedFunction>(Val: symbol)) {
1198 defined = f;
1199 break;
1200 }
1201 }
1202
1203 // If there are no definitions, and the undefined symbols disagree on
1204 // the signature, there is not we can do since we don't know which one
1205 // to use as the signature on the import.
1206 if (!defined) {
1207 reportFunctionSignatureMismatch(symName,
1208 a: cast<FunctionSymbol>(Val: variants[0]),
1209 b: cast<FunctionSymbol>(Val: variants[1]));
1210 return;
1211 }
1212
1213 for (auto *symbol : variants) {
1214 if (symbol != defined) {
1215 auto *f = cast<FunctionSymbol>(Val: symbol);
1216 reportFunctionSignatureMismatch(symName, a: f, b: defined, isError: false);
1217 StringRef debugName =
1218 saver().save(S: "signature_mismatch:" + toString(sym: *f));
1219 replaceWithUnreachable(sym: f, sig: *f->signature, debugName);
1220 }
1221 }
1222 }
1223}
1224
1225} // namespace lld::wasm
1226