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