aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Storsjo <martin@martin.st>2019-10-18 10:43:15 +0000
committerMartin Storsjo <martin@martin.st>2019-10-18 10:43:15 +0000
commit133aa00fca6fdd1b168c8ef5e236784ec155c98c (patch)
tree866c924dbfdbc4bd5025e59bcd5f61b62c4dd919
parent46510801b1abf1f3e3451e79534cc6b62514f215 (diff)
[LLD] [COFF] Try to report source locations for duplicate symbols
This fixes the second part of PR42407. For files with dwarf debug info, it manually loads and iterates .debug_info to find the declared location of variables, to allow reporting them. (This matches the corresponding code in the ELF linker.) For functions, it uses the existing getFileLineDwarf which uses LLVMSymbolizer for translating addresses to file lines. In object files with codeview debug info, only the source location of duplicate functions is printed. (And even there, only for the first input file. The getFileLineCodeView function requires the object file to be fully loaded and initialized to properly resolve source locations, but duplicate symbols are reported at a stage when the second object file isn't fully loaded yet.) Differential Revision: https://reviews.llvm.org/D68975 git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@375218 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--COFF/InputFiles.cpp88
-rw-r--r--COFF/InputFiles.h14
-rw-r--r--COFF/SymbolTable.cpp72
-rw-r--r--COFF/SymbolTable.h6
-rw-r--r--test/COFF/conflict-mangled.test8
-rw-r--r--test/COFF/conflict.test16
-rw-r--r--test/COFF/duplicate-cv.s30
-rw-r--r--test/COFF/duplicate-dwarf.s213
-rw-r--r--test/COFF/duplicate.test8
9 files changed, 433 insertions, 22 deletions
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index d1ef20140..8770a870d 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -382,7 +382,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
StringRef name;
coffObj->getSymbolName(sym, name);
if (sc)
- return symtab->addRegular(this, name, sym.getGeneric(), sc);
+ return symtab->addRegular(this, name, sym.getGeneric(), sc,
+ sym.getValue());
// For MinGW symbols named .weak.* that point to a discarded section,
// don't create an Undefined symbol. If nothing ever refers to the symbol,
// everything should be fine. If something actually refers to the symbol
@@ -536,7 +537,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
// if the two comdat sections have e.g. different alignment.
// Match that.
if (leaderChunk->getContents() != newChunk.getContents())
- symtab->reportDuplicate(leader, this);
+ symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
break;
}
@@ -788,6 +789,89 @@ void ObjFile::initializeDependencies() {
debugTypesObj = makeTpiSource(this);
}
+// Used only for DWARF debug info, which is not common (except in MinGW
+// environments). This returns an optional pair of file name and line
+// number for where the variable was defined.
+Optional<std::pair<StringRef, uint32_t>>
+ObjFile::getVariableLocation(StringRef var) {
+ if (!dwarf) {
+ dwarf = DWARFContext::create(*getCOFFObj());
+ if (!dwarf)
+ return None;
+ initializeDwarf();
+ }
+ if (config->machine == I386)
+ var.consume_front("_");
+ auto it = variableLoc.find(var);
+ if (it == variableLoc.end())
+ return None;
+
+ // Take file name string from line table.
+ std::string fileName;
+ if (!it->second.lt->getFileNameByIndex(
+ it->second.file, {},
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
+ return None;
+
+ return std::make_pair(saver.save(fileName), it->second.line);
+}
+
+// Used only for DWARF debug info, which is not common (except in MinGW
+// environments). This initializes the dwarf, lineTables and variableLoc
+// members.
+void ObjFile::initializeDwarf() {
+ for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) {
+ auto report = [](Error err) {
+ handleAllErrors(std::move(err),
+ [](ErrorInfoBase &info) { warn(info.message()); });
+ };
+ Expected<const DWARFDebugLine::LineTable *> expectedLT =
+ dwarf->getLineTableForUnit(cu.get(), report);
+ const DWARFDebugLine::LineTable *lt = nullptr;
+ if (expectedLT)
+ lt = *expectedLT;
+ else
+ report(expectedLT.takeError());
+ if (!lt)
+ continue;
+ lineTables.push_back(lt);
+
+ // Loop over variable records and insert them to variableLoc.
+ for (const auto &entry : cu->dies()) {
+ DWARFDie die(cu.get(), &entry);
+ // Skip all tags that are not variables.
+ if (die.getTag() != dwarf::DW_TAG_variable)
+ continue;
+
+ // Skip if a local variable because we don't need them for generating
+ // error messages. In general, only non-local symbols can fail to be
+ // linked.
+ if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
+ continue;
+
+ // Get the source filename index for the variable.
+ unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
+ if (!lt->hasFileAtIndex(file))
+ continue;
+
+ // Get the line number on which the variable is declared.
+ unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
+
+ // Here we want to take the variable name to add it into variableLoc.
+ // Variable can have regular and linkage name associated. At first, we try
+ // to get linkage name as it can be different, for example when we have
+ // two variables in different namespaces of the same object. Use common
+ // name otherwise, but handle the case when it also absent in case if the
+ // input object file lacks some debug info.
+ StringRef name =
+ dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
+ dwarf::toString(die.find(dwarf::DW_AT_name), ""));
+ if (!name.empty())
+ variableLoc.insert({name, {lt, file, line}});
+ }
+ }
+}
+
StringRef ltrim1(StringRef s, const char *chars) {
if (!s.empty() && strchr(chars, s[0]))
return s.substr(1);
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 108a4caac..d3f4cd7bb 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -16,6 +16,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
@@ -202,6 +203,9 @@ public:
// The .debug$T stream if there's one.
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
+ llvm::Optional<std::pair<StringRef, uint32_t>>
+ getVariableLocation(StringRef var);
+
private:
const coff_section* getSection(uint32_t i);
const coff_section *getSection(COFFSymbolRef sym) {
@@ -212,6 +216,7 @@ private:
void initializeSymbols();
void initializeFlags();
void initializeDependencies();
+ void initializeDwarf();
SectionChunk *
readSection(uint32_t sectionNumber,
@@ -285,6 +290,15 @@ private:
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
std::vector<Symbol *> symbols;
+
+ std::unique_ptr<llvm::DWARFContext> dwarf;
+ std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
+ struct VarLoc {
+ const llvm::DWARFDebugLine::LineTable *lt;
+ unsigned file;
+ unsigned line;
+ };
+ llvm::DenseMap<StringRef, VarLoc> variableLoc;
};
// This type represents import library members that contain DLL names
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 2d971a023..183761f1c 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -520,15 +520,69 @@ void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
f->fetch();
}
-void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
- std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
- toString(existing->getFile()) + " and in " +
- toString(newFile);
+static std::string getSourceLocationBitcode(BitcodeFile *file) {
+ std::string res("\n>>> defined at ");
+ StringRef source = file->obj->getSourceFileName();
+ if (!source.empty())
+ res += source.str() + "\n>>> ";
+ res += toString(file);
+ return res;
+}
+
+static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc,
+ uint32_t offset, StringRef name) {
+ Optional<std::pair<StringRef, uint32_t>> fileLine;
+ if (sc)
+ fileLine = getFileLine(sc, offset);
+ if (!fileLine)
+ fileLine = file->getVariableLocation(name);
+
+ std::string res;
+ llvm::raw_string_ostream os(res);
+ os << "\n>>> defined at ";
+ if (fileLine)
+ os << fileLine->first << ":" << fileLine->second << "\n>>> ";
+ os << toString(file);
+ return os.str();
+}
+
+static std::string getSourceLocation(InputFile *file, SectionChunk *sc,
+ uint32_t offset, StringRef name) {
+ if (auto *o = dyn_cast<ObjFile>(file))
+ return getSourceLocationObj(o, sc, offset, name);
+ if (auto *b = dyn_cast<BitcodeFile>(file))
+ return getSourceLocationBitcode(b);
+ return "\n>>> defined at " + toString(file);
+}
+
+// Construct and print an error message in the form of:
+//
+// lld-link: error: duplicate symbol: foo
+// >>> defined at bar.c:30
+// >>> bar.o
+// >>> defined at baz.c:563
+// >>> baz.o
+void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
+ SectionChunk *newSc,
+ uint32_t newSectionOffset) {
+ std::string msg;
+ llvm::raw_string_ostream os(msg);
+ os << "duplicate symbol: " << toString(*existing);
+
+ DefinedRegular *d = cast<DefinedRegular>(existing);
+ if (d && isa<ObjFile>(d->getFile())) {
+ os << getSourceLocation(d->getFile(), d->getChunk(), d->getValue(),
+ existing->getName());
+ } else {
+ os << getSourceLocation(existing->getFile(), nullptr, 0, "");
+ }
+ os << getSourceLocation(newFile, newSc, newSectionOffset,
+ existing->getName());
if (config->forceMultiple)
- warn(msg);
+ warn(os.str());
else
- error(msg);
+ error(os.str());
}
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
@@ -568,8 +622,8 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
}
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
- const coff_symbol_generic *sym,
- SectionChunk *c) {
+ const coff_symbol_generic *sym, SectionChunk *c,
+ uint32_t sectionOffset) {
Symbol *s;
bool wasInserted;
std::tie(s, wasInserted) = insert(n, f);
@@ -577,7 +631,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
/*IsExternal*/ true, sym, c);
else
- reportDuplicate(s, f);
+ reportDuplicate(s, f, c, sectionOffset);
return s;
}
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 4ae818ef2..cd8a53dce 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -91,7 +91,7 @@ public:
Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
Symbol *addRegular(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr,
- SectionChunk *c = nullptr);
+ SectionChunk *c = nullptr, uint32_t sectionOffset = 0);
std::pair<DefinedRegular *, bool>
addComdat(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr);
@@ -103,7 +103,9 @@ public:
uint16_t machine);
void addLibcall(StringRef name);
- void reportDuplicate(Symbol *existing, InputFile *newFile);
+ void reportDuplicate(Symbol *existing, InputFile *newFile,
+ SectionChunk *newSc = nullptr,
+ uint32_t newSectionOffset = 0);
// A list of chunks which to be added to .rdata.
std::vector<Chunk *> localImportChunks;
diff --git a/test/COFF/conflict-mangled.test b/test/COFF/conflict-mangled.test
index eaaede537..381c03568 100644
--- a/test/COFF/conflict-mangled.test
+++ b/test/COFF/conflict-mangled.test
@@ -5,9 +5,13 @@
# RUN: not lld-link /out:%t.exe /demangle %t1.obj %t2.obj 2>&1 | FileCheck %s
# RUN: not lld-link /out:%t.exe /demangle:no %t1.obj %t2.obj 2>&1 | FileCheck --check-prefix=NODEMANGLE %s
-# NODEMANGLE: duplicate symbol: ?mangled@@YAHXZ in {{.+}}1.obj and in {{.+}}2.obj
+# NODEMANGLE: duplicate symbol: ?mangled@@YAHXZ
+# NODEMANGLE: defined at {{.+}}1.obj
+# NODEMANGLE: defined at {{.+}}2.obj
-# CHECK: duplicate symbol: int __cdecl mangled(void) in {{.+}}1.obj and in {{.+}}2.obj
+# CHECK: duplicate symbol: int __cdecl mangled(void)
+# CHECK: defined at {{.+}}1.obj
+# CHECK: defined at {{.+}}2.obj
--- !COFF
header:
diff --git a/test/COFF/conflict.test b/test/COFF/conflict.test
index ae8e6c8ad..b46895638 100644
--- a/test/COFF/conflict.test
+++ b/test/COFF/conflict.test
@@ -1,15 +1,21 @@
# REQUIRES: x86
# RUN: yaml2obj < %s > %t1.obj
# RUN: yaml2obj < %s > %t2.obj
-# RUN: not lld-link /out:%t.exe %t1.obj %t2.obj >& %t.log
-# RUN: FileCheck %s < %t.log
+# RUN: not lld-link /out:%t.exe %t1.obj %t2.obj 2>&1 | FileCheck --check-prefix=OBJ %s
# RUN: llvm-as -o %t.lto1.obj %S/Inputs/conflict.ll
# RUN: llvm-as -o %t.lto2.obj %S/Inputs/conflict.ll
-# RUN: not lld-link /out:%t.exe %t.lto1.obj %t.lto2.obj >& %t.log
-# RUN: FileCheck %s < %t.log
+# RUN: not lld-link /out:%t.exe %t.lto1.obj %t.lto2.obj 2>&1 | FileCheck --check-prefix=BC %s
-# CHECK: duplicate symbol: foo in {{.+}}1.obj and in {{.+}}2.obj
+# OBJ: duplicate symbol: foo
+# OBJ: defined at {{.+}}1.obj
+# OBJ: defined at {{.+}}2.obj
+
+# BC: duplicate symbol: foo
+# BC: defined at {{.+}}conflict.ll
+# BC: {{.+}}1.obj
+# BC: defined at {{.+}}conflict.ll
+# BC: {{.+}}2.obj
--- !COFF
header:
diff --git a/test/COFF/duplicate-cv.s b/test/COFF/duplicate-cv.s
new file mode 100644
index 000000000..2075cc364
--- /dev/null
+++ b/test/COFF/duplicate-cv.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: cp %t.obj %t.dupl.obj
+# RUN: not lld-link /out:%t.exe %t.obj %t.dupl.obj 2>&1 | FileCheck %s
+
+# CHECK: error: duplicate symbol: main
+# CHECK-NEXT: >>> defined at file1.cpp:2
+# CHECK-NEXT: >>> {{.*}}.obj
+# CHECK-NEXT: >>> defined at {{.*}}.obj
+
+ .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
+ .cv_loc 0 1 2 0
+ retq
+.Lfunc_end0:
+
+ .section .debug$S,"dr",associative,main
+ .long 4
+ .cv_linetable 0, main, .Lfunc_end0
+
+ .section .debug$S,"dr"
+ .long 4
+ .cv_filechecksums
+ .cv_stringtable
diff --git a/test/COFF/duplicate-dwarf.s b/test/COFF/duplicate-dwarf.s
new file mode 100644
index 000000000..b81c13c43
--- /dev/null
+++ b/test/COFF/duplicate-dwarf.s
@@ -0,0 +1,213 @@
+# REQUIRES: x86
+# RUN: llvm-mc -triple=i686-windows-gnu -filetype=obj -o %t.o %s
+# RUN: cp %t.o %t.dupl.o
+# RUN: not lld-link -lldmingw -out:%t.exe %t.o %t.dupl.o -entry:_Z4funcv 2>&1 | FileCheck %s
+
+# CHECK: error: duplicate symbol: func()
+# CHECK-NEXT: >>> defined at /path/to/src{{[/\\]}}dupl.cpp:6
+# CHECK-NEXT: >>> {{.*}}.o
+# CHECK-NEXT: >>> defined at /path/to/src{{[/\\]}}dupl.cpp:6
+# CHECK-NEXT: >>> {{.*}}.o
+# CHECK-EMPTY:
+# CHECK-NEXT: error: duplicate symbol: _var
+# CHECK-NEXT: >>> defined at /path/to/src{{[/\\]}}dupl.cpp:1
+# CHECK-NEXT: >>> {{.*}}.o
+# CHECK-NEXT: >>> defined at /path/to/src{{[/\\]}}dupl.cpp:1
+# CHECK-NEXT: >>> {{.*}}.o
+# CHECK-EMPTY:
+# CHECK-NEXT: error: duplicate symbol: A::namespaceVar
+# CHECK-NEXT: >>> defined at /path/to/src{{[/\\]}}dupl.cpp:3
+# CHECK-NEXT: >>> {{.*}}.o
+# CHECK-NEXT: >>> defined at /path/to/src{{[/\\]}}dupl.cpp:3
+# CHECK-NEXT: >>> {{.*}}.o
+
+ .text
+ .file "dupl.cpp"
+ .file 1 "/path/to/src" "dupl.cpp"
+ .def __Z4funcv;
+ .globl __Z4funcv # -- Begin function _Z4funcv
+__Z4funcv: # @_Z4funcv
+Lfunc_begin0:
+ .loc 1 5 0 # dupl.cpp:5:0
+# %bb.0: # %entry
+ .loc 1 6 1 prologue_end # dupl.cpp:6:1
+ retl
+Lfunc_end0:
+ # -- End function
+ .bss
+ .globl _var # @var
+_var:
+ .long 0 # 0x0
+
+ .globl __ZN1A12namespaceVarE # @_ZN1A12namespaceVarE
+__ZN1A12namespaceVarE:
+ .long 0 # 0x0
+
+ .section .debug_str,"dr"
+Linfo_string:
+Linfo_string0:
+ .asciz "var"
+Linfo_string1:
+ .asciz "int"
+Linfo_string2:
+ .asciz "A"
+Linfo_string3:
+ .asciz "namespaceVar"
+Linfo_string4:
+ .asciz "_ZN1A12namespaceVarE"
+Linfo_string5:
+ .asciz "_Z4funcv"
+Linfo_string6:
+ .asciz "func"
+ .section .debug_abbrev,"dr"
+Lsection_abbrev:
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 57 # DW_TAG_namespace
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 110 # DW_AT_linkage_name
+ .byte 14 # DW_FORM_strp
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 0 # DW_CHILDREN_no
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 110 # DW_AT_linkage_name
+ .byte 14 # DW_FORM_strp
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"dr"
+Lsection_info:
+Lcu_begin0:
+ .long Ldebug_info_end0-Ldebug_info_start0 # Length of Unit
+Ldebug_info_start0:
+ .short 4 # DWARF version number
+ .secrel32 Lsection_abbrev # Offset Into Abbrev. Section
+ .byte 4 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x64 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 33 # DW_AT_language
+ .byte 0 # DW_AT_name
+ .secrel32 Lline_table_start0 # DW_AT_stmt_list
+ .long Lfunc_begin0 # DW_AT_low_pc
+ .long Lfunc_end0-Lfunc_begin0 # DW_AT_high_pc
+ .byte 2 # Abbrev [2] 0x26:0x11 DW_TAG_variable
+ .secrel32 Linfo_string0 # DW_AT_name
+ .secrel32 Linfo_type_int # DW_AT_type
+ # DW_AT_external
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .byte 5 # DW_AT_location
+ .byte 3
+ .long _var
+Linfo_type_int:
+ .byte 3 # Abbrev [3] 0x37:0x7 DW_TAG_base_type
+ .secrel32 Linfo_string1 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 4 # Abbrev [4] 0x3e:0x1b DW_TAG_namespace
+ .secrel32 Linfo_string2 # DW_AT_name
+ .byte 5 # Abbrev [5] 0x43:0x15 DW_TAG_variable
+ .secrel32 Linfo_string3 # DW_AT_name
+ .secrel32 Linfo_type_int # DW_AT_type
+ # DW_AT_external
+ .byte 1 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .byte 5 # DW_AT_location
+ .byte 3
+ .long __ZN1A12namespaceVarE
+ .secrel32 Linfo_string4 # DW_AT_linkage_name
+ .byte 0 # End Of Children Mark
+ .byte 6 # Abbrev [6] 0x59:0x15 DW_TAG_subprogram
+ .long Lfunc_begin0 # DW_AT_low_pc
+ .long Lfunc_end0-Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 84
+ .secrel32 Linfo_string5 # DW_AT_linkage_name
+ .secrel32 Linfo_string6 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ # DW_AT_external
+ .byte 0 # End Of Children Mark
+Ldebug_info_end0:
+
+ .section .debug_line,"dr"
+Lline_table_start0:
diff --git a/test/COFF/duplicate.test b/test/COFF/duplicate.test
index 0c0f3513a..76c88b070 100644
--- a/test/COFF/duplicate.test
+++ b/test/COFF/duplicate.test
@@ -4,10 +4,14 @@ RUN: llc -mtriple x86_64-windows-msvc -filetype obj -o beta.obj %S/Inputs/beta.l
RUN: lld-link /out:alpha.dll /dll alpha.obj /implib:alpha.lib
RUN: not lld-link /out:beta.dll /dll alpha.obj beta.obj alpha.lib 2>&1 | FileCheck %s -check-prefix CHECK-ALPHA
-CHECK-ALPHA: error: duplicate symbol: f in {{.*}}alpha.obj and in alpha.dll
+CHECK-ALPHA: error: duplicate symbol: f
+CHECK-ALPHA: defined at {{.*}}alpha.obj
+CHECK-APLHA: defined at alpha.dll
RUN: llc -mtriple x86_64-windows-msvc -filetype obj -o gamma.obj %S/Inputs/gamma.ll
RUN: not lld-link /out:gamma.exe /subsystem:console /entry:mainCRTStartup gamma.obj alpha.lib 2>&1 | FileCheck %s -check-prefix CHECK-GAMMA
-CHECK-GAMMA: error: duplicate symbol: __declspec(dllimport) f in {{.*}}gamma.obj and in alpha.dll
+CHECK-GAMMA: error: duplicate symbol: __declspec(dllimport) f
+CHECK-GAMMA: defined at {{.*}}gamma.obj
+CHECK-GAMMA: defined at alpha.dll