Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -185,6 +185,7 @@ // If no information is available, returns "". std::string getLineInfo(InputSectionBase *S, uint64_t Offset); llvm::Optional getDILineInfo(InputSectionBase *, uint64_t); + llvm::Optional> getVariableLoc(StringRef Name); // MIPS GP0 value defined by this file. This value represents the gp value // used to create the relocatable object and required to support @@ -200,7 +201,7 @@ void initializeSections(llvm::DenseSet &ComdatGroups); void initializeSymbols(); - void initializeDwarfLine(); + void initializeDwarfData(); InputSectionBase *getRelocTarget(const Elf_Shdr &Sec); InputSectionBase *createInputSection(const Elf_Shdr &Sec); StringRef getSectionName(const Elf_Shdr &Sec); @@ -216,6 +217,7 @@ // single object file, so we cache debugging information in order to // parse it only once for each object file we link. std::unique_ptr DwarfLine; + llvm::DenseMap> VariablesLoc; llvm::once_flag InitDwarfLine; }; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -67,7 +67,7 @@ return MBRef; } -template void ObjFile::initializeDwarfLine() { +template void ObjFile::initializeDwarfData() { DWARFContext Dwarf(make_unique>(this)); const DWARFObject &Obj = Dwarf.getDWARFObj(); DwarfLine.reset(new DWARFDebugLine); @@ -77,7 +77,56 @@ // 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); + const DWARFDebugLine::LineTable *LT = + DwarfLine->getOrParseLineTable(LineData, 0); + + // Return if there is no debug information about CU available. + if (!Dwarf.getNumCompileUnits()) + return; + + // Get information about global variables location. + DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0); + for (const auto &Entry : CU->dies()) { + DWARFDie Die = {CU, &Entry}; + // Skip all tags that are not variables. + if (Die.getTag() != dwarf::DW_TAG_variable) + continue; + + // Skip variables that are invisible outside the compilation unit. + if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0)) + continue; + + unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0); + if (!LT->hasFileAtIndex(File)) + continue; + unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); + StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), ""); + if (!Name.empty()) + VariablesLoc.insert({Name, {File, Line}}); + } +} + +template +llvm::Optional> +ObjFile::getVariableLoc(StringRef Name) { + llvm::call_once(InitDwarfLine, [this]() { initializeDwarfData(); }); + + // The offset to CU is 0. + const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0); + if (!LT) + return None; + + auto It = VariablesLoc.find(Name); + if (It == VariablesLoc.end()) + return None; + + std::string FileName; + if (!LT->getFileNameByIndex( + It->second.first /* File */, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName)) + return None; + + return std::make_pair(FileName, It->second.second /*Line*/); } // Returns source line information for a given offset @@ -85,7 +134,7 @@ template Optional ObjFile::getDILineInfo(InputSectionBase *S, uint64_t Offset) { - llvm::call_once(InitDwarfLine, [this]() { initializeDwarfLine(); }); + llvm::call_once(InitDwarfLine, [this]() { initializeDwarfData(); }); // The offset to CU is 0. const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0); Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -183,7 +183,7 @@ // Returns a source location string. Used to construct an error message. template std::string getLocation(uint64_t Offset); - template std::string getSrcMsg(uint64_t Offset); + template std::string getSrcMsg(const SymbolBody &Sym, uint64_t Offset); template std::string getObjMsg(uint64_t Offset); // Each section knows how to relocate itself. These functions apply Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -278,6 +278,14 @@ return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str(); } +static std::string getFileLineMsg(StringRef Path, unsigned Line) { + std::string Filename = path::filename(Path); + std::string Lineno = ":" + std::to_string(Line); + if (Filename == Path) + return Filename + Lineno; + return Filename + Lineno + " (" + Path.str() + Lineno + ")"; +} + // Returns a source location string. This function is intended to be // used for constructing an error message. The returned message looks // like this: @@ -285,24 +293,41 @@ // foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42) // // Returns an empty string if there's no way to get line info. -template std::string InputSectionBase::getSrcMsg(uint64_t Offset) { +template +std::string InputSectionBase::getSrcMsg(const SymbolBody &Sym, + uint64_t Offset) { // Synthetic sections don't have input files. ObjFile *File = getFile(); if (!File) return ""; - Optional Info = File->getDILineInfo(this, Offset); + // We want to get location string here. For functions we take information from + // .debug_line which contains association between location in source files and + // machine instuctions. But for data objects we have to scan DIEs in .debug_info + // which describes variables and their locations. + if (Sym.Type == STT_OBJECT) { + if (llvm::Optional> FileLine = + File->getVariableLoc(Sym.getName())) + return getFileLineMsg(FileLine->first, FileLine->second); + } else if (Optional Info = File->getDILineInfo(this, Offset)) { + return getFileLineMsg(Info->FileName, Info->Line); + } // File->SourceFile contains STT_FILE symbol, and that is a last resort. - if (!Info) - return File->SourceFile; + return File->SourceFile; +} - std::string Path = Info->FileName; - std::string Filename = path::filename(Path); - std::string Lineno = ":" + std::to_string(Info->Line); - if (Filename == Path) - return Filename + Lineno; - return Filename + Lineno + " (" + Path + Lineno + ")"; +template +std::string InputSectionBase::getVarSrcMsg(StringRef Name) { + ObjFile *File = getFile(); + if (!File) + return ""; + + llvm::Optional> FileLine = + File->getVariableLoc(Name); + if (!FileLine) + return ""; + return getFileLineMsg(FileLine->first, FileLine->second); } // Returns a filename string along with an optional section name. This @@ -1013,10 +1038,14 @@ template std::string InputSectionBase::getLocation(uint64_t); template std::string InputSectionBase::getLocation(uint64_t); -template std::string InputSectionBase::getSrcMsg(uint64_t); -template std::string InputSectionBase::getSrcMsg(uint64_t); -template std::string InputSectionBase::getSrcMsg(uint64_t); -template std::string InputSectionBase::getSrcMsg(uint64_t); +template std::string InputSectionBase::getSrcMsg(const SymbolBody &, + uint64_t); +template std::string InputSectionBase::getSrcMsg(const SymbolBody &, + uint64_t); +template std::string InputSectionBase::getSrcMsg(const SymbolBody &, + uint64_t); +template std::string InputSectionBase::getSrcMsg(const SymbolBody &, + uint64_t); template std::string InputSectionBase::getObjMsg(uint64_t); template std::string InputSectionBase::getObjMsg(uint64_t); Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -74,7 +74,7 @@ uint64_t Off) { std::string Msg = "\n>>> defined in " + toString(Sym.getFile()) + "\n>>> referenced by "; - std::string Src = S.getSrcMsg(Off); + std::string Src = S.getSrcMsg(Sym, Off); if (!Src.empty()) Msg += Src + "\n>>> "; return Msg + S.getObjMsg(Off); @@ -710,7 +710,7 @@ std::string Msg = "undefined symbol: " + toString(Sym) + "\n>>> referenced by "; - std::string Src = S.getSrcMsg(Offset); + std::string Src = S.getSrcMsg(Sym, Offset); if (!Src.empty()) Msg += Src + "\n>>> "; Msg += S.getObjMsg(Offset); Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -457,9 +457,9 @@ // >>> defined at baz.c:563 // >>> baz.o in archive libbaz.a auto *Sec1 = cast(D->Section); - std::string Src1 = Sec1->getSrcMsg(D->Value); + std::string Src1 = Sec1->getSrcMsg(*Sym, D->Value); std::string Obj1 = Sec1->getObjMsg(D->Value); - std::string Src2 = ErrSec->getSrcMsg(ErrOffset); + std::string Src2 = ErrSec->getSrcMsg(*Sym, ErrOffset); std::string Obj2 = ErrSec->getObjMsg(ErrOffset); std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at "; Index: test/ELF/Inputs/conflict-debug-variable.s =================================================================== --- test/ELF/Inputs/conflict-debug-variable.s +++ test/ELF/Inputs/conflict-debug-variable.s @@ -0,0 +1,119 @@ +# Used reduced output from following code and gcc 7.1.0 +# to produce this input file: +# source: int foo = 0; int bar = 1; +# invocation: g++ -g -S test.c + +.file "test.c" +.text +.Ltext0: + .globl foo + .bss + .align 4 + .type foo, @object + .size foo, 4 +foo: + .zero 4 + +.globl bar + .data + .align 4 + .type bar, @object + .size bar, 4 +bar: + .long 1 + +.text +.Letext0: +.file 1 "test.c" + +.section .debug_info,"",@progbits +.Ldebug_info0: + .long 0x4b + .value 0x4 + .long .Ldebug_abbrev0 + .byte 0x8 + .uleb128 0x1 + .long 0 + .byte 0x4 + .string "1.c" + .long 0 + .long 0 + .uleb128 0x2 + .string "foo" + .byte 0x1 + .byte 0x1 + .long 0x32 + .uleb128 0x9 + .byte 0x3 + .quad foo + .uleb128 0x3 + .byte 0x4 + .byte 0x5 + .string "int" + .uleb128 0x2 + .string "bar" + .byte 0x1 + .byte 0x2 + .long 0x32 + .uleb128 0x9 + .byte 0x3 + .quad bar + .byte 0 + +.section .debug_abbrev,"",@progbits +.Ldebug_abbrev0: + .uleb128 0x1 + .uleb128 0x11 + .byte 0x1 + .uleb128 0x25 + .uleb128 0xe + .uleb128 0x13 + .uleb128 0xb + .uleb128 0x3 + .uleb128 0x8 + .uleb128 0x1b + .uleb128 0xe + .uleb128 0x10 + .uleb128 0x17 + .byte 0 + .byte 0 + .uleb128 0x2 + .uleb128 0x34 + .byte 0 + .uleb128 0x3 + .uleb128 0x8 + .uleb128 0x3a + .uleb128 0xb + .uleb128 0x3b + .uleb128 0xb + .uleb128 0x49 + .uleb128 0x13 + .uleb128 0x3f + .uleb128 0x19 + .uleb128 0x2 + .uleb128 0x18 + .byte 0 + .byte 0 + .uleb128 0x3 + .uleb128 0x24 + .byte 0 + .uleb128 0xb + .uleb128 0xb + .uleb128 0x3e + .uleb128 0xb + .uleb128 0x3 + .uleb128 0x8 + .byte 0 + .byte 0 + .byte 0 + +.section .debug_aranges,"",@progbits + .long 0x1c + .value 0x2 + .long .Ldebug_info0 + .byte 0x8 + .byte 0 + .value 0 + .value 0 + .quad 0 + .quad 0 Index: test/ELF/conflict.s =================================================================== --- test/ELF/conflict.s +++ test/ELF/conflict.s @@ -42,6 +42,22 @@ # DBGINFO-NEXT: >>> defined at conflict-debug.s:4 # DBGINFO-NEXT: >>> {{.*}}:(.text+0x0) +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +# RUN: %p/Inputs/conflict-debug-variable.s -o %t-dbg2.o +# RUN: not ld.lld %t-dbg2.o %t-dbg2.o -o %t-dbg2 2>&1 | \ +# RUN: FileCheck -check-prefix=DBGINFOVAR %s + +# DBGINFOVAR: duplicate symbol: bar +# DBGINFOVAR-NEXT: >>> defined at test.c:2 +# DBGINFOVAR-NEXT: >>> {{.*}}:(bar) +# DBGINFOVAR-NEXT: >>> defined at test.c:2 +# DBGINFOVAR-NEXT: >>> {{.*}}:(.data+0x0) +# DBGINFOVAR: duplicate symbol: foo +# DBGINFOVAR-NEXT: >>> defined at test.c:1 +# DBGINFOVAR-NEXT: >>> {{.*}}:(foo) +# DBGINFOVAR-NEXT: >>> defined at test.c:1 +# DBGINFOVAR-NEXT: >>> {{.*}}:(.bss+0x0) + .globl _Z3muldd, foo _Z3muldd: foo: