diff options
| -rw-r--r-- | lld/ELF/InputFiles.cpp | 48 | ||||
| -rw-r--r-- | lld/ELF/InputFiles.h | 26 | ||||
| -rw-r--r-- | lld/ELF/Relocations.cpp | 52 | ||||
| -rw-r--r-- | lld/test/ELF/Inputs/undef-debug.s | 3 | ||||
| -rw-r--r-- | lld/test/ELF/libsearch.s | 2 | ||||
| -rw-r--r-- | lld/test/ELF/linkerscript/edata-etext.s | 6 | ||||
| -rw-r--r-- | lld/test/ELF/linkerscript/ehdr_start.s | 2 | ||||
| -rw-r--r-- | lld/test/ELF/lto/combined-lto-object-name.ll | 2 | ||||
| -rw-r--r-- | lld/test/ELF/sysroot.s | 2 | ||||
| -rw-r--r-- | lld/test/ELF/tls-static.s | 2 | ||||
| -rw-r--r-- | lld/test/ELF/undef-shared.s | 6 | ||||
| -rw-r--r-- | lld/test/ELF/undef.s | 18 | ||||
| -rw-r--r-- | lld/test/ELF/unresolved-symbols.s | 4 | ||||
| -rw-r--r-- | lld/test/ELF/verneed-local.s | 2 | ||||
| -rw-r--r-- | lld/test/ELF/zdefs.s | 2 |
15 files changed, 149 insertions, 28 deletions
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index f03a5bdf05f..2dd34f0d5d8 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -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<ELFT>::getSymbols() { 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>::createSymbolBody(const Elf_Sym *Sym) { 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::createELF<ELF32LE>(); 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>; diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index cf188e2ad2a..3ac390e9282 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -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 diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index a75e9dbb31e..022495490a2 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -522,7 +522,48 @@ static typename ELFT::uint computeAddend(const elf::ObjectFile<ELFT> &File, 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 &Sym) { 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<ELFT> &C, ArrayRef<RelTy> Rels) { // 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); diff --git a/lld/test/ELF/Inputs/undef-debug.s b/lld/test/ELF/Inputs/undef-debug.s new file mode 100644 index 00000000000..0e27fe438f6 --- /dev/null +++ b/lld/test/ELF/Inputs/undef-debug.s @@ -0,0 +1,3 @@ +.file 1 "undef-debug.s" +.loc 1 3 + .quad zed3 diff --git a/lld/test/ELF/libsearch.s b/lld/test/ELF/libsearch.s index 92c5805fbc0..ca9fe2ed678 100644 --- a/lld/test/ELF/libsearch.s +++ b/lld/test/ELF/libsearch.s @@ -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 \ diff --git a/lld/test/ELF/linkerscript/edata-etext.s b/lld/test/ELF/linkerscript/edata-etext.s index 03e39b64110..e6da8d4a013 100644 --- a/lld/test/ELF/linkerscript/edata-etext.s +++ b/lld/test/ELF/linkerscript/edata-etext.s @@ -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 diff --git a/lld/test/ELF/linkerscript/ehdr_start.s b/lld/test/ELF/linkerscript/ehdr_start.s index ef3dc597cb8..48055d5b85e 100644 --- a/lld/test/ELF/linkerscript/ehdr_start.s +++ b/lld/test/ELF/linkerscript/ehdr_start.s @@ -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 diff --git a/lld/test/ELF/lto/combined-lto-object-name.ll b/lld/test/ELF/lto/combined-lto-object-name.ll index f5b7e3ae40e..60b72245a7c 100644 --- a/lld/test/ELF/lto/combined-lto-object-name.ll +++ b/lld/test/ELF/lto/combined-lto-object-name.ll @@ -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' diff --git a/lld/test/ELF/sysroot.s b/lld/test/ELF/sysroot.s index 663bf134ef7..6ddb6cf37c9 100644 --- a/lld/test/ELF/sysroot.s +++ b/lld/test/ELF/sysroot.s @@ -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 \ diff --git a/lld/test/ELF/tls-static.s b/lld/test/ELF/tls-static.s index 4c515abb125..31ae3e515f3 100644 --- a/lld/test/ELF/tls-static.s +++ b/lld/test/ELF/tls-static.s @@ -10,4 +10,4 @@ _start: call __tls_get_addr -// CHECK: undefined symbol: __tls_get_addr +// CHECK: error: {{.*}} (.text+0x1): undefined symbol '__tls_get_addr' diff --git a/lld/test/ELF/undef-shared.s b/lld/test/ELF/undef-shared.s index c34c71aa15b..6bb237e540d 100644 --- a/lld/test/ELF/undef-shared.s +++ b/lld/test/ELF/undef-shared.s @@ -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 diff --git a/lld/test/ELF/undef.s b/lld/test/ELF/undef.s index a0a5f0e8694..19bf451c290 100644 --- a/lld/test/ELF/undef.s +++ b/lld/test/ELF/undef.s @@ -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: diff --git a/lld/test/ELF/unresolved-symbols.s b/lld/test/ELF/unresolved-symbols.s index 2fa59cb0ffd..9c37421fb50 100644 --- a/lld/test/ELF/unresolved-symbols.s +++ b/lld/test/ELF/unresolved-symbols.s @@ -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 diff --git a/lld/test/ELF/verneed-local.s b/lld/test/ELF/verneed-local.s index a50f670ed7b..9da0673e7ea 100644 --- a/lld/test/ELF/verneed-local.s +++ b/lld/test/ELF/verneed-local.s @@ -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 diff --git a/lld/test/ELF/zdefs.s b/lld/test/ELF/zdefs.s index 410da1812db..333905c0ec2 100644 --- a/lld/test/ELF/zdefs.s +++ b/lld/test/ELF/zdefs.s @@ -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@PLT |

