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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lld/MachO/SymbolTable.cpp