Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -29,6 +29,7 @@ #include namespace llvm { +class DWARFDebugLine; namespace lto { class InputFile; } @@ -43,6 +44,23 @@ 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 DIHelper { +public: + typedef typename ELFT::uint uintX_t; + + DIHelper(InputFile *F); + ~DIHelper(); + std::string getLineInfo(uintX_t Offset); + +private: + llvm::DWARFDebugLine *DwarfLine = nullptr; +}; + // The root class of input files. class InputFile { public: @@ -171,6 +189,10 @@ const Elf_Shdr *getSymbolTable() const { return this->Symtab; }; + // DI helper allows manipilating debugging information for this + // object file. Used for error reporting. + DIHelper *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 +206,11 @@ // 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 &ComdatGroups); @@ -211,6 +238,7 @@ llvm::SpecificBumpPtrAllocator> IAlloc; llvm::SpecificBumpPtrAllocator> MAlloc; llvm::SpecificBumpPtrAllocator> EHAlloc; + std::unique_ptr> DIH; }; // LazyObjectFile is analogous to ArchiveFile in the sense that Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ 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,41 @@ std::vector InputFile::Pool; +template DIHelper::DIHelper(elf::InputFile *F) { + Expected> Obj = + object::ObjectFile::createObjectFile(F->MB); + if (!Obj) + return; + + DWARFContextInMemory Dwarf(*Obj.get()); + DwarfLine = 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 DIHelper::~DIHelper() { delete DwarfLine; } + +template std::string DIHelper::getLineInfo(uintX_t Offset) { + DILineInfo LineInfo; + DILineInfoSpecifier Spec; + if (!DwarfLine) + return ""; + + // 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 +168,13 @@ return makeArrayRef(this->SymbolBodies).slice(1); } +template DIHelper *elf::ObjectFile::getDIHelper() { + if (!DIH) + DIH.reset(new DIHelper(this)); + + return DIH.get(); +} + template uint32_t elf::ObjectFile::getMipsGp0() const { if (ELFT::Is64Bits && MipsOptions && MipsOptions->Reginfo) return MipsOptions->Reginfo->ri_gp_value; @@ -432,6 +475,8 @@ int Binding = Sym->getBinding(); InputSectionBase *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); @@ -904,3 +949,8 @@ template InputFile *BinaryFile::createELF(); template InputFile *BinaryFile::createELF(); template InputFile *BinaryFile::createELF(); + +template class elf::DIHelper; +template class elf::DIHelper; +template class elf::DIHelper; +template class elf::DIHelper; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -522,7 +522,48 @@ return Addend; } -static void reportUndefined(SymbolBody &Sym) { +// Find symbol that encloses given offset. Used for error reporting. +template +static DefinedRegular *getSymbolAt(InputSectionBase *S, + typename ELFT::uint Offset) { + for (SymbolBody *B : S->getFile()->getSymbols()) + if (auto *D = dyn_cast>(B)) + if (D->Value <= Offset && D->Value + D->Size > Offset && D->Section == S) + return D; + + return nullptr; +} + +template +static std::string getLocation(SymbolBody &Sym, InputSectionBase &S, + typename ELFT::uint Offset) { + ObjectFile *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 *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 +static void reportUndefined(SymbolBody &Sym, InputSectionBase &S, + typename ELFT::uint Offset) { if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore) return; @@ -530,15 +571,15 @@ Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef) return; - std::string Msg = "undefined symbol: "; - Msg += Config->Demangle ? demangle(Sym.getName()) : Sym.getName().str(); + std::string Location = getLocation(Sym, S, Offset); + std::string Msg = + "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); + warn(Location + ": " + Msg); else - error(Msg); + error(Location + ": " + Msg); } // The reason we have to do this early scan is as follows @@ -583,7 +624,7 @@ // 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); Index: test/ELF/Inputs/undef-debug.s =================================================================== --- test/ELF/Inputs/undef-debug.s +++ test/ELF/Inputs/undef-debug.s @@ -0,0 +1,3 @@ +.file 1 "undef-debug.s" +.loc 1 3 + .quad zed3 Index: test/ELF/libsearch.s =================================================================== --- test/ELF/libsearch.s +++ 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 \ Index: test/ELF/linkerscript/edata-etext.s =================================================================== --- test/ELF/linkerscript/edata-etext.s +++ 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 Index: test/ELF/linkerscript/ehdr_start.s =================================================================== --- test/ELF/linkerscript/ehdr_start.s +++ 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 Index: test/ELF/lto/combined-lto-object-name.ll =================================================================== --- test/ELF/lto/combined-lto-object-name.ll +++ test/ELF/lto/combined-lto-object-name.ll @@ -11,4 +11,4 @@ ret void } -; CHECK: undefined symbol: foo in {{.*}}combined-lto-object-name.ll.tmp.o +; CHECK: error: ld-temp.o (function _start): undefined symbol 'foo' Index: test/ELF/sysroot.s =================================================================== --- test/ELF/sysroot.s +++ 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 \ Index: test/ELF/tls-static.s =================================================================== --- test/ELF/tls-static.s +++ 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' Index: test/ELF/undef-shared.s =================================================================== --- test/ELF/undef-shared.s +++ 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 Index: test/ELF/undef.s =================================================================== --- test/ELF/undef.s +++ 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: Index: test/ELF/unresolved-symbols.s =================================================================== --- test/ELF/unresolved-symbols.s +++ 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 Index: test/ELF/verneed-local.s =================================================================== --- test/ELF/verneed-local.s +++ 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 Index: test/ELF/zdefs.s =================================================================== --- test/ELF/zdefs.s +++ 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