[lld] r285186 - [ELF] Better error reporting for undefined symbols

Eugene Leviant via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 26 04:07:10 PDT 2016


Author: evgeny777
Date: Wed Oct 26 06:07:09 2016
New Revision: 285186

URL: http://llvm.org/viewvc/llvm-project?rev=285186&view=rev
Log:
[ELF] Better error reporting for undefined symbols

This patch make lld show following details for undefined symbol errors:
- file (line)
- file (function name)
- file (section name + offset)

Differential revision: https://reviews.llvm.org/D25826

Added:
    lld/trunk/test/ELF/Inputs/undef-debug.s
Modified:
    lld/trunk/ELF/InputFiles.cpp
    lld/trunk/ELF/InputFiles.h
    lld/trunk/ELF/Relocations.cpp
    lld/trunk/test/ELF/libsearch.s
    lld/trunk/test/ELF/linkerscript/edata-etext.s
    lld/trunk/test/ELF/linkerscript/ehdr_start.s
    lld/trunk/test/ELF/lto/combined-lto-object-name.ll
    lld/trunk/test/ELF/sysroot.s
    lld/trunk/test/ELF/tls-static.s
    lld/trunk/test/ELF/undef-shared.s
    lld/trunk/test/ELF/undef.s
    lld/trunk/test/ELF/unresolved-symbols.s
    lld/trunk/test/ELF/verneed-local.s
    lld/trunk/test/ELF/zdefs.s

Modified: lld/trunk/ELF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputFiles.cpp?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/ELF/InputFiles.cpp (original)
+++ lld/trunk/ELF/InputFiles.cpp Wed Oct 26 06:07:09 2016
@@ -18,6 +18,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Bitcode/ReaderWriter.h"
 #include "llvm/CodeGen/Analysis.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
 #include "llvm/LTO/LTO.h"
@@ -35,6 +36,39 @@ using namespace lld::elf;
 
 std::vector<InputFile *> InputFile::Pool;
 
+template <class ELFT> DIHelper<ELFT>::DIHelper(elf::InputFile *F) {
+  Expected<std::unique_ptr<object::ObjectFile>> Obj =
+      object::ObjectFile::createObjectFile(F->MB);
+  if (!Obj)
+    return;
+
+  DWARFContextInMemory Dwarf(*Obj.get());
+  DwarfLine.reset(new DWARFDebugLine(&Dwarf.getLineSection().Relocs));
+  DataExtractor LineData(Dwarf.getLineSection().Data,
+                         ELFT::TargetEndianness == support::little,
+                         ELFT::Is64Bits ? 8 : 4);
+  // The second parameter is offset in .debug_line section
+  // for compilation unit (CU) of interest. We have only one
+  // CU (object file), so offset is always 0.
+  DwarfLine->getOrParseLineTable(LineData, 0);
+}
+
+template <class ELFT> std::string DIHelper<ELFT>::getLineInfo(uintX_t Offset) {
+  if (!DwarfLine)
+    return "";
+
+  DILineInfo LineInfo;
+  DILineInfoSpecifier Spec;
+  // The offset to CU is 0 (see DIHelper constructor).
+  const DWARFDebugLine::LineTable *LineTbl = DwarfLine->getLineTable(0);
+  if (!LineTbl)
+    return "";
+  LineTbl->getFileLineInfoForAddress(Offset, nullptr, Spec.FLIKind, LineInfo);
+  return LineInfo.Line != 0
+             ? LineInfo.FileName + " (" + std::to_string(LineInfo.Line) + ")"
+             : "";
+}
+
 // Deletes all InputFile instances created so far.
 void InputFile::freePool() {
   // Files are freed in reverse order so that files created
@@ -132,6 +166,13 @@ ArrayRef<SymbolBody *> elf::ObjectFile<E
   return makeArrayRef(this->SymbolBodies).slice(1);
 }
 
+template <class ELFT> DIHelper<ELFT> *elf::ObjectFile<ELFT>::getDIHelper() {
+  if (!DIH)
+    DIH.reset(new DIHelper<ELFT>(this));
+
+  return DIH.get();
+}
+
 template <class ELFT> uint32_t elf::ObjectFile<ELFT>::getMipsGp0() const {
   if (ELFT::Is64Bits && MipsOptions && MipsOptions->Reginfo)
     return MipsOptions->Reginfo->ri_gp_value;
@@ -432,6 +473,8 @@ SymbolBody *elf::ObjectFile<ELFT>::creat
   int Binding = Sym->getBinding();
   InputSectionBase<ELFT> *Sec = getSection(*Sym);
   if (Binding == STB_LOCAL) {
+    if (Sym->getType() == STT_FILE)
+      SourceFile = check(Sym->getName(this->StringTable));
     if (Sym->st_shndx == SHN_UNDEF)
       return new (this->Alloc)
           Undefined(Sym->st_name, Sym->st_other, Sym->getType(), this);
@@ -897,3 +940,8 @@ template InputFile *BinaryFile::createEL
 template InputFile *BinaryFile::createELF<ELF32BE>();
 template InputFile *BinaryFile::createELF<ELF64LE>();
 template InputFile *BinaryFile::createELF<ELF64BE>();
+
+template class elf::DIHelper<ELF32LE>;
+template class elf::DIHelper<ELF32BE>;
+template class elf::DIHelper<ELF64LE>;
+template class elf::DIHelper<ELF64BE>;

Modified: lld/trunk/ELF/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputFiles.h?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/ELF/InputFiles.h (original)
+++ lld/trunk/ELF/InputFiles.h Wed Oct 26 06:07:09 2016
@@ -29,6 +29,7 @@
 #include <map>
 
 namespace llvm {
+class DWARFDebugLine;
 namespace lto {
 class InputFile;
 }
@@ -43,6 +44,21 @@ class InputFile;
 class Lazy;
 class SymbolBody;
 
+// Debugging information helper class. The main purpose is to
+// retrieve source file and line for error reporting. Linker may
+// find reasonable number of errors in a single object file, so
+// we cache debugging information in order to parse it only once
+// for each object file we link.
+template <class ELFT> class DIHelper {
+public:
+  typedef typename ELFT::uint uintX_t;
+
+  DIHelper(InputFile *F);
+  std::string getLineInfo(uintX_t Offset);
+private:
+  std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
+};
+
 // The root class of input files.
 class InputFile {
 public:
@@ -171,6 +187,10 @@ public:
 
   const Elf_Shdr *getSymbolTable() const { return this->Symtab; };
 
+  // DI helper allows manipilating debugging information for this
+  // object file. Used for error reporting.
+  DIHelper<ELFT> *getDIHelper();
+
   // Get MIPS GP0 value defined by this file. This value represents the gp value
   // used to create the relocatable object and required to support
   // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
@@ -184,6 +204,11 @@ public:
   // using this buffer.
   llvm::BumpPtrAllocator Alloc;
 
+  // Name of source file obtained from STT_FILE symbol value,
+  // or empty string if there is no such symbol in object file
+  // symbol table.
+  StringRef SourceFile;
+
 private:
   void
   initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
@@ -211,6 +236,7 @@ private:
   llvm::SpecificBumpPtrAllocator<InputSection<ELFT>> IAlloc;
   llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc;
   llvm::SpecificBumpPtrAllocator<EhInputSection<ELFT>> EHAlloc;
+  std::unique_ptr<DIHelper<ELFT>> DIH;
 };
 
 // LazyObjectFile is analogous to ArchiveFile in the sense that

Modified: lld/trunk/ELF/Relocations.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Relocations.cpp?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/ELF/Relocations.cpp (original)
+++ lld/trunk/ELF/Relocations.cpp Wed Oct 26 06:07:09 2016
@@ -522,7 +522,48 @@ static typename ELFT::uint computeAddend
   return Addend;
 }
 
-static void reportUndefined(SymbolBody &Sym) {
+// Find symbol that encloses given offset. Used for error reporting.
+template <class ELFT>
+static DefinedRegular<ELFT> *getSymbolAt(InputSectionBase<ELFT> *S,
+                                         typename ELFT::uint Offset) {
+  for (SymbolBody *B : S->getFile()->getSymbols())
+    if (auto *D = dyn_cast<DefinedRegular<ELFT>>(B))
+      if (D->Value <= Offset && D->Value + D->Size > Offset && D->Section == S)
+        return D;
+
+  return nullptr;
+}
+
+template <class ELFT>
+static std::string getLocation(SymbolBody &Sym, InputSectionBase<ELFT> &S,
+                               typename ELFT::uint Offset) {
+  ObjectFile<ELFT> *File = S.getFile();
+
+  // First check if we can get desired values from debugging information.
+  std::string LineInfo = File->getDIHelper()->getLineInfo(Offset);
+  if (!LineInfo.empty())
+    return LineInfo;
+
+  // If don't have STT_FILE typed symbol in object file then
+  // use object file name.
+  std::string SrcFile = File->SourceFile;
+  if (SrcFile.empty())
+    SrcFile = Sym.File ? getFilename(Sym.File) : getFilename(File);
+
+  DefinedRegular<ELFT> *Encl = getSymbolAt(&S, Offset);
+  if (Encl && Encl->Type == STT_FUNC) {
+    StringRef Func = getSymbolName(*File, *Encl);
+    return SrcFile + " (function " +
+           (Config->Demangle ? demangle(Func) : Func.str()) + ")";
+  }
+
+  return (SrcFile + " (" + S.Name + "+0x" + Twine::utohexstr(Offset) + ")")
+      .str();
+}
+
+template <class ELFT>
+static void reportUndefined(SymbolBody &Sym, InputSectionBase<ELFT> &S,
+                            typename ELFT::uint Offset) {
   if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
     return;
 
@@ -530,11 +571,10 @@ static void reportUndefined(SymbolBody &
       Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef)
     return;
 
-  std::string Msg = "undefined symbol: ";
-  Msg += Config->Demangle ? demangle(Sym.getName()) : Sym.getName().str();
+  std::string Msg =
+      getLocation(Sym, S, Offset) + ": undefined symbol '" +
+      (Config->Demangle ? demangle(Sym.getName()) : Sym.getName().str()) + "'";
 
-  if (Sym.File)
-    Msg += " in " + getFilename(Sym.File);
   if (Config->UnresolvedSymbols == UnresolvedPolicy::Warn)
     warn(Msg);
   else
@@ -583,7 +623,7 @@ static void scanRelocs(InputSectionBase<
     // We only report undefined symbols if they are referenced somewhere in the
     // code.
     if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak())
-      reportUndefined(Body);
+      reportUndefined(Body, C, RI.r_offset);
 
     RelExpr Expr = Target->getRelExpr(Type, Body);
     bool Preemptible = isPreemptible(Body, Type);

Added: lld/trunk/test/ELF/Inputs/undef-debug.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/Inputs/undef-debug.s?rev=285186&view=auto
==============================================================================
--- lld/trunk/test/ELF/Inputs/undef-debug.s (added)
+++ lld/trunk/test/ELF/Inputs/undef-debug.s Wed Oct 26 06:07:09 2016
@@ -0,0 +1,3 @@
+.file 1 "undef-debug.s"
+.loc 1 3
+        .quad zed3

Modified: lld/trunk/test/ELF/libsearch.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/libsearch.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/libsearch.s (original)
+++ lld/trunk/test/ELF/libsearch.s Wed Oct 26 06:07:09 2016
@@ -22,7 +22,7 @@
 // Should not link because of undefined symbol _bar
 // RUN: not ld.lld -o %t3 %t.o %tbar.o 2>&1 \
 // RUN:   | FileCheck --check-prefix=UNDEFINED %s
-// UNDEFINED: undefined symbol: _bar
+// UNDEFINED: error: {{.*}} (.bar+0x0): undefined symbol '_bar'
 
 // Should fail if cannot find specified library (without -L switch)
 // RUN: not ld.lld -o %t3 %t.o -lls 2>&1 \

Modified: lld/trunk/test/ELF/linkerscript/edata-etext.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/linkerscript/edata-etext.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/linkerscript/edata-etext.s (original)
+++ lld/trunk/test/ELF/linkerscript/edata-etext.s Wed Oct 26 06:07:09 2016
@@ -2,9 +2,9 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
 # RUN: echo "SECTIONS { }" > %t.script
 # RUN: not ld.lld %t.o -script %t.script -o %t 2>&1 | FileCheck %s
-# CHECK: undefined symbol: _edata
-# CHECK: undefined symbol: _etext
-# CHECK: undefined symbol: _end
+# CHECK: error: {{.*}} (.text+0x0): undefined symbol '_edata'
+# CHECK: error: {{.*}} (.text+0x8): undefined symbol '_etext'
+# CHECK: error: {{.*}} (.text+0x10): undefined symbol '_end'
 
 .global _start,_end,_etext,_edata
 .text

Modified: lld/trunk/test/ELF/linkerscript/ehdr_start.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/linkerscript/ehdr_start.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/linkerscript/ehdr_start.s (original)
+++ lld/trunk/test/ELF/linkerscript/ehdr_start.s Wed Oct 26 06:07:09 2016
@@ -3,7 +3,7 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
 # RUN: echo "SECTIONS { }" > %t.script
 # RUN: not ld.lld %t.o -script %t.script -o %t 2>&1 | FileCheck %s
-# CHECK: undefined symbol: __ehdr_start
+# CHECK: error: {{.*}} (.text+0x0): undefined symbol '__ehdr_start'
 
 .text
 .global _start, __ehdr_start

Modified: lld/trunk/test/ELF/lto/combined-lto-object-name.ll
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/lto/combined-lto-object-name.ll?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/lto/combined-lto-object-name.ll (original)
+++ lld/trunk/test/ELF/lto/combined-lto-object-name.ll Wed Oct 26 06:07:09 2016
@@ -11,4 +11,4 @@ define void @_start() {
   ret void
 }
 
-; CHECK: undefined symbol: foo in {{.*}}combined-lto-object-name.ll.tmp.o
+; CHECK: error: ld-temp.o (function _start): undefined symbol 'foo'

Modified: lld/trunk/test/ELF/sysroot.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/sysroot.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/sysroot.s (original)
+++ lld/trunk/test/ELF/sysroot.s Wed Oct 26 06:07:09 2016
@@ -9,7 +9,7 @@
 // Should not link because of undefined symbol _bar
 // RUN: not ld.lld -o %t/r %t/m.o 2>&1 \
 // RUN:     | FileCheck --check-prefix=UNDEFINED %s
-// UNDEFINED: undefined symbol: _bar
+// UNDEFINED: error: {{.*}} (.text+0x1): undefined symbol '_bar'
 
 // We need to be sure that there is no suitable library in the /lib directory
 // RUN: not ld.lld -o %t/r %t/m.o -L/lib -l:libls.a 2>&1 \

Modified: lld/trunk/test/ELF/tls-static.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/tls-static.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/tls-static.s (original)
+++ lld/trunk/test/ELF/tls-static.s Wed Oct 26 06:07:09 2016
@@ -10,4 +10,4 @@
 _start:
   call __tls_get_addr
 
-// CHECK: undefined symbol: __tls_get_addr
+// CHECK: error: {{.*}} (.text+0x1): undefined symbol '__tls_get_addr'

Modified: lld/trunk/test/ELF/undef-shared.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/undef-shared.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/undef-shared.s (original)
+++ lld/trunk/test/ELF/undef-shared.s Wed Oct 26 06:07:09 2016
@@ -1,15 +1,15 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
 # RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
 
-# CHECK: undefined symbol: hidden in {{.*}}
+# CHECK: error: {{.*}} (.data+0x0): undefined symbol 'hidden'
 .global hidden
 .hidden hidden
 
-# CHECK: undefined symbol: internal in {{.*}}
+# CHECK: error: {{.*}} (.data+0x8): undefined symbol 'internal'
 .global internal
 .internal internal
 
-# CHECK: undefined symbol: protected in {{.*}}
+# CHECK: error: {{.*}} (.data+0x10): undefined symbol 'protected'
 .global protected
 .protected protected
 

Modified: lld/trunk/test/ELF/undef.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/undef.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/undef.s (original)
+++ lld/trunk/test/ELF/undef.s Wed Oct 26 06:07:09 2016
@@ -1,17 +1,21 @@
 # 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: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef-debug.s -o %t3.o
 # RUN: llvm-ar rc %t2.a %t2.o
-# RUN: not ld.lld %t.o %t2.a -o %t.exe 2>&1 | FileCheck %s
-# RUN: not ld.lld -pie %t.o %t2.a -o %t.exe 2>&1 | FileCheck %s
-# CHECK: undefined symbol: foo in
-# CHECK: undefined symbol: bar in
-# CHECK: undefined symbol: foo(int) in
-# CHECK: undefined symbol: zed2 in {{.*}}2.a({{.*}}.o)
+# RUN: not ld.lld %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s
+# RUN: not ld.lld -pie %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s
+# CHECK: error: undef.s (.text+0x1): undefined symbol 'foo'
+# CHECK: error: undef.s (.text+0x6): undefined symbol 'bar'
+# CHECK: error: undef.s (.text+0x10): undefined symbol 'foo(int)'
+# CHECK: error: {{.*}}2.a({{.*}}.o) (.text+0x0): undefined symbol 'zed2'
+# CHECK: error: undef-debug.s (3): undefined symbol 'zed3'
 
 # RUN: not ld.lld %t.o %t2.a -o %t.exe -no-demangle 2>&1 | \
 # RUN:   FileCheck -check-prefix=NO-DEMANGLE %s
-# NO-DEMANGLE: undefined symbol: _Z3fooi in
+# NO-DEMANGLE: error: undef.s (.text+0x10): undefined symbol '_Z3fooi'
+
+.file "undef.s"
 
   .globl _start
 _start:

Modified: lld/trunk/test/ELF/unresolved-symbols.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/unresolved-symbols.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/unresolved-symbols.s (original)
+++ lld/trunk/test/ELF/unresolved-symbols.s Wed Oct 26 06:07:09 2016
@@ -6,7 +6,7 @@
 ## Check that %t2.o contains undefined symbol undef.
 # RUN: not ld.lld %t1.o %t2.o -o %t 2>&1 | \
 # RUN:   FileCheck -check-prefix=UNDCHECK %s
-# UNDCHECK: undefined symbol: undef in {{.*}}2.o
+# UNDCHECK: error: {{.*}}2.o (.text+0x1): undefined symbol 'undef'
 
 ## Error out if unknown option value was set.
 # RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols=xxx 2>&1 | \
@@ -19,7 +19,7 @@
 # RUN: llvm-readobj %t1_1 > /dev/null 2>&1
 # RUN: not ld.lld %t2.o -o %t1_2 --unresolved-symbols=ignore-all --no-undefined 2>&1 | \
 # RUN:   FileCheck -check-prefix=ERRUND %s
-# ERRUND: undefined symbol: undef
+# ERRUND: error: {{.*}} (.text+0x1): undefined symbol 'undef'
 ## Also ignore all should not produce error for symbols from DSOs.
 # RUN: ld.lld %t1.o %t.so -o %t1_3 --unresolved-symbols=ignore-all
 # RUN: llvm-readobj %t1_3 > /dev/null 2>&1

Modified: lld/trunk/test/ELF/verneed-local.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/verneed-local.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/verneed-local.s (original)
+++ lld/trunk/test/ELF/verneed-local.s Wed Oct 26 06:07:09 2016
@@ -2,7 +2,7 @@
 # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
 # RUN: not ld.lld %t.o %S/Inputs/verneed1.so -o %t 2>&1 | FileCheck %s
 
-# CHECK: undefined symbol: f3 in
+# CHECK: error: {{.*}} (.text+0x1): undefined symbol 'f3'
 .globl _start
 _start:
 call f3

Modified: lld/trunk/test/ELF/zdefs.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/zdefs.s?rev=285186&r1=285185&r2=285186&view=diff
==============================================================================
--- lld/trunk/test/ELF/zdefs.s (original)
+++ lld/trunk/test/ELF/zdefs.s Wed Oct 26 06:07:09 2016
@@ -2,6 +2,6 @@
 # RUN: ld.lld -shared %t.o -o %t1.so
 
 # RUN: not ld.lld -z defs -shared %t.o -o %t1.so 2>&1 | FileCheck -check-prefix=ERR %s
-# ERR: undefined symbol: foo
+# ERR: error: {{.*}} (.text+0x1): undefined symbol 'foo'
 
 callq foo at PLT




More information about the llvm-commits mailing list