Skip to content

Commit 5792797

Browse files
BertalanDnico
authored andcommitted
Reland "[lld-macho] Show source information for undefined references"
The error used to look like this: ld64.lld: error: undefined symbol: _foo >>> referenced by /path/to/bar.o:(symbol _baz+0x4) If DWARF line information is available, we now show where in the source the references are coming from: ld64.lld: error: unreferenced symbol: _foo >>> referenced by: bar.cpp:42 (/path/to/bar.cpp:42) >>> /path/to/bar.o:(symbol _baz+0x4) The reland is identical to the first time this landed. The fix was in D128294. This reverts commit 0cc7ad4. Differential Revision: https://reviews.llvm.org/D128184
1 parent fde04ae commit 5792797

File tree

11 files changed

+334
-25
lines changed

11 files changed

+334
-25
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ template <class ELFT>
224224
static std::string getSrcMsgAux(ObjFile<ELFT> &file, const Symbol &sym,
225225
InputSectionBase &sec, uint64_t offset) {
226226
// In DWARF, functions and variables are stored to different places.
227-
// First, lookup a function for a given offset.
227+
// First, look up a function for a given offset.
228228
if (Optional<DILineInfo> info = file.getDILineInfo(&sec, offset))
229229
return createFileLineMsg(info->FileName, info->Line);
230230

231-
// If it failed, lookup again as a variable.
231+
// If it failed, look up again as a variable.
232232
if (Optional<std::pair<std::string, unsigned>> fileLine =
233233
file.getVariableLoc(sym.getName()))
234234
return createFileLineMsg(fileLine->first, fileLine->second);

lld/MachO/Dwarf.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ using namespace llvm;
2020
std::unique_ptr<DwarfObject> DwarfObject::create(ObjFile *obj) {
2121
auto dObj = std::make_unique<DwarfObject>();
2222
bool hasDwarfInfo = false;
23-
// LLD only needs to extract the source file path from the debug info, so we
24-
// initialize DwarfObject with just the sections necessary to get that path.
25-
// The debugger will locate the debug info via the object file paths that we
26-
// emit in our STABS symbols, so we don't need to process & emit them
27-
// ourselves.
23+
// LLD only needs to extract the source file path and line numbers from the
24+
// debug info, so we initialize DwarfObject with just the sections necessary
25+
// to get that path. The debugger will locate the debug info via the object
26+
// file paths that we emit in our STABS symbols, so we don't need to process &
27+
// emit them ourselves.
2828
for (const InputSection *isec : obj->debugSections) {
2929
if (StringRef *s =
3030
StringSwitch<StringRef *>(isec->getName())
3131
.Case(section_names::debugInfo, &dObj->infoSection.Data)
32+
.Case(section_names::debugLine, &dObj->lineSection.Data)
3233
.Case(section_names::debugAbbrev, &dObj->abbrevSection)
3334
.Case(section_names::debugStr, &dObj->strSection)
3435
.Default(nullptr)) {

lld/MachO/Dwarf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,17 @@ class DwarfObject final : public llvm::DWARFObject {
3737
llvm::StringRef getAbbrevSection() const override { return abbrevSection; }
3838
llvm::StringRef getStrSection() const override { return strSection; }
3939

40+
llvm::DWARFSection const &getLineSection() const override {
41+
return lineSection;
42+
}
43+
4044
// Returns an instance of DwarfObject if the given object file has the
4145
// relevant DWARF debug sections.
4246
static std::unique_ptr<DwarfObject> create(ObjFile *);
4347

4448
private:
4549
llvm::DWARFSection infoSection;
50+
llvm::DWARFSection lineSection;
4651
llvm::StringRef abbrevSection;
4752
llvm::StringRef strSection;
4853
};

lld/MachO/InputFiles.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,8 @@ void ObjFile::parseDebugInfo() {
998998
if (!dObj)
999999
return;
10001000

1001+
// We do not re-use the context from getDwarf() here as that function
1002+
// constructs an expensive DWARFCache object.
10011003
auto *ctx = make<DWARFContext>(
10021004
std::move(dObj), "",
10031005
[&](Error err) {
@@ -1373,6 +1375,31 @@ void ObjFile::registerEhFrames(Section &ehFrameSection) {
13731375
}
13741376
}
13751377

1378+
std::string ObjFile::sourceFile() const {
1379+
SmallString<261> dir(compileUnit->getCompilationDir());
1380+
StringRef sep = sys::path::get_separator();
1381+
// We don't use `path::append` here because we want an empty `dir` to result
1382+
// in an absolute path. `append` would give us a relative path for that case.
1383+
if (!dir.endswith(sep))
1384+
dir += sep;
1385+
return (dir + compileUnit->getUnitDIE().getShortName()).str();
1386+
}
1387+
1388+
lld::DWARFCache *ObjFile::getDwarf() {
1389+
llvm::call_once(initDwarf, [this]() {
1390+
auto dwObj = DwarfObject::create(this);
1391+
if (!dwObj)
1392+
return;
1393+
dwarfCache = std::make_unique<DWARFCache>(std::make_unique<DWARFContext>(
1394+
std::move(dwObj), "",
1395+
[&](Error err) { warn(getName() + ": " + toString(std::move(err))); },
1396+
[&](Error warning) {
1397+
warn(getName() + ": " + toString(std::move(warning)));
1398+
}));
1399+
});
1400+
1401+
return dwarfCache.get();
1402+
}
13761403
// The path can point to either a dylib or a .tbd file.
13771404
static DylibFile *loadDylib(StringRef path, DylibFile *umbrella) {
13781405
Optional<MemoryBufferRef> mbref = readFile(path);

lld/MachO/InputFiles.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "MachOStructs.h"
1313
#include "Target.h"
1414

15+
#include "lld/Common/DWARF.h"
1516
#include "lld/Common/LLVM.h"
1617
#include "lld/Common/Memory.h"
1718
#include "llvm/ADT/CachedHashString.h"
@@ -21,6 +22,7 @@
2122
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
2223
#include "llvm/Object/Archive.h"
2324
#include "llvm/Support/MemoryBuffer.h"
25+
#include "llvm/Support/Threading.h"
2426
#include "llvm/TextAPI/TextAPIReader.h"
2527

2628
#include <vector>
@@ -159,14 +161,21 @@ class ObjFile final : public InputFile {
159161

160162
static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
161163

164+
std::string sourceFile() const;
165+
// Parses line table information for diagnostics. compileUnit should be used
166+
// for other purposes.
167+
lld::DWARFCache *getDwarf();
168+
162169
llvm::DWARFUnit *compileUnit = nullptr;
170+
std::unique_ptr<lld::DWARFCache> dwarfCache;
163171
Section *addrSigSection = nullptr;
164172
const uint32_t modTime;
165173
std::vector<ConcatInputSection *> debugSections;
166174
std::vector<CallGraphEntry> callGraph;
167175
llvm::DenseMap<ConcatInputSection *, FDE> fdes;
168176

169177
private:
178+
llvm::once_flag initDwarf;
170179
template <class LP> void parseLazy();
171180
template <class SectionHeader> void parseSections(ArrayRef<SectionHeader>);
172181
template <class LP>

lld/MachO/InputSection.cpp

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,21 @@ static uint64_t resolveSymbolVA(const Symbol *sym, uint8_t type) {
5555
return sym->getVA();
5656
}
5757

58+
const Defined *InputSection::getContainingSymbol(uint64_t off) const {
59+
auto *nextSym = llvm::upper_bound(
60+
symbols, off, [](uint64_t a, const Defined *b) { return a < b->value; });
61+
if (nextSym == symbols.begin())
62+
return nullptr;
63+
return *std::prev(nextSym);
64+
}
65+
5866
std::string InputSection::getLocation(uint64_t off) const {
5967
// First, try to find a symbol that's near the offset. Use it as a reference
6068
// point.
61-
auto *nextSym = llvm::upper_bound(
62-
symbols, off, [](uint64_t a, const Defined *b) { return a < b->value; });
63-
if (nextSym != symbols.begin()) {
64-
auto &sym = *std::prev(nextSym);
69+
if (auto *sym = getContainingSymbol(off))
6570
return (toString(getFile()) + ":(symbol " + sym->getName() + "+0x" +
6671
Twine::utohexstr(off - sym->value) + ")")
6772
.str();
68-
}
6973

7074
// If that fails, use the section itself as a reference point.
7175
for (const Subsection &subsec : section.subsections) {
@@ -74,11 +78,61 @@ std::string InputSection::getLocation(uint64_t off) const {
7478
break;
7579
}
7680
}
81+
7782
return (toString(getFile()) + ":(" + getName() + "+0x" +
7883
Twine::utohexstr(off) + ")")
7984
.str();
8085
}
8186

87+
std::string InputSection::getSourceLocation(uint64_t off) const {
88+
auto *obj = dyn_cast<ObjFile>(getFile());
89+
if (!obj)
90+
return {};
91+
92+
DWARFCache *dwarf = obj->getDwarf();
93+
if (!dwarf)
94+
return std::string();
95+
96+
for (const Subsection &subsec : section.subsections) {
97+
if (subsec.isec == this) {
98+
off += subsec.offset;
99+
break;
100+
}
101+
}
102+
103+
auto createMsg = [&](StringRef path, unsigned line) {
104+
std::string filename = sys::path::filename(path).str();
105+
std::string lineStr = (":" + Twine(line)).str();
106+
if (filename == path)
107+
return filename + lineStr;
108+
return (filename + lineStr + " (" + path + lineStr + ")").str();
109+
};
110+
111+
// First, look up a function for a given offset.
112+
if (Optional<DILineInfo> li = dwarf->getDILineInfo(
113+
section.addr + off, object::SectionedAddress::UndefSection))
114+
return createMsg(li->FileName, li->Line);
115+
116+
// If it failed, look up again as a variable.
117+
if (const Defined *sym = getContainingSymbol(off)) {
118+
// Symbols are generally prefixed with an underscore, which is not included
119+
// in the debug information.
120+
StringRef symName = sym->getName();
121+
if (!symName.empty() && symName[0] == '_')
122+
symName = symName.substr(1);
123+
124+
if (Optional<std::pair<std::string, unsigned>> fileLine =
125+
dwarf->getVariableLoc(symName))
126+
return createMsg(fileLine->first, fileLine->second);
127+
}
128+
129+
// Try to get the source file's name from the DWARF information.
130+
if (obj->compileUnit)
131+
return obj->sourceFile();
132+
133+
return {};
134+
}
135+
82136
void ConcatInputSection::foldIdentical(ConcatInputSection *copy) {
83137
align = std::max(align, copy->align);
84138
copy->live = false;

lld/MachO/InputSection.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ class InputSection {
5050
// The offset from the beginning of the file.
5151
uint64_t getVA(uint64_t off) const;
5252
// Return a user-friendly string for use in diagnostics.
53+
// Format: /path/to/object.o:(symbol _func+0x123)
5354
std::string getLocation(uint64_t off) const;
55+
// Return the source line corresponding to an address, or the empty string.
56+
// Format: Source.cpp:123 (/path/to/Source.cpp:123)
57+
std::string getSourceLocation(uint64_t off) const;
5458
// Whether the data at \p off in this InputSection is live.
5559
virtual bool isLive(uint64_t off) const = 0;
5660
virtual void markLive(uint64_t off) = 0;
@@ -85,6 +89,8 @@ class InputSection {
8589

8690
protected:
8791
const Section &section;
92+
93+
const Defined *getContainingSymbol(uint64_t off) const;
8894
};
8995

9096
// ConcatInputSections are combined into (Concat)OutputSections through simple
@@ -292,6 +298,7 @@ constexpr const char compactUnwind[] = "__compact_unwind";
292298
constexpr const char data[] = "__data";
293299
constexpr const char debugAbbrev[] = "__debug_abbrev";
294300
constexpr const char debugInfo[] = "__debug_info";
301+
constexpr const char debugLine[] = "__debug_line";
295302
constexpr const char debugStr[] = "__debug_str";
296303
constexpr const char ehFrame[] = "__eh_frame";
297304
constexpr const char gccExceptTab[] = "__gcc_except_tab";

lld/MachO/SymbolTable.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,11 @@ void macho::reportPendingUndefinedSymbols() {
381381
locations.codeReferences) {
382382
if (i >= maxUndefinedReferences)
383383
break;
384-
// TODO: Get source file/line from debug information.
385-
message += "\n>>> referenced by " + loc.isec->getLocation(loc.offset);
384+
message += "\n>>> referenced by ";
385+
std::string src = loc.isec->getSourceLocation(loc.offset);
386+
if (!src.empty())
387+
message += src + "\n>>> ";
388+
message += loc.isec->getLocation(loc.offset);
386389
++i;
387390
}
388391

lld/MachO/SyntheticSections.cpp

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -852,16 +852,9 @@ SymtabSection::SymtabSection(StringTableSection &stringTableSection)
852852
: LinkEditSection(segment_names::linkEdit, section_names::symbolTable),
853853
stringTableSection(stringTableSection) {}
854854

855-
void SymtabSection::emitBeginSourceStab(DWARFUnit *compileUnit) {
855+
void SymtabSection::emitBeginSourceStab(StringRef sourceFile) {
856856
StabsEntry stab(N_SO);
857-
SmallString<261> dir(compileUnit->getCompilationDir());
858-
StringRef sep = sys::path::get_separator();
859-
// We don't use `path::append` here because we want an empty `dir` to result
860-
// in an absolute path. `append` would give us a relative path for that case.
861-
if (!dir.endswith(sep))
862-
dir += sep;
863-
stab.strx = stringTableSection.addString(
864-
saver().save(dir + compileUnit->getUnitDIE().getShortName()));
857+
stab.strx = stringTableSection.addString(saver().save(sourceFile));
865858
stabs.emplace_back(std::move(stab));
866859
}
867860

@@ -956,7 +949,7 @@ void SymtabSection::emitStabs() {
956949
emitEndSourceStab();
957950
lastFile = file;
958951

959-
emitBeginSourceStab(file->compileUnit);
952+
emitBeginSourceStab(file->sourceFile());
960953
emitObjectFileStab(file);
961954
}
962955

lld/MachO/SyntheticSections.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ class SymtabSection : public LinkEditSection {
435435
uint32_t getNumUndefinedSymbols() const { return undefinedSymbols.size(); }
436436

437437
private:
438-
void emitBeginSourceStab(llvm::DWARFUnit *compileUnit);
438+
void emitBeginSourceStab(StringRef);
439439
void emitEndSourceStab();
440440
void emitObjectFileStab(ObjFile *);
441441
void emitEndFunStab(Defined *);

0 commit comments

Comments
 (0)