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 "ConcatOutputSection.h" 11 #include "Config.h" 12 #include "InputFiles.h" 13 #include "InputSection.h" 14 #include "Symbols.h" 15 #include "SyntheticSections.h" 16 #include "lld/Common/ErrorHandler.h" 17 #include "lld/Common/Memory.h" 18 #include "llvm/Demangle/Demangle.h" 19 20 using namespace llvm; 21 using namespace lld; 22 using namespace lld::macho; 23 24 Symbol *SymbolTable::find(CachedHashStringRef cachedName) { 25 auto it = symMap.find(cachedName); 26 if (it == symMap.end()) 27 return nullptr; 28 return symVector[it->second]; 29 } 30 31 std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, 32 const InputFile *file) { 33 auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()}); 34 35 Symbol *sym; 36 if (!p.second) { 37 // Name already present in the symbol table. 38 sym = symVector[p.first->second]; 39 } else { 40 // Name is a new symbol. 41 sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); 42 symVector.push_back(sym); 43 } 44 45 sym->isUsedInRegularObj |= !file || isa<ObjFile>(file); 46 return {sym, p.second}; 47 } 48 49 namespace { 50 struct DuplicateSymbolDiag { 51 // Pair containing source location and source file 52 const std::pair<std::string, std::string> src1; 53 const std::pair<std::string, std::string> src2; 54 const Symbol *sym; 55 56 DuplicateSymbolDiag(const std::pair<std::string, std::string> src1, 57 const std::pair<std::string, std::string> src2, 58 const Symbol *sym) 59 : src1(src1), src2(src2), sym(sym) {} 60 }; 61 SmallVector<DuplicateSymbolDiag> dupSymDiags; 62 } // namespace 63 64 // Move symbols at \p fromOff in \p fromIsec into \p toIsec, unless that symbol 65 // is \p skip. 66 static void transplantSymbolsAtOffset(InputSection *fromIsec, 67 InputSection *toIsec, Defined *skip, 68 uint64_t fromOff, uint64_t toOff) { 69 // Ensure the symbols will still be in address order after our insertions. 70 auto insertIt = llvm::upper_bound(toIsec->symbols, toOff, 71 [](uint64_t off, const Symbol *s) { 72 return cast<Defined>(s)->value < off; 73 }); 74 llvm::erase_if(fromIsec->symbols, [&](Symbol *s) { 75 auto *d = cast<Defined>(s); 76 if (d->value != fromOff) 77 return false; 78 if (d != skip) { 79 // This repeated insertion will be quadratic unless insertIt is the end 80 // iterator. However, that is typically the case for files that have 81 // .subsections_via_symbols set. 82 insertIt = toIsec->symbols.insert(insertIt, d); 83 d->isec = toIsec; 84 d->value = toOff; 85 // We don't want to have more than one unwindEntry at a given address, so 86 // drop the redundant ones. We We can safely drop the unwindEntries of 87 // the symbols in fromIsec since we will be adding another unwindEntry as 88 // we finish parsing toIsec's file. (We can assume that toIsec has its 89 // own unwindEntry because of the ODR.) 90 d->unwindEntry = nullptr; 91 } 92 return true; 93 }); 94 } 95 96 Defined *SymbolTable::addDefined(StringRef name, InputFile *file, 97 InputSection *isec, uint64_t value, 98 uint64_t size, bool isWeakDef, 99 bool isPrivateExtern, 100 bool isReferencedDynamically, bool noDeadStrip, 101 bool isWeakDefCanBeHidden) { 102 bool overridesWeakDef = false; 103 auto [s, wasInserted] = insert(name, file); 104 105 assert(!file || !isa<BitcodeFile>(file) || !isec); 106 107 if (!wasInserted) { 108 if (auto *defined = dyn_cast<Defined>(s)) { 109 if (isWeakDef) { 110 // See further comment in createDefined() in InputFiles.cpp 111 if (defined->isWeakDef()) { 112 defined->privateExtern &= isPrivateExtern; 113 defined->weakDefCanBeHidden &= isWeakDefCanBeHidden; 114 defined->referencedDynamically |= isReferencedDynamically; 115 defined->noDeadStrip |= noDeadStrip; 116 } 117 if (auto concatIsec = dyn_cast_or_null<ConcatInputSection>(isec)) { 118 concatIsec->wasCoalesced = true; 119 // Any local symbols that alias the coalesced symbol should be moved 120 // into the prevailing section. Note that we have sorted the symbols 121 // in ObjFile::parseSymbols() such that extern weak symbols appear 122 // last, so we don't need to worry about subsequent symbols being 123 // added to an already-coalesced section. 124 if (defined->isec) 125 transplantSymbolsAtOffset(concatIsec, defined->isec, 126 /*skip=*/nullptr, value, defined->value); 127 } 128 return defined; 129 } 130 131 if (defined->isWeakDef()) { 132 if (auto concatIsec = 133 dyn_cast_or_null<ConcatInputSection>(defined->isec)) { 134 concatIsec->wasCoalesced = true; 135 if (isec) 136 transplantSymbolsAtOffset(concatIsec, isec, defined, defined->value, 137 value); 138 } 139 } else { 140 std::string srcLoc1 = defined->getSourceLocation(); 141 std::string srcLoc2 = isec ? isec->getSourceLocation(value) : ""; 142 std::string srcFile1 = toString(defined->getFile()); 143 std::string srcFile2 = toString(file); 144 145 dupSymDiags.push_back({make_pair(srcLoc1, srcFile1), 146 make_pair(srcLoc2, srcFile2), defined}); 147 } 148 149 } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) { 150 overridesWeakDef = !isWeakDef && dysym->isWeakDef(); 151 dysym->unreference(); 152 } else if (auto *undef = dyn_cast<Undefined>(s)) { 153 // Preserve the original bitcode file name (instead of using the object 154 // file name). 155 if (undef->wasBitcodeSymbol) 156 file = undef->getFile(); 157 } 158 // Defined symbols take priority over other types of symbols, so in case 159 // of a name conflict, we fall through to the replaceSymbol() call below. 160 } 161 162 // With -flat_namespace, all extern symbols in dylibs are interposable. 163 // FIXME: Add support for `-interposable` (PR53680). 164 bool interposable = config->namespaceKind == NamespaceKind::flat && 165 config->outputType != MachO::MH_EXECUTE && 166 !isPrivateExtern; 167 Defined *defined = replaceSymbol<Defined>( 168 s, name, file, isec, value, size, isWeakDef, /*isExternal=*/true, 169 isPrivateExtern, /*includeInSymtab=*/true, isReferencedDynamically, 170 noDeadStrip, overridesWeakDef, isWeakDefCanBeHidden, interposable); 171 return defined; 172 } 173 174 Defined *SymbolTable::aliasDefined(Defined *src, StringRef target, 175 InputFile *newFile, bool makePrivateExtern) { 176 bool isPrivateExtern = makePrivateExtern || src->privateExtern; 177 return addDefined(target, newFile, src->isec, src->value, src->size, 178 src->isWeakDef(), isPrivateExtern, 179 src->referencedDynamically, src->noDeadStrip, 180 src->weakDefCanBeHidden); 181 } 182 183 Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file, 184 bool isWeakRef) { 185 auto [s, wasInserted] = insert(name, file); 186 187 RefState refState = isWeakRef ? RefState::Weak : RefState::Strong; 188 189 if (wasInserted) 190 replaceSymbol<Undefined>(s, name, file, refState, 191 /*wasBitcodeSymbol=*/false); 192 else if (auto *lazy = dyn_cast<LazyArchive>(s)) 193 lazy->fetchArchiveMember(); 194 else if (isa<LazyObject>(s)) 195 extract(*s->getFile(), s->getName()); 196 else if (auto *dynsym = dyn_cast<DylibSymbol>(s)) 197 dynsym->reference(refState); 198 else if (auto *undefined = dyn_cast<Undefined>(s)) 199 undefined->refState = std::max(undefined->refState, refState); 200 return s; 201 } 202 203 Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size, 204 uint32_t align, bool isPrivateExtern) { 205 auto [s, wasInserted] = insert(name, file); 206 207 if (!wasInserted) { 208 if (auto *common = dyn_cast<CommonSymbol>(s)) { 209 if (size < common->size) 210 return s; 211 } else if (isa<Defined>(s)) { 212 return s; 213 } 214 // Common symbols take priority over all non-Defined symbols, so in case of 215 // a name conflict, we fall through to the replaceSymbol() call below. 216 } 217 218 replaceSymbol<CommonSymbol>(s, name, file, size, align, isPrivateExtern); 219 return s; 220 } 221 222 Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef, 223 bool isTlv) { 224 auto [s, wasInserted] = insert(name, file); 225 226 RefState refState = RefState::Unreferenced; 227 if (!wasInserted) { 228 if (auto *defined = dyn_cast<Defined>(s)) { 229 if (isWeakDef && !defined->isWeakDef()) 230 defined->overridesWeakDef = true; 231 } else if (auto *undefined = dyn_cast<Undefined>(s)) { 232 refState = undefined->refState; 233 } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) { 234 refState = dysym->getRefState(); 235 } 236 } 237 238 bool isDynamicLookup = file == nullptr; 239 if (wasInserted || isa<Undefined>(s) || 240 (isa<DylibSymbol>(s) && 241 ((!isWeakDef && s->isWeakDef()) || 242 (!isDynamicLookup && cast<DylibSymbol>(s)->isDynamicLookup())))) { 243 if (auto *dynsym = dyn_cast<DylibSymbol>(s)) 244 dynsym->unreference(); 245 replaceSymbol<DylibSymbol>(s, file, name, isWeakDef, refState, isTlv); 246 } 247 248 return s; 249 } 250 251 Symbol *SymbolTable::addDynamicLookup(StringRef name) { 252 return addDylib(name, /*file=*/nullptr, /*isWeakDef=*/false, /*isTlv=*/false); 253 } 254 255 Symbol *SymbolTable::addLazyArchive(StringRef name, ArchiveFile *file, 256 const object::Archive::Symbol &sym) { 257 auto [s, wasInserted] = insert(name, file); 258 259 if (wasInserted) { 260 replaceSymbol<LazyArchive>(s, file, sym); 261 } else if (isa<Undefined>(s)) { 262 file->fetch(sym); 263 } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) { 264 if (dysym->isWeakDef()) { 265 if (dysym->getRefState() != RefState::Unreferenced) 266 file->fetch(sym); 267 else 268 replaceSymbol<LazyArchive>(s, file, sym); 269 } 270 } 271 return s; 272 } 273 274 Symbol *SymbolTable::addLazyObject(StringRef name, InputFile &file) { 275 auto [s, wasInserted] = insert(name, &file); 276 277 if (wasInserted) { 278 replaceSymbol<LazyObject>(s, file, name); 279 } else if (isa<Undefined>(s)) { 280 extract(file, name); 281 } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) { 282 if (dysym->isWeakDef()) { 283 if (dysym->getRefState() != RefState::Unreferenced) 284 extract(file, name); 285 else 286 replaceSymbol<LazyObject>(s, file, name); 287 } 288 } 289 return s; 290 } 291 292 Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec, 293 uint64_t value, bool isPrivateExtern, 294 bool includeInSymtab, 295 bool referencedDynamically) { 296 assert(!isec || !isec->getFile()); // See makeSyntheticInputSection(). 297 Defined *s = addDefined(name, /*file=*/nullptr, isec, value, /*size=*/0, 298 /*isWeakDef=*/false, isPrivateExtern, 299 referencedDynamically, /*noDeadStrip=*/false, 300 /*isWeakDefCanBeHidden=*/false); 301 s->includeInSymtab = includeInSymtab; 302 return s; 303 } 304 305 enum class Boundary { 306 Start, 307 End, 308 }; 309 310 static Defined *createBoundarySymbol(const Undefined &sym) { 311 return symtab->addSynthetic( 312 sym.getName(), /*isec=*/nullptr, /*value=*/-1, /*isPrivateExtern=*/true, 313 /*includeInSymtab=*/false, /*referencedDynamically=*/false); 314 } 315 316 static void handleSectionBoundarySymbol(const Undefined &sym, StringRef segSect, 317 Boundary which) { 318 auto [segName, sectName] = segSect.split('$'); 319 320 // Attach the symbol to any InputSection that will end up in the right 321 // OutputSection -- it doesn't matter which one we pick. 322 // Don't bother looking through inputSections for a matching 323 // ConcatInputSection -- we need to create ConcatInputSection for 324 // non-existing sections anyways, and that codepath works even if we should 325 // already have a ConcatInputSection with the right name. 326 327 OutputSection *osec = nullptr; 328 // This looks for __TEXT,__cstring etc. 329 for (SyntheticSection *ssec : syntheticSections) 330 if (ssec->segname == segName && ssec->name == sectName) { 331 osec = ssec->isec->parent; 332 break; 333 } 334 335 if (!osec) { 336 ConcatInputSection *isec = makeSyntheticInputSection(segName, sectName); 337 338 // This runs after markLive() and is only called for Undefineds that are 339 // live. Marking the isec live ensures an OutputSection is created that the 340 // start/end symbol can refer to. 341 assert(sym.isLive()); 342 isec->live = true; 343 344 // This runs after gatherInputSections(), so need to explicitly set parent 345 // and add to inputSections. 346 osec = isec->parent = ConcatOutputSection::getOrCreateForInput(isec); 347 inputSections.push_back(isec); 348 } 349 350 if (which == Boundary::Start) 351 osec->sectionStartSymbols.push_back(createBoundarySymbol(sym)); 352 else 353 osec->sectionEndSymbols.push_back(createBoundarySymbol(sym)); 354 } 355 356 static void handleSegmentBoundarySymbol(const Undefined &sym, StringRef segName, 357 Boundary which) { 358 OutputSegment *seg = getOrCreateOutputSegment(segName); 359 if (which == Boundary::Start) 360 seg->segmentStartSymbols.push_back(createBoundarySymbol(sym)); 361 else 362 seg->segmentEndSymbols.push_back(createBoundarySymbol(sym)); 363 } 364 365 // Try to find a definition for an undefined symbol. 366 // Returns true if a definition was found and no diagnostics are needed. 367 static bool recoverFromUndefinedSymbol(const Undefined &sym) { 368 // Handle start/end symbols. 369 StringRef name = sym.getName(); 370 if (name.consume_front("section$start$")) { 371 handleSectionBoundarySymbol(sym, name, Boundary::Start); 372 return true; 373 } 374 if (name.consume_front("section$end$")) { 375 handleSectionBoundarySymbol(sym, name, Boundary::End); 376 return true; 377 } 378 if (name.consume_front("segment$start$")) { 379 handleSegmentBoundarySymbol(sym, name, Boundary::Start); 380 return true; 381 } 382 if (name.consume_front("segment$end$")) { 383 handleSegmentBoundarySymbol(sym, name, Boundary::End); 384 return true; 385 } 386 387 // Leave dtrace symbols, since we will handle them when we do the relocation 388 if (name.starts_with("___dtrace_")) 389 return true; 390 391 // Handle -U. 392 if (config->explicitDynamicLookups.count(sym.getName())) { 393 symtab->addDynamicLookup(sym.getName()); 394 return true; 395 } 396 397 // Handle -undefined. 398 if (config->undefinedSymbolTreatment == 399 UndefinedSymbolTreatment::dynamic_lookup || 400 config->undefinedSymbolTreatment == UndefinedSymbolTreatment::suppress) { 401 symtab->addDynamicLookup(sym.getName()); 402 return true; 403 } 404 405 // We do not return true here, as we still need to print diagnostics. 406 if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::warning) 407 symtab->addDynamicLookup(sym.getName()); 408 409 return false; 410 } 411 412 namespace { 413 struct UndefinedDiag { 414 struct SectionAndOffset { 415 const InputSection *isec; 416 uint64_t offset; 417 }; 418 419 std::vector<SectionAndOffset> codeReferences; 420 std::vector<std::string> otherReferences; 421 }; 422 423 MapVector<const Undefined *, UndefinedDiag> undefs; 424 } // namespace 425 426 void macho::reportPendingDuplicateSymbols() { 427 for (const auto &duplicate : dupSymDiags) { 428 if (!config->deadStripDuplicates || duplicate.sym->isLive()) { 429 std::string message = 430 "duplicate symbol: " + toString(*duplicate.sym) + "\n>>> defined in "; 431 if (!duplicate.src1.first.empty()) 432 message += duplicate.src1.first + "\n>>> "; 433 message += duplicate.src1.second + "\n>>> defined in "; 434 if (!duplicate.src2.first.empty()) 435 message += duplicate.src2.first + "\n>>> "; 436 error(message + duplicate.src2.second); 437 } 438 } 439 } 440 441 // Check whether the definition name def is a mangled function name that matches 442 // the reference name ref. 443 static bool canSuggestExternCForCXX(StringRef ref, StringRef def) { 444 llvm::ItaniumPartialDemangler d; 445 std::string name = def.str(); 446 if (d.partialDemangle(name.c_str())) 447 return false; 448 char *buf = d.getFunctionName(nullptr, nullptr); 449 if (!buf) 450 return false; 451 bool ret = ref == buf; 452 free(buf); 453 return ret; 454 } 455 456 // Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns 457 // the suggested symbol, which is either in the symbol table, or in the same 458 // file of sym. 459 static const Symbol *getAlternativeSpelling(const Undefined &sym, 460 std::string &preHint, 461 std::string &postHint) { 462 DenseMap<StringRef, const Symbol *> map; 463 if (sym.getFile() && sym.getFile()->kind() == InputFile::ObjKind) { 464 // Build a map of local defined symbols. 465 for (const Symbol *s : sym.getFile()->symbols) 466 if (auto *defined = dyn_cast_or_null<Defined>(s)) 467 if (!defined->isExternal()) 468 map.try_emplace(s->getName(), s); 469 } 470 471 auto suggest = [&](StringRef newName) -> const Symbol * { 472 // If defined locally. 473 if (const Symbol *s = map.lookup(newName)) 474 return s; 475 476 // If in the symbol table and not undefined. 477 if (const Symbol *s = symtab->find(newName)) 478 if (dyn_cast<Undefined>(s) == nullptr) 479 return s; 480 481 return nullptr; 482 }; 483 484 // This loop enumerates all strings of Levenshtein distance 1 as typo 485 // correction candidates and suggests the one that exists as a non-undefined 486 // symbol. 487 StringRef name = sym.getName(); 488 for (size_t i = 0, e = name.size(); i != e + 1; ++i) { 489 // Insert a character before name[i]. 490 std::string newName = (name.substr(0, i) + "0" + name.substr(i)).str(); 491 for (char c = '0'; c <= 'z'; ++c) { 492 newName[i] = c; 493 if (const Symbol *s = suggest(newName)) 494 return s; 495 } 496 if (i == e) 497 break; 498 499 // Substitute name[i]. 500 newName = std::string(name); 501 for (char c = '0'; c <= 'z'; ++c) { 502 newName[i] = c; 503 if (const Symbol *s = suggest(newName)) 504 return s; 505 } 506 507 // Transpose name[i] and name[i+1]. This is of edit distance 2 but it is 508 // common. 509 if (i + 1 < e) { 510 newName[i] = name[i + 1]; 511 newName[i + 1] = name[i]; 512 if (const Symbol *s = suggest(newName)) 513 return s; 514 } 515 516 // Delete name[i]. 517 newName = (name.substr(0, i) + name.substr(i + 1)).str(); 518 if (const Symbol *s = suggest(newName)) 519 return s; 520 } 521 522 // Case mismatch, e.g. Foo vs FOO. 523 for (auto &it : map) 524 if (name.equals_insensitive(it.first)) 525 return it.second; 526 for (Symbol *sym : symtab->getSymbols()) 527 if (dyn_cast<Undefined>(sym) == nullptr && 528 name.equals_insensitive(sym->getName())) 529 return sym; 530 531 // The reference may be a mangled name while the definition is not. Suggest a 532 // missing extern "C". 533 if (name.starts_with("__Z")) { 534 std::string buf = name.str(); 535 llvm::ItaniumPartialDemangler d; 536 if (!d.partialDemangle(buf.c_str())) 537 if (char *buf = d.getFunctionName(nullptr, nullptr)) { 538 const Symbol *s = suggest((Twine("_") + buf).str()); 539 free(buf); 540 if (s) { 541 preHint = ": extern \"C\" "; 542 return s; 543 } 544 } 545 } else { 546 StringRef nameWithoutUnderscore = name; 547 nameWithoutUnderscore.consume_front("_"); 548 const Symbol *s = nullptr; 549 for (auto &it : map) 550 if (canSuggestExternCForCXX(nameWithoutUnderscore, it.first)) { 551 s = it.second; 552 break; 553 } 554 if (!s) 555 for (Symbol *sym : symtab->getSymbols()) 556 if (canSuggestExternCForCXX(nameWithoutUnderscore, sym->getName())) { 557 s = sym; 558 break; 559 } 560 if (s) { 561 preHint = " to declare "; 562 postHint = " as extern \"C\"?"; 563 return s; 564 } 565 } 566 567 return nullptr; 568 } 569 570 static void reportUndefinedSymbol(const Undefined &sym, 571 const UndefinedDiag &locations, 572 bool correctSpelling) { 573 std::string message = "undefined symbol"; 574 if (config->archMultiple) 575 message += (" for arch " + getArchitectureName(config->arch())).str(); 576 message += ": " + toString(sym); 577 578 const size_t maxUndefinedReferences = 3; 579 size_t i = 0; 580 for (const std::string &loc : locations.otherReferences) { 581 if (i >= maxUndefinedReferences) 582 break; 583 message += "\n>>> referenced by " + loc; 584 ++i; 585 } 586 587 for (const UndefinedDiag::SectionAndOffset &loc : locations.codeReferences) { 588 if (i >= maxUndefinedReferences) 589 break; 590 message += "\n>>> referenced by "; 591 std::string src = loc.isec->getSourceLocation(loc.offset); 592 if (!src.empty()) 593 message += src + "\n>>> "; 594 message += loc.isec->getLocation(loc.offset); 595 ++i; 596 } 597 598 size_t totalReferences = 599 locations.otherReferences.size() + locations.codeReferences.size(); 600 if (totalReferences > i) 601 message += 602 ("\n>>> referenced " + Twine(totalReferences - i) + " more times") 603 .str(); 604 605 if (correctSpelling) { 606 std::string preHint = ": ", postHint; 607 if (const Symbol *corrected = 608 getAlternativeSpelling(sym, preHint, postHint)) { 609 message += 610 "\n>>> did you mean" + preHint + toString(*corrected) + postHint; 611 if (corrected->getFile()) 612 message += "\n>>> defined in: " + toString(corrected->getFile()); 613 } 614 } 615 616 if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error) 617 error(message); 618 else if (config->undefinedSymbolTreatment == 619 UndefinedSymbolTreatment::warning) 620 warn(message); 621 else 622 assert(false && "diagnostics make sense for -undefined error|warning only"); 623 } 624 625 void macho::reportPendingUndefinedSymbols() { 626 // Enable spell corrector for the first 2 diagnostics. 627 for (const auto &[i, undef] : llvm::enumerate(undefs)) 628 reportUndefinedSymbol(*undef.first, undef.second, i < 2); 629 630 // This function is called multiple times during execution. Clear the printed 631 // diagnostics to avoid printing the same things again the next time. 632 undefs.clear(); 633 } 634 635 void macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) { 636 if (recoverFromUndefinedSymbol(sym)) 637 return; 638 639 undefs[&sym].otherReferences.push_back(source.str()); 640 } 641 642 void macho::treatUndefinedSymbol(const Undefined &sym, const InputSection *isec, 643 uint64_t offset) { 644 if (recoverFromUndefinedSymbol(sym)) 645 return; 646 647 undefs[&sym].codeReferences.push_back({isec, offset}); 648 } 649 650 std::unique_ptr<SymbolTable> macho::symtab; 651