[lld] r363962 - lld/elf: Deduplicate undefined symbol diagnostics

Nico Weber via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 20 11:25:57 PDT 2019


Author: nico
Date: Thu Jun 20 11:25:57 2019
New Revision: 363962

URL: http://llvm.org/viewvc/llvm-project?rev=363962&view=rev
Log:
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

Added:
    lld/trunk/test/ELF/undef-multi.s
Modified:
    lld/trunk/ELF/Relocations.cpp
    lld/trunk/ELF/Relocations.h
    lld/trunk/ELF/Writer.cpp
    lld/trunk/test/ELF/debug-line-obj.s

Modified: lld/trunk/ELF/Relocations.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Relocations.cpp?rev=363962&r1=363961&r2=363962&view=diff
==============================================================================
--- lld/trunk/ELF/Relocations.cpp (original)
+++ lld/trunk/ELF/Relocations.cpp Thu Jun 20 11:25:57 2019
@@ -678,27 +678,24 @@ static std::string maybeReportDiscarded(
   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
   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;
+  }
+
+  for (const UndefinedDiag &Undef : Undefs) {
+    if (!Undef.Locs.empty())
+      reportUndefinedSymbol<ELFT>(Undef);
   }
+  Undefs.clear();
+}
 
-  error(Msg);
-  return true;
+// 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
             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<ELF32
 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>();

Modified: lld/trunk/ELF/Relocations.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Relocations.h?rev=363962&r1=363961&r2=363962&view=diff
==============================================================================
--- lld/trunk/ELF/Relocations.h (original)
+++ lld/trunk/ELF/Relocations.h Thu Jun 20 11:25:57 2019
@@ -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;

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=363962&r1=363961&r2=363962&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Thu Jun 20 11:25:57 2019
@@ -1737,8 +1737,10 @@ template <class ELFT> void Writer<ELFT>:
 
   // 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();
 

Modified: lld/trunk/test/ELF/debug-line-obj.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/debug-line-obj.s?rev=363962&r1=363961&r2=363962&view=diff
==============================================================================
--- lld/trunk/test/ELF/debug-line-obj.s (original)
+++ lld/trunk/test/ELF/debug-line-obj.s Thu Jun 20 11:25:57 2019
@@ -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())
 

Added: lld/trunk/test/ELF/undef-multi.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/undef-multi.s?rev=363962&view=auto
==============================================================================
--- lld/trunk/test/ELF/undef-multi.s (added)
+++ lld/trunk/test/ELF/undef-multi.s Thu Jun 20 11:25:57 2019
@@ -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




More information about the llvm-commits mailing list