summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNico Weber <nicolasweber@gmx.de>2019-06-20 18:25:57 +0000
committerNico Weber <nicolasweber@gmx.de>2019-06-20 18:25:57 +0000
commit2c450434152d383ed9722a5a82864e1d49c248ec (patch)
tree4e71891c5d5ff23ed95304b33b8abb4b91667321
parent6d9fb68c536d910818b1a051d0829ea32feadf43 (diff)
downloadbcm5719-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.cpp121
-rw-r--r--lld/ELF/Relocations.h5
-rw-r--r--lld/ELF/Writer.cpp4
-rw-r--r--lld/test/ELF/debug-line-obj.s1
-rw-r--r--lld/test/ELF/undef-multi.s65
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
OpenPOWER on IntegriCloud