[lld] r330235 - COFF: Friendlier undefined symbol errors.
Peter Collingbourne via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 17 16:32:33 PDT 2018
Author: pcc
Date: Tue Apr 17 16:32:33 2018
New Revision: 330235
URL: http://llvm.org/viewvc/llvm-project?rev=330235&view=rev
Log:
COFF: Friendlier undefined symbol errors.
Summary:
This change does three things:
- Try to find the file and line number of an undefined symbol
reference by reading codeview debug info.
- Try to find the name of the function or global variable with the
undefined symbol reference by searching the object file's symbol
table.
- Prints the information in the same style as the ELF linker.
Differential Revision: https://reviews.llvm.org/D45467
Added:
lld/trunk/test/COFF/undefined-symbol-cv.s
lld/trunk/test/COFF/undefined-symbol.s
Modified:
lld/trunk/COFF/Chunks.h
lld/trunk/COFF/InputFiles.cpp
lld/trunk/COFF/PDB.cpp
lld/trunk/COFF/PDB.h
lld/trunk/COFF/SymbolTable.cpp
lld/trunk/test/COFF/filename-casing.s
lld/trunk/test/COFF/force.test
lld/trunk/test/COFF/nodefaultlib.test
Modified: lld/trunk/COFF/Chunks.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Chunks.h?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/COFF/Chunks.h (original)
+++ lld/trunk/COFF/Chunks.h Tue Apr 17 16:32:33 2018
@@ -213,10 +213,11 @@ public:
// The COMDAT leader symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr;
+ ArrayRef<coff_relocation> Relocs;
+
private:
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
- ArrayRef<coff_relocation> Relocs;
// Used by the garbage collector.
bool Live;
Modified: lld/trunk/COFF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.cpp?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.cpp (original)
+++ lld/trunk/COFF/InputFiles.cpp Tue Apr 17 16:32:33 2018
@@ -170,8 +170,8 @@ SectionChunk *ObjFile::readSection(uint3
// CodeView needs a linker support. We need to interpret and debug
// info, and then write it to a separate .pdb file.
- // Ignore debug info unless /debug is given.
- if (!Config->Debug && Name.startswith(".debug"))
+ // Ignore DWARF debug info unless /debug is given.
+ if (!Config->Debug && Name.startswith(".debug_"))
return nullptr;
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
Modified: lld/trunk/COFF/PDB.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/PDB.cpp?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/COFF/PDB.cpp (original)
+++ lld/trunk/COFF/PDB.cpp Tue Apr 17 16:32:33 2018
@@ -1170,3 +1170,145 @@ void PDBLinker::commit() {
// Write to a file.
ExitOnErr(Builder.commit(Config->PDBPath));
}
+
+static Expected<StringRef>
+getFileName(const DebugStringTableSubsectionRef &Strings,
+ const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) {
+ auto Iter = Checksums.getArray().at(FileID);
+ if (Iter == Checksums.getArray().end())
+ return make_error<CodeViewError>(cv_error_code::no_records);
+ uint32_t Offset = Iter->FileNameOffset;
+ return Strings.getString(Offset);
+}
+
+static uint32_t getSecrelReloc() {
+ switch (Config->Machine) {
+ case AMD64:
+ return COFF::IMAGE_REL_AMD64_SECREL;
+ case I386:
+ return COFF::IMAGE_REL_I386_SECREL;
+ case ARMNT:
+ return COFF::IMAGE_REL_ARM_SECREL;
+ case ARM64:
+ return COFF::IMAGE_REL_ARM64_SECREL;
+ default:
+ llvm_unreachable("unknown machine type");
+ }
+}
+
+// Try to find a line table for the given offset Addr into the given chunk C.
+// If a line table was found, the line table, the string and checksum tables
+// that are used to interpret the line table, and the offset of Addr in the line
+// table are stored in the output arguments. Returns whether a line table was
+// found.
+static bool findLineTable(const SectionChunk *C, uint32_t Addr,
+ DebugStringTableSubsectionRef &CVStrTab,
+ DebugChecksumsSubsectionRef &Checksums,
+ DebugLinesSubsectionRef &Lines,
+ uint32_t &OffsetInLinetable) {
+ ExitOnError ExitOnErr;
+ uint32_t SecrelReloc = getSecrelReloc();
+
+ for (SectionChunk *DbgC : C->File->getDebugChunks()) {
+ if (DbgC->getSectionName() != ".debug$S")
+ continue;
+
+ // Build a mapping of SECREL relocations in DbgC that refer to C.
+ DenseMap<uint32_t, uint32_t> Secrels;
+ for (const coff_relocation &R : DbgC->Relocs) {
+ if (R.Type != SecrelReloc)
+ continue;
+
+ if (auto *S = dyn_cast_or_null<DefinedRegular>(
+ C->File->getSymbols()[R.SymbolTableIndex]))
+ if (S->getChunk() == C)
+ Secrels[R.VirtualAddress] = S->getValue();
+ }
+
+ ArrayRef<uint8_t> Contents =
+ consumeDebugMagic(DbgC->getContents(), ".debug$S");
+ DebugSubsectionArray Subsections;
+ BinaryStreamReader Reader(Contents, support::little);
+ ExitOnErr(Reader.readArray(Subsections, Contents.size()));
+
+ for (const DebugSubsectionRecord &SS : Subsections) {
+ switch (SS.kind()) {
+ case DebugSubsectionKind::StringTable: {
+ assert(!CVStrTab.valid() &&
+ "Encountered multiple string table subsections!");
+ ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
+ break;
+ }
+ case DebugSubsectionKind::FileChecksums:
+ assert(!Checksums.valid() &&
+ "Encountered multiple checksum subsections!");
+ ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ break;
+ case DebugSubsectionKind::Lines: {
+ ArrayRef<uint8_t> Bytes;
+ auto Ref = SS.getRecordData();
+ ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes));
+ size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data();
+
+ // Check whether this line table refers to C.
+ auto I = Secrels.find(OffsetInDbgC);
+ if (I == Secrels.end())
+ break;
+
+ // Check whether this line table covers Addr in C.
+ DebugLinesSubsectionRef LinesTmp;
+ ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref)));
+ uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset;
+ if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize)
+ break;
+
+ assert(!Lines.header() &&
+ "Encountered multiple line tables for function!");
+ ExitOnErr(Lines.initialize(BinaryStreamReader(Ref)));
+ OffsetInLinetable = Addr - OffsetInC;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (CVStrTab.valid() && Checksums.valid() && Lines.header())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Use CodeView line tables to resolve a file and line number for the given
+// offset into the given chunk and return them, or {"", 0} if a line table was
+// not found.
+std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C,
+ uint32_t Addr) {
+ ExitOnError ExitOnErr;
+
+ DebugStringTableSubsectionRef CVStrTab;
+ DebugChecksumsSubsectionRef Checksums;
+ DebugLinesSubsectionRef Lines;
+ uint32_t OffsetInLinetable;
+
+ if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable))
+ return {"", 0};
+
+ uint32_t NameIndex;
+ uint32_t LineNumber;
+ for (LineColumnEntry &Entry : Lines) {
+ for (const LineNumberEntry &LN : Entry.LineNumbers) {
+ if (LN.Offset > OffsetInLinetable) {
+ StringRef Filename =
+ ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex));
+ return {Filename, LineNumber};
+ }
+ LineInfo LI(LN.Flags);
+ NameIndex = Entry.NameIndex;
+ LineNumber = LI.getStartLine();
+ }
+ }
+ StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex));
+ return {Filename, LineNumber};
+}
Modified: lld/trunk/COFF/PDB.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/PDB.h?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/COFF/PDB.h (original)
+++ lld/trunk/COFF/PDB.h Tue Apr 17 16:32:33 2018
@@ -22,12 +22,16 @@ union DebugInfo;
namespace lld {
namespace coff {
class OutputSection;
+class SectionChunk;
class SymbolTable;
void createPDB(SymbolTable *Symtab,
llvm::ArrayRef<OutputSection *> OutputSections,
llvm::ArrayRef<uint8_t> SectionTable,
const llvm::codeview::DebugInfo &BuildId);
+
+std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C,
+ uint32_t Addr);
}
}
Modified: lld/trunk/COFF/SymbolTable.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/SymbolTable.cpp?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/COFF/SymbolTable.cpp (original)
+++ lld/trunk/COFF/SymbolTable.cpp Tue Apr 17 16:32:33 2018
@@ -11,6 +11,7 @@
#include "Config.h"
#include "Driver.h"
#include "LTO.h"
+#include "PDB.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
@@ -64,6 +65,66 @@ static void errorOrWarn(const Twine &S)
error(S);
}
+// Returns the name of the symbol in SC whose value is <= Addr that is closest
+// to Addr. This is generally the name of the global variable or function whose
+// definition contains Addr.
+static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) {
+ DefinedRegular *Candidate = nullptr;
+
+ for (Symbol *S : SC->File->getSymbols()) {
+ auto *D = dyn_cast_or_null<DefinedRegular>(S);
+ if (!D || D->getChunk() != SC || D->getValue() > Addr ||
+ (Candidate && D->getValue() < Candidate->getValue()))
+ continue;
+
+ Candidate = D;
+ }
+
+ if (!Candidate)
+ return "";
+ return Candidate->getName();
+}
+
+static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
+ struct Location {
+ StringRef SymName;
+ std::pair<StringRef, uint32_t> FileLine;
+ };
+ std::vector<Location> Locations;
+
+ for (Chunk *C : File->getChunks()) {
+ auto *SC = dyn_cast<SectionChunk>(C);
+ if (!SC)
+ continue;
+ for (const coff_relocation &R : SC->Relocs) {
+ if (R.SymbolTableIndex != SymIndex)
+ continue;
+ std::pair<StringRef, uint32_t> FileLine =
+ getFileLine(SC, R.VirtualAddress);
+ StringRef SymName = getSymbolName(SC, R.VirtualAddress);
+ if (!FileLine.first.empty() || !SymName.empty())
+ Locations.push_back({SymName, FileLine});
+ }
+ }
+
+ if (Locations.empty())
+ return "\n>>> referenced by " + toString(File) + "\n";
+
+ std::string Out;
+ llvm::raw_string_ostream OS(Out);
+ for (Location Loc : Locations) {
+ OS << "\n>>> referenced by ";
+ if (!Loc.FileLine.first.empty())
+ OS << Loc.FileLine.first << ":" << Loc.FileLine.second
+ << "\n>>> ";
+ OS << toString(File);
+ if (!Loc.SymName.empty())
+ OS << ":(" << Loc.SymName << ')';
+ }
+ OS << '\n';
+ return OS.str();
+}
+
void SymbolTable::reportRemainingUndefines() {
SmallPtrSet<Symbol *, 8> Undefs;
DenseMap<Symbol *, Symbol *> LocalImports;
@@ -127,11 +188,14 @@ void SymbolTable::reportRemainingUndefin
}
for (ObjFile *File : ObjFile::Instances) {
+ size_t SymIndex = -1ull;
for (Symbol *Sym : File->getSymbols()) {
+ ++SymIndex;
if (!Sym)
continue;
if (Undefs.count(Sym))
- errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName());
+ errorOrWarn("undefined symbol: " + Sym->getName() +
+ getSymbolLocations(File, SymIndex));
if (Config->WarnLocallyDefinedImported)
if (Symbol *Imp = LocalImports.lookup(Sym))
warn(toString(File) + ": locally defined symbol imported: " +
Modified: lld/trunk/test/COFF/filename-casing.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/filename-casing.s?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/test/COFF/filename-casing.s (original)
+++ lld/trunk/test/COFF/filename-casing.s Tue Apr 17 16:32:33 2018
@@ -6,8 +6,10 @@
# RUN: llvm-lib /out:%T/MixedCase.lib %T/MixedCase.obj
# RUN: not lld-link /machine:x64 /entry:main %T/MixedCase.lib 2>&1 | FileCheck -check-prefix=ARCHIVE %s
-# OBJECT: MixedCase.obj: undefined symbol: f
-# ARCHIVE: MixedCase.lib(MixedCase.obj): undefined symbol: f
+# OBJECT: undefined symbol: f
+# OBJECT-NEXT: >>> referenced by {{.*}}MixedCase.obj:(main)
+# ARCHIVE: undefined symbol: f
+# ARCHIVE-NEXT: >>> referenced by {{.*}}MixedCase.lib(MixedCase.obj):(main)
.globl main
main:
Modified: lld/trunk/test/COFF/force.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/force.test?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/test/COFF/force.test (original)
+++ lld/trunk/test/COFF/force.test Tue Apr 17 16:32:33 2018
@@ -4,8 +4,10 @@
# RUN: lld-link /out:%t.exe /entry:main %t.obj /force >& %t.log
# RUN: FileCheck -check-prefix=WARN %s < %t.log
-# ERROR: error: {{.*}}.obj: undefined symbol: foo
-# WARN: warning: {{.*}}.obj: undefined symbol: foo
+# ERROR: error: undefined symbol: foo
+# ERROR-NEXT: >>> referenced by {{.*}}.obj
+# WARN: warning: undefined symbol: foo
+# WARN-NEXT: >>> referenced by {{.*}}.obj
--- !COFF
header:
Modified: lld/trunk/test/COFF/nodefaultlib.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/nodefaultlib.test?rev=330235&r1=330234&r2=330235&view=diff
==============================================================================
--- lld/trunk/test/COFF/nodefaultlib.test (original)
+++ lld/trunk/test/COFF/nodefaultlib.test Tue Apr 17 16:32:33 2018
@@ -21,7 +21,8 @@
CHECK1: error: could not open hello64.obj: {{[Nn]}}o such file or directory
CHECK2: error: could not open hello64: {{[Nn]}}o such file or directory
-CHECK3: error: {{.*}}hello64.obj: undefined symbol: MessageBoxA
+CHECK3: error: undefined symbol: MessageBoxA
+CHECK3-NEXT: >>> referenced by {{.*}}hello64.obj:(main)
# RUN: lld-link /libpath:%T /out:%t.exe /entry:main \
# RUN: /subsystem:console hello64.obj /defaultlib:std64.lib
Added: lld/trunk/test/COFF/undefined-symbol-cv.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/undefined-symbol-cv.s?rev=330235&view=auto
==============================================================================
--- lld/trunk/test/COFF/undefined-symbol-cv.s (added)
+++ lld/trunk/test/COFF/undefined-symbol-cv.s Tue Apr 17 16:32:33 2018
@@ -0,0 +1,61 @@
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: not lld-link /out:%t.exe %t.obj 2>&1 | FileCheck %s
+
+# CHECK: error: undefined symbol: ?foo@@YAHXZ
+# CHECK-NEXT: >>> referenced by file1.cpp:1
+# CHECK-NEXT: >>> {{.*}}.obj:(main)
+# CHECK-NEXT: >>> referenced by file1.cpp:2
+# CHECK-NEXT: >>> {{.*}}.obj:(main)
+
+# CHECK: error: undefined symbol: ?bar@@YAHXZ
+# CHECK-NEXT: >>> referenced by file2.cpp:3
+# CHECK-NEXT: >>> {{.*}}.obj:(main)
+# CHECK-NEXT: >>> referenced by file1.cpp:4
+# CHECK-NEXT: >>> {{.*}}.obj:(f1)
+
+# CHECK: error: undefined symbol: ?baz@@YAHXZ
+# CHECK-NEXT: >>> referenced by file1.cpp:5
+# CHECK-NEXT: >>> {{.*}}.obj:(f2)
+
+ .cv_file 1 "file1.cpp" "EDA15C78BB573E49E685D8549286F33C" 1
+ .cv_file 2 "file2.cpp" "EDA15C78BB573E49E685D8549286F33D" 1
+
+ .section .text,"xr",one_only,main
+.globl main
+main:
+ .cv_func_id 0
+ .cv_loc 0 1 1 0 is_stmt 0
+ call "?foo@@YAHXZ"
+ .cv_loc 0 1 2 0
+ call "?foo@@YAHXZ"
+ .cv_loc 0 2 3 0
+ call "?bar@@YAHXZ"
+.Lfunc_end0:
+
+f1:
+ .cv_func_id 1
+ .cv_loc 1 1 4 0 is_stmt 0
+ call "?bar@@YAHXZ"
+.Lfunc_end1:
+
+ .section .text,"xr",one_only,f2
+.globl f2
+f2:
+ .cv_func_id 2
+ .cv_loc 2 1 5 0 is_stmt 0
+ call "?baz@@YAHXZ"
+.Lfunc_end2:
+
+ .section .debug$S,"dr",associative,main
+ .long 4
+ .cv_linetable 0, main, .Lfunc_end0
+ .cv_linetable 1, f1, .Lfunc_end1
+
+ .section .debug$S,"dr",associative,f2
+ .long 4
+ .cv_linetable 2, f2, .Lfunc_end2
+
+ .section .debug$S,"dr"
+ .long 4
+ .cv_filechecksums
+ .cv_stringtable
Added: lld/trunk/test/COFF/undefined-symbol.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/undefined-symbol.s?rev=330235&view=auto
==============================================================================
--- lld/trunk/test/COFF/undefined-symbol.s (added)
+++ lld/trunk/test/COFF/undefined-symbol.s Tue Apr 17 16:32:33 2018
@@ -0,0 +1,29 @@
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: not lld-link /out:%t.exe %t.obj 2>&1 | FileCheck %s
+
+# CHECK: error: undefined symbol: ?foo@@YAHXZ
+# CHECK-NEXT: >>> referenced by {{.*}}.obj:(main)
+# CHECK-NEXT: >>> referenced by {{.*}}.obj:(main)
+
+# CHECK: error: undefined symbol: ?bar@@YAHXZ
+# CHECK-NEXT: >>> referenced by {{.*}}.obj:(main)
+# CHECK-NEXT: >>> referenced by {{.*}}.obj:(f1)
+
+# CHECK: error: undefined symbol: ?baz@@YAHXZ
+# CHECK-NEXT: >>> referenced by {{.*}}.obj:(f2)
+
+ .section .text,"xr",one_only,main
+.globl main
+main:
+ call "?foo@@YAHXZ"
+ call "?foo@@YAHXZ"
+ call "?bar@@YAHXZ"
+
+f1:
+ call "?bar@@YAHXZ"
+.Lfunc_end1:
+
+ .section .text,"xr",one_only,f2
+.globl f2
+f2:
+ call "?baz@@YAHXZ"
More information about the llvm-commits
mailing list