diff options
Diffstat (limited to 'lld/ELF/Relocations.cpp')
| -rw-r--r-- | lld/ELF/Relocations.cpp | 121 |
1 files changed, 91 insertions, 30 deletions
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 0c33e47c29d..8b8a3b1d4f9 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -678,27 +678,24 @@ static std::string maybeReportDiscarded(Undefined &Sym) { return Msg; } -// Report an undefined symbol if necessary. -// Returns true if this function printed out an error message. -template <class ELFT> -static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, - uint64_t Offset) { - if (!Sym.isUndefined() || Sym.isWeak()) - return false; +// Undefined diagnostics are collected in a vector and emitted once all of +// them are known, so that some postprocessing on the list of undefined symbols +// can happen before lld emits diagnostics. +struct UndefinedDiag { + Symbol *Sym; + struct Loc { + InputSectionBase *Sec; + uint64_t Offset; + }; + std::vector<Loc> Locs; + bool IsWarning; +}; - bool CanBeExternal = !Sym.isLocal() && Sym.computeBinding() != STB_LOCAL && - Sym.Visibility == STV_DEFAULT; - if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) - return false; +static std::vector<UndefinedDiag> Undefs; - // clang (as of 2019-06-12) / gcc (as of 8.2.1) PPC64 may emit a .rela.toc - // which references a switch table in a discarded .rodata/.text section. The - // .toc and the .rela.toc are incorrectly not placed in the comdat. The ELF - // spec says references from outside the group to a STB_LOCAL symbol are not - // allowed. Work around the bug. - if (Config->EMachine == EM_PPC64 && - cast<Undefined>(Sym).DiscardedSecIdx != 0 && Sec.Name == ".toc") - return false; +template <class ELFT> +static void reportUndefinedSymbol(const UndefinedDiag &Undef) { + Symbol &Sym = *Undef.Sym; auto Visibility = [&]() -> std::string { switch (Sym.Visibility) { @@ -717,24 +714,83 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, if (Msg.empty()) Msg = "undefined " + Visibility() + "symbol: " + toString(Sym); - Msg += "\n>>> referenced by "; - std::string Src = Sec.getSrcMsg(Sym, Offset); - if (!Src.empty()) - Msg += Src + "\n>>> "; - Msg += Sec.getObjMsg(Offset); + const size_t MaxUndefReferences = 10; + size_t I = 0; + for (UndefinedDiag::Loc L : Undef.Locs) { + if (I >= MaxUndefReferences) + break; + InputSectionBase &Sec = *L.Sec; + uint64_t Offset = L.Offset; + + Msg += "\n>>> referenced by "; + std::string Src = Sec.getSrcMsg(Sym, Offset); + if (!Src.empty()) + Msg += Src + "\n>>> "; + Msg += Sec.getObjMsg(Offset); + I++; + } + + if (I < Undef.Locs.size()) + Msg += ("\n>>> referenced " + Twine(Undef.Locs.size() - I) + " more times") + .str(); if (Sym.getName().startswith("_ZTV")) Msg += "\nthe vtable symbol may be undefined because the class is missing " "its key function (see https://lld.llvm.org/missingkeyfunction)"; - if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) || - Config->NoinhibitExec) { + if (Undef.IsWarning) warn(Msg); - return false; + else + error(Msg); +} + +template <class ELFT> void elf::reportUndefinedSymbols() { + // Find the first "undefined symbol" diagnostic for each diagnostic, and + // collect all "referenced from" lines at the first diagnostic. + DenseMap<Symbol *, UndefinedDiag *> FirstRef; + for (UndefinedDiag &Undef : Undefs) { + assert(Undef.Locs.size() == 1); + if (UndefinedDiag *Canon = FirstRef.lookup(Undef.Sym)) { + Canon->Locs.push_back(Undef.Locs[0]); + Undef.Locs.clear(); + } else + FirstRef[Undef.Sym] = &Undef; } - error(Msg); - return true; + for (const UndefinedDiag &Undef : Undefs) { + if (!Undef.Locs.empty()) + reportUndefinedSymbol<ELFT>(Undef); + } + Undefs.clear(); +} + +// Report an undefined symbol if necessary. +// Returns true if the undefined symbol will produce an error message. +template <class ELFT> +static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, + uint64_t Offset) { + if (!Sym.isUndefined() || Sym.isWeak()) + return false; + + bool CanBeExternal = !Sym.isLocal() && Sym.computeBinding() != STB_LOCAL && + Sym.Visibility == STV_DEFAULT; + if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) + return false; + + // clang (as of 2019-06-12) / gcc (as of 8.2.1) PPC64 may emit a .rela.toc + // which references a switch table in a discarded .rodata/.text section. The + // .toc and the .rela.toc are incorrectly not placed in the comdat. The ELF + // spec says references from outside the group to a STB_LOCAL symbol are not + // allowed. Work around the bug. + if (Config->EMachine == EM_PPC64 && + cast<Undefined>(Sym).DiscardedSecIdx != 0 && Sec.Name == ".toc") + return false; + + bool IsWarning = + (Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) || + Config->NoinhibitExec; + Undefs.push_back({&Sym, {{&Sec, Offset}}, IsWarning}); + return !IsWarning; } // MIPS N32 ABI treats series of successive relocations with the same offset @@ -1734,7 +1790,8 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) { Rel.Sym = T->getThunkTargetSym(); Rel.Expr = fromPlt(Rel.Expr); - // Addend of R_PPC_PLTREL24 should be ignored after changing to R_PC. + // The addend of R_PPC_PLTREL24 should be ignored after changing to + // R_PC. if (Config->EMachine == EM_PPC && Rel.Type == R_PPC_PLTREL24) Rel.Addend = 0; } @@ -1756,3 +1813,7 @@ template void elf::scanRelocations<ELF32LE>(InputSectionBase &); template void elf::scanRelocations<ELF32BE>(InputSectionBase &); template void elf::scanRelocations<ELF64LE>(InputSectionBase &); template void elf::scanRelocations<ELF64BE>(InputSectionBase &); +template void elf::reportUndefinedSymbols<ELF32LE>(); +template void elf::reportUndefinedSymbols<ELF32BE>(); +template void elf::reportUndefinedSymbols<ELF64LE>(); +template void elf::reportUndefinedSymbols<ELF64BE>(); |

