diff options
| author | Nico Weber <nicolasweber@gmx.de> | 2019-06-20 18:25:57 +0000 |
|---|---|---|
| committer | Nico Weber <nicolasweber@gmx.de> | 2019-06-20 18:25:57 +0000 |
| commit | 2c450434152d383ed9722a5a82864e1d49c248ec (patch) | |
| tree | 4e71891c5d5ff23ed95304b33b8abb4b91667321 | |
| parent | 6d9fb68c536d910818b1a051d0829ea32feadf43 (diff) | |
| download | bcm5719-llvm-2c450434152d383ed9722a5a82864e1d49c248ec.tar.gz bcm5719-llvm-2c450434152d383ed9722a5a82864e1d49c248ec.zip | |
lld/elf: Deduplicate undefined symbol diagnostics
Before:
```
ld.lld: error: undefined symbol: f()
>>> referenced by test.cc:3
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-9c0808.o:(g())
ld.lld: error: undefined symbol: f()
>>> referenced by test.cc:4
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-9c0808.o:(h())
ld.lld: error: undefined symbol: f()
>>> referenced by test.cc:5
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-9c0808.o:(j())
ld.lld: error: undefined symbol: k()
>>> referenced by test.cc:5
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-9c0808.o:(j())
ld.lld: error: undefined symbol: f()
>>> referenced by test2.cc:2
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test2-07b391.o:(asdf())
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```
Now:
```
ld.lld: error: undefined symbol: f()
>>> referenced by test.cc:3
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-0e07ba.o:(g())
>>> referenced by test.cc:4
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-0e07ba.o:(h())
>>> referenced by test.cc:5
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-0e07ba.o:(j())
>>> referenced by test2.cc:2
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test2-6bdb24.o:(asdf())
ld.lld: error: undefined symbol: k()
>>> referenced by test.cc:5
>>> /var/folders/c5/8d7sdn1x2mg92mj0rndghhdr0000gn/T/test-0e07ba.o:(j())
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```
If there are more than 10 references to an undefined symbol, only the
first 10 are printed.
Fixes PR42260.
Differential Revision: https://reviews.llvm.org/D63344
llvm-svn: 363962
| -rw-r--r-- | lld/ELF/Relocations.cpp | 121 | ||||
| -rw-r--r-- | lld/ELF/Relocations.h | 5 | ||||
| -rw-r--r-- | lld/ELF/Writer.cpp | 4 | ||||
| -rw-r--r-- | lld/test/ELF/debug-line-obj.s | 1 | ||||
| -rw-r--r-- | lld/test/ELF/undef-multi.s | 65 |
5 files changed, 164 insertions, 32 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>(); diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index dd040569824..a008a6f1b18 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -109,8 +109,13 @@ struct Relocation { Symbol *Sym; }; +// This function writes undefined symbol diagnostics to an internal buffer. +// Call reportUndefinedSymbols() after calling scanRelocations() to emit +// the diagnostics. template <class ELFT> void scanRelocations(InputSectionBase &); +template <class ELFT> void reportUndefinedSymbols(); + void addIRelativeRelocs(); class ThunkSection; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index a667c9b28ab..82010302ff7 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1737,8 +1737,10 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { // Scan relocations. This must be done after every symbol is declared so that // we can correctly decide if a dynamic relocation is needed. - if (!Config->Relocatable) + if (!Config->Relocatable) { forEachRelSec(scanRelocations<ELFT>); + reportUndefinedSymbols<ELFT>(); + } addIRelativeRelocs(); diff --git a/lld/test/ELF/debug-line-obj.s b/lld/test/ELF/debug-line-obj.s index 32182a15810..020678bb631 100644 --- a/lld/test/ELF/debug-line-obj.s +++ b/lld/test/ELF/debug-line-obj.s @@ -10,7 +10,6 @@ # CHECK: error: undefined symbol: foo() # CHECK-NEXT: >>> referenced by test.cpp:2 # CHECK-NEXT: >>> {{.*}}.o:(bar()) -# CHECK: error: undefined symbol: foo() # CHECK-NEXT: >>> referenced by test.cpp:3 # CHECK-NEXT: >>> {{.*}}.o:(baz()) diff --git a/lld/test/ELF/undef-multi.s b/lld/test/ELF/undef-multi.s new file mode 100644 index 00000000000..627235b37b1 --- /dev/null +++ b/lld/test/ELF/undef-multi.s @@ -0,0 +1,65 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef.s -o %t2.o +# RUN: not ld.lld %t.o %t2.o -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: undefined symbol: zed2 +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0x1) +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0x6) +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0xB) +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0x10) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.o:(.text+0x0) + +# All references to a single undefined symbol count as a single error -- but +# at most 10 references are printed. +# RUN: echo ".globl _bar" > %t.moreref.s +# RUN: echo "_bar:" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: echo " call zed2" >> %t.moreref.s +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t.moreref.s -o %t3.o +# RUN: not ld.lld %t.o %t2.o %t3.o -o /dev/null -error-limit=2 2>&1 | \ +# RUN: FileCheck --check-prefix=LIMIT %s + +# LIMIT: error: undefined symbol: zed2 +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0x1) +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0x6) +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0xB) +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0x10) +# LIMIT-NEXT: >>> referenced by {{.*}}tmp2.o:(.text+0x0) +# LIMIT-NEXT: >>> referenced by {{.*}}tmp3.o:(.text+0x1) +# LIMIT-NEXT: >>> referenced by {{.*}}tmp3.o:(.text+0x6) +# LIMIT-NEXT: >>> referenced by {{.*}}tmp3.o:(.text+0xB) +# LIMIT-NEXT: >>> referenced by {{.*}}tmp3.o:(.text+0x10) +# LIMIT-NEXT: >>> referenced by {{.*}}tmp3.o:(.text+0x15) +# LIMIT-NEXT: >>> referenced 2 more times + +.file "undef-multi.s" + + .globl _start +_start: + call zed2 + + .globl _f +_f: + call zed2 + + .globl _g +_g: + call zed2 + + .globl _h +_h: + call zed2 |

