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 symbol. 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 DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name, 245 uint32_t flags, 246 InputGlobal *global) { 247 LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbols: " << name << " -> " << global 248 << "\n"); 249 Symbol *s = find(name); 250 if (!s || s->isDefined()) 251 return nullptr; 252 syntheticGlobals.emplace_back(global); 253 return replaceSymbol<DefinedGlobal>(s, name, flags, nullptr, global); 254 } 255 256 static bool shouldReplace(const Symbol *existing, InputFile *newFile, 257 uint32_t newFlags) { 258 // If existing symbol is undefined, replace it. 259 if (!existing->isDefined()) { 260 LLVM_DEBUG(dbgs() << "resolving existing undefined symbol: " 261 << existing->getName() << "\n"); 262 return true; 263 } 264 265 // Now we have two defined symbols. If the new one is weak, we can ignore it. 266 if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) { 267 LLVM_DEBUG(dbgs() << "existing symbol takes precedence\n"); 268 return false; 269 } 270 271 // If the existing symbol is weak, we should replace it. 272 if (existing->isWeak()) { 273 LLVM_DEBUG(dbgs() << "replacing existing weak symbol\n"); 274 return true; 275 } 276 277 // Neither symbol is week. They conflict. 278 error("duplicate symbol: " + toString(*existing) + "\n>>> defined in " + 279 toString(existing->getFile()) + "\n>>> defined in " + 280 toString(newFile)); 281 return true; 282 } 283 284 Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags, 285 InputFile *file, 286 InputFunction *function) { 287 LLVM_DEBUG(dbgs() << "addDefinedFunction: " << name << " [" 288 << (function ? toString(function->signature) : "none") 289 << "]\n"); 290 Symbol *s; 291 bool wasInserted; 292 std::tie(s, wasInserted) = insert(name, file); 293 294 auto replaceSym = [&](Symbol *sym) { 295 // If the new defined function doesn't have signature (i.e. bitcode 296 // functions) but the old symbol does, then preserve the old signature 297 const WasmSignature *oldSig = s->getSignature(); 298 auto* newSym = replaceSymbol<DefinedFunction>(sym, name, flags, file, function); 299 if (!newSym->signature) 300 newSym->signature = oldSig; 301 }; 302 303 if (wasInserted || s->isLazy()) { 304 replaceSym(s); 305 return s; 306 } 307 308 auto existingFunction = dyn_cast<FunctionSymbol>(s); 309 if (!existingFunction) { 310 reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION); 311 return s; 312 } 313 314 bool checkSig = true; 315 if (auto ud = dyn_cast<UndefinedFunction>(existingFunction)) 316 checkSig = ud->isCalledDirectly; 317 318 if (checkSig && function && !signatureMatches(existingFunction, &function->signature)) { 319 Symbol* variant; 320 if (getFunctionVariant(s, &function->signature, file, &variant)) 321 // New variant, always replace 322 replaceSym(variant); 323 else if (shouldReplace(s, file, flags)) 324 // Variant already exists, replace it after checking shouldReplace 325 replaceSym(variant); 326 327 // This variant we found take the place in the symbol table as the primary 328 // variant. 329 replace(name, variant); 330 return variant; 331 } 332 333 // Existing function with matching signature. 334 if (shouldReplace(s, file, flags)) 335 replaceSym(s); 336 337 return s; 338 } 339 340 Symbol *SymbolTable::addDefinedData(StringRef name, uint32_t flags, 341 InputFile *file, InputSegment *segment, 342 uint64_t address, uint64_t size) { 343 LLVM_DEBUG(dbgs() << "addDefinedData:" << name << " addr:" << address 344 << "\n"); 345 Symbol *s; 346 bool wasInserted; 347 std::tie(s, wasInserted) = insert(name, file); 348 349 auto replaceSym = [&]() { 350 replaceSymbol<DefinedData>(s, name, flags, file, segment, address, size); 351 }; 352 353 if (wasInserted || s->isLazy()) { 354 replaceSym(); 355 return s; 356 } 357 358 checkDataType(s, file); 359 360 if (shouldReplace(s, file, flags)) 361 replaceSym(); 362 return s; 363 } 364 365 Symbol *SymbolTable::addDefinedGlobal(StringRef name, uint32_t flags, 366 InputFile *file, InputGlobal *global) { 367 LLVM_DEBUG(dbgs() << "addDefinedGlobal:" << name << "\n"); 368 369 Symbol *s; 370 bool wasInserted; 371 std::tie(s, wasInserted) = insert(name, file); 372 373 auto replaceSym = [&]() { 374 replaceSymbol<DefinedGlobal>(s, name, flags, file, global); 375 }; 376 377 if (wasInserted || s->isLazy()) { 378 replaceSym(); 379 return s; 380 } 381 382 checkGlobalType(s, file, &global->getType()); 383 384 if (shouldReplace(s, file, flags)) 385 replaceSym(); 386 return s; 387 } 388 389 Symbol *SymbolTable::addDefinedEvent(StringRef name, uint32_t flags, 390 InputFile *file, InputEvent *event) { 391 LLVM_DEBUG(dbgs() << "addDefinedEvent:" << name << "\n"); 392 393 Symbol *s; 394 bool wasInserted; 395 std::tie(s, wasInserted) = insert(name, file); 396 397 auto replaceSym = [&]() { 398 replaceSymbol<DefinedEvent>(s, name, flags, file, event); 399 }; 400 401 if (wasInserted || s->isLazy()) { 402 replaceSym(); 403 return s; 404 } 405 406 checkEventType(s, file, &event->getType(), &event->signature); 407 408 if (shouldReplace(s, file, flags)) 409 replaceSym(); 410 return s; 411 } 412 413 // This function get called when an undefined symbol is added, and there is 414 // already an existing one in the symbols table. In this case we check that 415 // custom 'import-module' and 'import-field' symbol attributes agree. 416 // With LTO these attributes are not available when the bitcode is read and only 417 // become available when the LTO object is read. In this case we silently 418 // replace the empty attributes with the valid ones. 419 template <typename T> 420 static void setImportAttributes(T *existing, Optional<StringRef> importName, 421 Optional<StringRef> importModule, 422 uint32_t flags, InputFile *file) { 423 if (importName) { 424 if (!existing->importName) 425 existing->importName = importName; 426 if (existing->importName != importName) 427 error("import name mismatch for symbol: " + toString(*existing) + 428 "\n>>> defined as " + *existing->importName + " in " + 429 toString(existing->getFile()) + "\n>>> defined as " + *importName + 430 " in " + toString(file)); 431 } 432 433 if (importModule) { 434 if (!existing->importModule) 435 existing->importModule = importModule; 436 if (existing->importModule != importModule) 437 error("import module mismatch for symbol: " + toString(*existing) + 438 "\n>>> defined as " + *existing->importModule + " in " + 439 toString(existing->getFile()) + "\n>>> defined as " + 440 *importModule + " in " + toString(file)); 441 } 442 443 // Update symbol binding, if the existing symbol is weak 444 uint32_t binding = flags & WASM_SYMBOL_BINDING_MASK; 445 if (existing->isWeak() && binding != WASM_SYMBOL_BINDING_WEAK) { 446 existing->flags = (existing->flags & ~WASM_SYMBOL_BINDING_MASK) | binding; 447 } 448 } 449 450 Symbol *SymbolTable::addUndefinedFunction(StringRef name, 451 Optional<StringRef> importName, 452 Optional<StringRef> importModule, 453 uint32_t flags, InputFile *file, 454 const WasmSignature *sig, 455 bool isCalledDirectly) { 456 LLVM_DEBUG(dbgs() << "addUndefinedFunction: " << name << " [" 457 << (sig ? toString(*sig) : "none") 458 << "] IsCalledDirectly:" << isCalledDirectly << " flags=0x" 459 << utohexstr(flags) << "\n"); 460 assert(flags & WASM_SYMBOL_UNDEFINED); 461 462 Symbol *s; 463 bool wasInserted; 464 std::tie(s, wasInserted) = insert(name, file); 465 if (s->traced) 466 printTraceSymbolUndefined(name, file); 467 468 auto replaceSym = [&]() { 469 replaceSymbol<UndefinedFunction>(s, name, importName, importModule, flags, 470 file, sig, isCalledDirectly); 471 }; 472 473 if (wasInserted) { 474 replaceSym(); 475 } else if (auto *lazy = dyn_cast<LazySymbol>(s)) { 476 if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) { 477 lazy->setWeak(); 478 lazy->signature = sig; 479 } else { 480 lazy->fetch(); 481 } 482 } else { 483 auto existingFunction = dyn_cast<FunctionSymbol>(s); 484 if (!existingFunction) { 485 reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION); 486 return s; 487 } 488 if (!existingFunction->signature && sig) 489 existingFunction->signature = sig; 490 auto *existingUndefined = dyn_cast<UndefinedFunction>(existingFunction); 491 if (isCalledDirectly && !signatureMatches(existingFunction, sig)) { 492 // If the existing undefined functions is not called directly then let 493 // this one take precedence. Otherwise the existing function is either 494 // directly called or defined, in which case we need a function variant. 495 if (existingUndefined && !existingUndefined->isCalledDirectly) 496 replaceSym(); 497 else if (getFunctionVariant(s, sig, file, &s)) 498 replaceSym(); 499 } 500 if (existingUndefined) 501 setImportAttributes(existingUndefined, importName, importModule, flags, 502 file); 503 } 504 505 return s; 506 } 507 508 Symbol *SymbolTable::addUndefinedData(StringRef name, uint32_t flags, 509 InputFile *file) { 510 LLVM_DEBUG(dbgs() << "addUndefinedData: " << name << "\n"); 511 assert(flags & WASM_SYMBOL_UNDEFINED); 512 513 Symbol *s; 514 bool wasInserted; 515 std::tie(s, wasInserted) = insert(name, file); 516 if (s->traced) 517 printTraceSymbolUndefined(name, file); 518 519 if (wasInserted) { 520 replaceSymbol<UndefinedData>(s, name, flags, file); 521 } else if (auto *lazy = dyn_cast<LazySymbol>(s)) { 522 if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) 523 lazy->setWeak(); 524 else 525 lazy->fetch(); 526 } else if (s->isDefined()) { 527 checkDataType(s, file); 528 } 529 return s; 530 } 531 532 Symbol *SymbolTable::addUndefinedGlobal(StringRef name, 533 Optional<StringRef> importName, 534 Optional<StringRef> importModule, 535 uint32_t flags, InputFile *file, 536 const WasmGlobalType *type) { 537 LLVM_DEBUG(dbgs() << "addUndefinedGlobal: " << name << "\n"); 538 assert(flags & WASM_SYMBOL_UNDEFINED); 539 540 Symbol *s; 541 bool wasInserted; 542 std::tie(s, wasInserted) = insert(name, file); 543 if (s->traced) 544 printTraceSymbolUndefined(name, file); 545 546 if (wasInserted) 547 replaceSymbol<UndefinedGlobal>(s, name, importName, importModule, flags, 548 file, type); 549 else if (auto *lazy = dyn_cast<LazySymbol>(s)) 550 lazy->fetch(); 551 else if (s->isDefined()) 552 checkGlobalType(s, file, type); 553 return s; 554 } 555 556 void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) { 557 LLVM_DEBUG(dbgs() << "addLazy: " << sym->getName() << "\n"); 558 StringRef name = sym->getName(); 559 560 Symbol *s; 561 bool wasInserted; 562 std::tie(s, wasInserted) = insertName(name); 563 564 if (wasInserted) { 565 replaceSymbol<LazySymbol>(s, name, 0, file, *sym); 566 return; 567 } 568 569 if (!s->isUndefined()) 570 return; 571 572 // The existing symbol is undefined, load a new one from the archive, 573 // unless the existing symbol is weak in which case replace the undefined 574 // symbols with a LazySymbol. 575 if (s->isWeak()) { 576 const WasmSignature *oldSig = nullptr; 577 // In the case of an UndefinedFunction we need to preserve the expected 578 // signature. 579 if (auto *f = dyn_cast<UndefinedFunction>(s)) 580 oldSig = f->signature; 581 LLVM_DEBUG(dbgs() << "replacing existing weak undefined symbol\n"); 582 auto newSym = replaceSymbol<LazySymbol>(s, name, WASM_SYMBOL_BINDING_WEAK, 583 file, *sym); 584 newSym->signature = oldSig; 585 return; 586 } 587 588 LLVM_DEBUG(dbgs() << "replacing existing undefined\n"); 589 file->addMember(sym); 590 } 591 592 bool SymbolTable::addComdat(StringRef name) { 593 return comdatGroups.insert(CachedHashStringRef(name)).second; 594 } 595 596 // The new signature doesn't match. Create a variant to the symbol with the 597 // signature encoded in the name and return that instead. These symbols are 598 // then unified later in handleSymbolVariants. 599 bool SymbolTable::getFunctionVariant(Symbol* sym, const WasmSignature *sig, 600 const InputFile *file, Symbol **out) { 601 LLVM_DEBUG(dbgs() << "getFunctionVariant: " << sym->getName() << " -> " 602 << " " << toString(*sig) << "\n"); 603 Symbol *variant = nullptr; 604 605 // Linear search through symbol variants. Should never be more than two 606 // or three entries here. 607 auto &variants = symVariants[CachedHashStringRef(sym->getName())]; 608 if (variants.empty()) 609 variants.push_back(sym); 610 611 for (Symbol* v : variants) { 612 if (*v->getSignature() == *sig) { 613 variant = v; 614 break; 615 } 616 } 617 618 bool wasAdded = !variant; 619 if (wasAdded) { 620 // Create a new variant; 621 LLVM_DEBUG(dbgs() << "added new variant\n"); 622 variant = reinterpret_cast<Symbol *>(make<SymbolUnion>()); 623 variant->isUsedInRegularObj = 624 !file || file->kind() == InputFile::ObjectKind; 625 variant->canInline = true; 626 variant->traced = false; 627 variant->forceExport = false; 628 variants.push_back(variant); 629 } else { 630 LLVM_DEBUG(dbgs() << "variant already exists: " << toString(*variant) << "\n"); 631 assert(*variant->getSignature() == *sig); 632 } 633 634 *out = variant; 635 return wasAdded; 636 } 637 638 // Set a flag for --trace-symbol so that we can print out a log message 639 // if a new symbol with the same name is inserted into the symbol table. 640 void SymbolTable::trace(StringRef name) { 641 symMap.insert({CachedHashStringRef(name), -1}); 642 } 643 644 void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { 645 // Swap symbols as instructed by -wrap. 646 int &origIdx = symMap[CachedHashStringRef(sym->getName())]; 647 int &realIdx= symMap[CachedHashStringRef(real->getName())]; 648 int &wrapIdx = symMap[CachedHashStringRef(wrap->getName())]; 649 LLVM_DEBUG(dbgs() << "wrap: " << sym->getName() << "\n"); 650 651 // Anyone looking up __real symbols should get the original 652 realIdx = origIdx; 653 // Anyone looking up the original should get the __wrap symbol 654 origIdx = wrapIdx; 655 } 656 657 static const uint8_t unreachableFn[] = { 658 0x03 /* ULEB length */, 0x00 /* ULEB num locals */, 659 0x00 /* opcode unreachable */, 0x0b /* opcode end */ 660 }; 661 662 // Replace the given symbol body with an unreachable function. 663 // This is used by handleWeakUndefines in order to generate a callable 664 // equivalent of an undefined function and also handleSymbolVariants for 665 // undefined functions that don't match the signature of the definition. 666 InputFunction *SymbolTable::replaceWithUnreachable(Symbol *sym, 667 const WasmSignature &sig, 668 StringRef debugName) { 669 auto *func = make<SyntheticFunction>(sig, sym->getName(), debugName); 670 func->setBody(unreachableFn); 671 syntheticFunctions.emplace_back(func); 672 // Mark new symbols as local. For relocatable output we don't want them 673 // to be exported outside the object file. 674 replaceSymbol<DefinedFunction>(sym, debugName, WASM_SYMBOL_BINDING_LOCAL, 675 nullptr, func); 676 // Ensure the stub function doesn't get a table entry. Its address 677 // should always compare equal to the null pointer. 678 sym->isStub = true; 679 return func; 680 } 681 682 void SymbolTable::replaceWithUndefined(Symbol *sym) { 683 // Add a synthetic dummy for weak undefined functions. These dummies will 684 // be GC'd if not used as the target of any "call" instructions. 685 StringRef debugName = saver.save("undefined_weak:" + toString(*sym)); 686 replaceWithUnreachable(sym, *sym->getSignature(), debugName); 687 // Hide our dummy to prevent export. 688 sym->setHidden(true); 689 } 690 691 // For weak undefined functions, there may be "call" instructions that reference 692 // the symbol. In this case, we need to synthesise a dummy/stub function that 693 // will abort at runtime, so that relocations can still provided an operand to 694 // the call instruction that passes Wasm validation. 695 void SymbolTable::handleWeakUndefines() { 696 for (Symbol *sym : getSymbols()) { 697 if (sym->isUndefWeak()) { 698 if (sym->getSignature()) { 699 replaceWithUndefined(sym); 700 } else { 701 // It is possible for undefined functions not to have a signature (eg. 702 // if added via "--undefined"), but weak undefined ones do have a 703 // signature. Lazy symbols may not be functions and therefore Sig can 704 // still be null in some circumstance. 705 assert(!isa<FunctionSymbol>(sym)); 706 } 707 } 708 } 709 } 710 711 DefinedFunction *SymbolTable::createUndefinedStub(const WasmSignature &sig) { 712 if (stubFunctions.count(sig)) 713 return stubFunctions[sig]; 714 LLVM_DEBUG(dbgs() << "createUndefinedStub: " << toString(sig) << "\n"); 715 auto *sym = reinterpret_cast<DefinedFunction *>(make<SymbolUnion>()); 716 sym->isUsedInRegularObj = true; 717 sym->canInline = true; 718 sym->traced = false; 719 sym->forceExport = false; 720 sym->signature = &sig; 721 replaceSymbol<DefinedFunction>( 722 sym, "undefined_stub", WASM_SYMBOL_VISIBILITY_HIDDEN, nullptr, nullptr); 723 replaceWithUnreachable(sym, sig, "undefined_stub"); 724 stubFunctions[sig] = sym; 725 return sym; 726 } 727 728 static void reportFunctionSignatureMismatch(StringRef symName, 729 FunctionSymbol *a, 730 FunctionSymbol *b, bool isError) { 731 std::string msg = ("function signature mismatch: " + symName + 732 "\n>>> defined as " + toString(*a->signature) + " in " + 733 toString(a->getFile()) + "\n>>> defined as " + 734 toString(*b->signature) + " in " + toString(b->getFile())) 735 .str(); 736 if (isError) 737 error(msg); 738 else 739 warn(msg); 740 } 741 742 // Remove any variant symbols that were created due to function signature 743 // mismatches. 744 void SymbolTable::handleSymbolVariants() { 745 for (auto pair : symVariants) { 746 // Push the initial symbol onto the list of variants. 747 StringRef symName = pair.first.val(); 748 std::vector<Symbol *> &variants = pair.second; 749 750 #ifndef NDEBUG 751 LLVM_DEBUG(dbgs() << "symbol with (" << variants.size() 752 << ") variants: " << symName << "\n"); 753 for (auto *s: variants) { 754 auto *f = cast<FunctionSymbol>(s); 755 LLVM_DEBUG(dbgs() << " variant: " + f->getName() << " " 756 << toString(*f->signature) << "\n"); 757 } 758 #endif 759 760 // Find the one definition. 761 DefinedFunction *defined = nullptr; 762 for (auto *symbol : variants) { 763 if (auto f = dyn_cast<DefinedFunction>(symbol)) { 764 defined = f; 765 break; 766 } 767 } 768 769 // If there are no definitions, and the undefined symbols disagree on 770 // the signature, there is not we can do since we don't know which one 771 // to use as the signature on the import. 772 if (!defined) { 773 reportFunctionSignatureMismatch(symName, 774 cast<FunctionSymbol>(variants[0]), 775 cast<FunctionSymbol>(variants[1]), true); 776 return; 777 } 778 779 for (auto *symbol : variants) { 780 if (symbol != defined) { 781 auto *f = cast<FunctionSymbol>(symbol); 782 reportFunctionSignatureMismatch(symName, f, defined, false); 783 StringRef debugName = saver.save("signature_mismatch:" + toString(*f)); 784 replaceWithUnreachable(f, *f->signature, debugName); 785 } 786 } 787 } 788 } 789 790 } // namespace wasm 791 } // namespace lld 792