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