Skip to content

Commit 82f0c42

Browse files
author
George Rimar
committed
[ELF] - Teach LLD to report line numbers for data symbols.
This is PR34826. Currently LLD is unable to report line number when reporting duplicate declaration of some variable. That happens because for extracting line information we always use .debug_line section content which describes mapping from machine instructions to source file locations, what does not help for variables as does not describe them. In this patch I am taking the approproate information about variables locations from the .debug_info section. Differential revision: https://reviews.llvm.org/D38721 llvm-svn: 317080
1 parent 688f0ca commit 82f0c42

7 files changed

+246
-28
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
6767
return MBRef;
6868
}
6969

70-
template <class ELFT> void ObjFile<ELFT>::initializeDwarfLine() {
70+
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
7171
DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
7272
const DWARFObject &Obj = Dwarf.getDWARFObj();
7373
DwarfLine.reset(new DWARFDebugLine);
@@ -77,15 +77,76 @@ template <class ELFT> void ObjFile<ELFT>::initializeDwarfLine() {
7777
// The second parameter is offset in .debug_line section
7878
// for compilation unit (CU) of interest. We have only one
7979
// CU (object file), so offset is always 0.
80-
DwarfLine->getOrParseLineTable(LineData, 0);
80+
const DWARFDebugLine::LineTable *LT =
81+
DwarfLine->getOrParseLineTable(LineData, 0);
82+
83+
// Return if there is no debug information about CU available.
84+
if (!Dwarf.getNumCompileUnits())
85+
return;
86+
87+
// Loop over variable records and insert them to VariableLoc.
88+
DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0);
89+
for (const auto &Entry : CU->dies()) {
90+
DWARFDie Die(CU, &Entry);
91+
// Skip all tags that are not variables.
92+
if (Die.getTag() != dwarf::DW_TAG_variable)
93+
continue;
94+
95+
// Skip if a local variable because we don't need them for generating error
96+
// messages. In general, only non-local symbols can fail to be linked.
97+
if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
98+
continue;
99+
100+
// Get the source filename index for the variable.
101+
unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
102+
if (!LT->hasFileAtIndex(File))
103+
continue;
104+
105+
// Get the line number on which the variable is declared.
106+
unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
107+
108+
// Get the name of the variable and add the collected information to
109+
// VariableLoc. Usually Name is non-empty, but it can be empty if the input
110+
// object file lacks some debug info.
111+
StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
112+
if (!Name.empty())
113+
VariableLoc.insert({Name, {File, Line}});
114+
}
115+
}
116+
117+
// Returns the pair of file name and line number describing location of data
118+
// object (variable, array, etc) definition.
119+
template <class ELFT>
120+
Optional<std::pair<std::string, unsigned>>
121+
ObjFile<ELFT>::getVariableLoc(StringRef Name) {
122+
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
123+
124+
// There is always only one CU so it's offset is 0.
125+
const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0);
126+
if (!LT)
127+
return None;
128+
129+
// Return if we have no debug information about data object.
130+
auto It = VariableLoc.find(Name);
131+
if (It == VariableLoc.end())
132+
return None;
133+
134+
// Take file name string from line table.
135+
std::string FileName;
136+
if (!LT->getFileNameByIndex(
137+
It->second.first /* File */, nullptr,
138+
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
139+
return None;
140+
141+
return std::make_pair(FileName, It->second.second /*Line*/);
81142
}
82143

83144
// Returns source line information for a given offset
84145
// using DWARF debug info.
85146
template <class ELFT>
86147
Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
87148
uint64_t Offset) {
88-
llvm::call_once(InitDwarfLine, [this]() { initializeDwarfLine(); });
149+
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
89150

90151
// The offset to CU is 0.
91152
const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);

lld/ELF/InputFiles.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
185185
// If no information is available, returns "".
186186
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
187187
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
188+
llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
188189

189190
// MIPS GP0 value defined by this file. This value represents the gp value
190191
// used to create the relocatable object and required to support
@@ -200,7 +201,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
200201
void
201202
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
202203
void initializeSymbols();
203-
void initializeDwarfLine();
204+
void initializeDwarf();
204205
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
205206
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
206207
StringRef getSectionName(const Elf_Shdr &Sec);
@@ -216,6 +217,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
216217
// single object file, so we cache debugging information in order to
217218
// parse it only once for each object file we link.
218219
std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
220+
llvm::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc;
219221
llvm::once_flag InitDwarfLine;
220222
};
221223

lld/ELF/InputSection.cpp

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -261,31 +261,41 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
261261
return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
262262
}
263263

264-
// Returns a source location string. This function is intended to be
265-
// used for constructing an error message. The returned message looks
266-
// like this:
264+
// Concatenates arguments to construct a string representing an error location.
265+
static std::string createFileLineMsg(StringRef Path, unsigned Line) {
266+
std::string Filename = path::filename(Path);
267+
std::string Lineno = ":" + std::to_string(Line);
268+
if (Filename == Path)
269+
return Filename + Lineno;
270+
return Filename + Lineno + " (" + Path.str() + Lineno + ")";
271+
}
272+
273+
// This function is intended to be used for constructing an error message.
274+
// The returned message looks like this:
267275
//
268276
// foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42)
269277
//
270-
// Returns an empty string if there's no way to get line info.
271-
template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
278+
// Returns an empty string if there's no way to get line info.
279+
template <class ELFT>
280+
std::string InputSectionBase::getSrcMsg(const SymbolBody &Sym,
281+
uint64_t Offset) {
272282
// Synthetic sections don't have input files.
273283
ObjFile<ELFT> *File = getFile<ELFT>();
274284
if (!File)
275285
return "";
276286

277-
Optional<DILineInfo> Info = File->getDILineInfo(this, Offset);
287+
// In DWARF, functions and variables are stored to different places.
288+
// First, lookup a function for a given offset.
289+
if (Optional<DILineInfo> Info = File->getDILineInfo(this, Offset))
290+
return createFileLineMsg(Info->FileName, Info->Line);
278291

279-
// File->SourceFile contains STT_FILE symbol, and that is a last resort.
280-
if (!Info)
281-
return File->SourceFile;
292+
// If it failed, lookup again as a variable.
293+
if (Optional<std::pair<std::string, unsigned>> FileLine =
294+
File->getVariableLoc(Sym.getName()))
295+
return createFileLineMsg(FileLine->first, FileLine->second);
282296

283-
std::string Path = Info->FileName;
284-
std::string Filename = path::filename(Path);
285-
std::string Lineno = ":" + std::to_string(Info->Line);
286-
if (Filename == Path)
287-
return Filename + Lineno;
288-
return Filename + Lineno + " (" + Path + Lineno + ")";
297+
// File->SourceFile contains STT_FILE symbol, and that is a last resort.
298+
return File->SourceFile;
289299
}
290300

291301
// Returns a filename string along with an optional section name. This
@@ -1004,10 +1014,14 @@ template std::string InputSectionBase::getLocation<ELF32BE>(uint64_t);
10041014
template std::string InputSectionBase::getLocation<ELF64LE>(uint64_t);
10051015
template std::string InputSectionBase::getLocation<ELF64BE>(uint64_t);
10061016

1007-
template std::string InputSectionBase::getSrcMsg<ELF32LE>(uint64_t);
1008-
template std::string InputSectionBase::getSrcMsg<ELF32BE>(uint64_t);
1009-
template std::string InputSectionBase::getSrcMsg<ELF64LE>(uint64_t);
1010-
template std::string InputSectionBase::getSrcMsg<ELF64BE>(uint64_t);
1017+
template std::string InputSectionBase::getSrcMsg<ELF32LE>(const SymbolBody &,
1018+
uint64_t);
1019+
template std::string InputSectionBase::getSrcMsg<ELF32BE>(const SymbolBody &,
1020+
uint64_t);
1021+
template std::string InputSectionBase::getSrcMsg<ELF64LE>(const SymbolBody &,
1022+
uint64_t);
1023+
template std::string InputSectionBase::getSrcMsg<ELF64BE>(const SymbolBody &,
1024+
uint64_t);
10111025

10121026
template void InputSection::writeTo<ELF32LE>(uint8_t *);
10131027
template void InputSection::writeTo<ELF32BE>(uint8_t *);

lld/ELF/InputSection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class InputSectionBase : public SectionBase {
177177

178178
// Returns a source location string. Used to construct an error message.
179179
template <class ELFT> std::string getLocation(uint64_t Offset);
180-
template <class ELFT> std::string getSrcMsg(uint64_t Offset);
180+
template <class ELFT> std::string getSrcMsg(const SymbolBody &Sym, uint64_t Offset);
181181
std::string getObjMsg(uint64_t Offset);
182182

183183
// Each section knows how to relocate itself. These functions apply

lld/ELF/Relocations.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static std::string getLocation(InputSectionBase &S, const SymbolBody &Sym,
7474
uint64_t Off) {
7575
std::string Msg =
7676
"\n>>> defined in " + toString(Sym.getFile()) + "\n>>> referenced by ";
77-
std::string Src = S.getSrcMsg<ELFT>(Off);
77+
std::string Src = S.getSrcMsg<ELFT>(Sym, Off);
7878
if (!Src.empty())
7979
Msg += Src + "\n>>> ";
8080
return Msg + S.getObjMsg(Off);
@@ -728,7 +728,7 @@ static bool maybeReportUndefined(SymbolBody &Sym, InputSectionBase &Sec,
728728
std::string Msg =
729729
"undefined symbol: " + toString(Sym) + "\n>>> referenced by ";
730730

731-
std::string Src = Sec.getSrcMsg<ELFT>(Offset);
731+
std::string Src = Sec.getSrcMsg<ELFT>(Sym, Offset);
732732
if (!Src.empty())
733733
Msg += Src + "\n>>> ";
734734
Msg += Sec.getObjMsg(Offset);

lld/ELF/SymbolTable.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,9 @@ static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
456456
// >>> defined at baz.c:563
457457
// >>> baz.o in archive libbaz.a
458458
auto *Sec1 = cast<InputSectionBase>(D->Section);
459-
std::string Src1 = Sec1->getSrcMsg<ELFT>(D->Value);
459+
std::string Src1 = Sec1->getSrcMsg<ELFT>(*Sym, D->Value);
460460
std::string Obj1 = Sec1->getObjMsg(D->Value);
461-
std::string Src2 = ErrSec->getSrcMsg<ELFT>(ErrOffset);
461+
std::string Src2 = ErrSec->getSrcMsg<ELFT>(*Sym, ErrOffset);
462462
std::string Obj2 = ErrSec->getObjMsg(ErrOffset);
463463

464464
std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
2+
# RUN: llvm-dwarfdump %t.o | FileCheck -check-prefix=INPUT %s
3+
# RUN: not ld.lld %t.o %t.o -o %t 2>&1 | FileCheck %s
4+
5+
# INPUT: .debug_info contents:
6+
# INPUT: DW_TAG_variable
7+
# INPUT-NEXT: DW_AT_name ("foo")
8+
# INPUT-NEXT: DW_AT_decl_file ("1.c")
9+
# INPUT-NEXT: DW_AT_decl_line (1)
10+
# INPUT-NEXT: DW_AT_type (cu + 0x0032 "int")
11+
# INPUT-NEXT: DW_AT_external (true)
12+
# INPUT-NEXT: DW_AT_location (DW_OP_addr 0x0)
13+
# INPUT: DW_TAG_variable
14+
# INPUT-NEXT: DW_AT_name ("bar")
15+
# INPUT-NEXT: DW_AT_decl_file ("1.c")
16+
# INPUT-NEXT: DW_AT_decl_line (2)
17+
# INPUT-NEXT: DW_AT_type (cu + 0x0032 "int")
18+
# INPUT-NEXT: DW_AT_external (true)
19+
# INPUT-NEXT: DW_AT_location (DW_OP_addr 0x0)
20+
21+
## Check we use information from .debug_info in messages.
22+
# CHECK: duplicate symbol: bar
23+
# CHECK-NEXT: >>> defined at 1.c:2
24+
# CHECK-NEXT: >>> {{.*}}:(bar)
25+
# CHECK-NEXT: >>> defined at 1.c:2
26+
# CHECK-NEXT: >>> {{.*}}:(.data+0x0)
27+
# CHECK: duplicate symbol: foo
28+
# CHECK-NEXT: >>> defined at 1.c:1
29+
# CHECK-NEXT: >>> {{.*}}:(foo)
30+
# CHECK-NEXT: >>> defined at 1.c:1
31+
# CHECK-NEXT: >>> {{.*}}:(.bss+0x0)
32+
33+
# Used reduced output from following code and gcc 7.1.0
34+
# to produce this input file:
35+
# Source (1.c):
36+
# int foo = 0;
37+
# int bar = 1;
38+
# Invocation: g++ -g -S 1.c
39+
40+
.bss
41+
.globl foo
42+
.type foo, @object
43+
.size foo, 4
44+
foo:
45+
46+
.data
47+
.globl bar
48+
.type bar, @object
49+
.size bar, 4
50+
bar:
51+
52+
.text
53+
.file 1 "1.c"
54+
55+
.section .debug_info,"",@progbits
56+
.long 0x4b # Compile Unit: length = 0x0000004b)
57+
.value 0x4 # version = 0x0004
58+
.long 0 # abbr_offset = 0x0
59+
.byte 0x8 # addr_size = 0x08
60+
61+
.uleb128 0x1 # DW_TAG_compile_unit [1] *
62+
.long 0 # DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000000] = )
63+
.byte 0x4 # DW_AT_language [DW_FORM_data1] (DW_LANG_C_plus_plus)
64+
.string "1.c" # DW_AT_name [DW_FORM_string] ("1.c")
65+
.long 0 # DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x00000000] = )
66+
.long 0 # DW_AT_stmt_list [DW_FORM_sec_offset] (0x00000000)
67+
68+
.uleb128 0x2 # DW_TAG_variable [2]
69+
.string "foo" # DW_AT_name [DW_FORM_string] ("foo")
70+
.byte 0x1 # DW_AT_decl_file [DW_FORM_data1] ("1.c")
71+
.byte 0x1 # DW_AT_decl_line [DW_FORM_data1] (1)
72+
.long 0x32 # DW_AT_type [DW_FORM_ref4] (cu + 0x0032 => {0x00000032})
73+
.uleb128 0x9 # DW_AT_external [DW_FORM_flag_present] (true)
74+
.byte 0x3
75+
.quad foo # DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x0)
76+
77+
.uleb128 0x3 # DW_TAG_base_type [3]
78+
.byte 0x4 # DW_AT_byte_size [DW_FORM_data1] (0x04)
79+
.byte 0x5 # DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed)
80+
.string "int" # DW_AT_name [DW_FORM_string] ("int")
81+
82+
.uleb128 0x2 # DW_TAG_variable [2]
83+
.string "bar" # DW_AT_name [DW_FORM_string] ("bar")
84+
.byte 0x1 # DW_AT_decl_file [DW_FORM_data1] ("1.c")
85+
.byte 0x2 # DW_AT_decl_line [DW_FORM_data1] (2)
86+
.long 0x32 # DW_AT_type [DW_FORM_ref4] (cu + 0x0032 => {0x00000032})
87+
.uleb128 0x9 # DW_AT_external [DW_FORM_flag_present] (true)
88+
.byte 0x3
89+
.quad bar # DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x0)
90+
.byte 0 # END
91+
92+
93+
.section .debug_abbrev,"",@progbits
94+
.uleb128 0x1 # Abbreviation code.
95+
.uleb128 0x11 # DW_TAG_compile_unit
96+
97+
.byte 0x1 # ID
98+
.uleb128 0x25 # DW_AT_producer, DW_FORM_strp
99+
.uleb128 0xe
100+
.uleb128 0x13 # DW_AT_language, DW_FORM_data1
101+
.uleb128 0xb
102+
.uleb128 0x3 # DW_AT_name, DW_FORM_string
103+
.uleb128 0x8
104+
.uleb128 0x1b # DW_AT_comp_dir, DW_FORM_strp
105+
.uleb128 0xe
106+
.uleb128 0x10 # DW_AT_stmt_list, DW_FORM_sec_offset
107+
.uleb128 0x17
108+
.byte 0
109+
.byte 0
110+
111+
.uleb128 0x2 # ID
112+
.uleb128 0x34 # DW_TAG_variable, DW_CHILDREN_no
113+
.byte 0
114+
.uleb128 0x3 # DW_AT_name, DW_FORM_string
115+
.uleb128 0x8
116+
.uleb128 0x3a # DW_AT_decl_file, DW_FORM_data1
117+
.uleb128 0xb
118+
.uleb128 0x3b # DW_AT_decl_line, DW_FORM_data1
119+
.uleb128 0xb
120+
.uleb128 0x49 # DW_AT_type, DW_FORM_ref4
121+
.uleb128 0x13
122+
.uleb128 0x3f # DW_AT_external, DW_FORM_flag_present
123+
.uleb128 0x19
124+
.uleb128 0x2 # DW_AT_location, DW_FORM_exprloc
125+
.uleb128 0x18
126+
.byte 0
127+
.byte 0
128+
129+
.uleb128 0x3 # ID
130+
.uleb128 0x24 # DW_TAG_base_type, DW_CHILDREN_no
131+
.byte 0
132+
.uleb128 0xb # DW_AT_byte_size, DW_FORM_data1
133+
.uleb128 0xb
134+
.uleb128 0x3e # DW_AT_encoding, DW_FORM_data1
135+
.uleb128 0xb
136+
.uleb128 0x3 # DW_AT_name, DW_FORM_string
137+
.uleb128 0x8
138+
.byte 0
139+
.byte 0
140+
.byte 0
141+

0 commit comments

Comments
 (0)