diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h --- a/lld/COFF/InputFiles.h +++ b/lld/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 @@ // The .debug$T stream if there's one. llvm::Optional debugTypes; + llvm::Optional> + getVariableLocation(StringRef var); + private: const coff_section* getSection(uint32_t i); const coff_section *getSection(COFFSymbolRef sym) { @@ -212,6 +216,7 @@ void initializeSymbols(); void initializeFlags(); void initializeDependencies(); + void initializeDwarf(); SectionChunk * readSection(uint32_t sectionNumber, @@ -285,6 +290,15 @@ // index. Nonexistent indices (which are occupied by auxiliary // symbols in the real symbol table) are filled with null pointers. std::vector symbols; + + std::unique_ptr dwarf; + std::vector lineTables; + struct VarLoc { + const llvm::DWARFDebugLine::LineTable *lt; + unsigned file; + unsigned line; + }; + llvm::DenseMap variableLoc; }; // This type represents import library members that contain DLL names diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -382,7 +382,8 @@ 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 @@ // 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 @@ 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> +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 &cu : dwarf->compile_units()) { + auto report = [](Error err) { + handleAllErrors(std::move(err), + [](ErrorInfoBase &info) { warn(info.message()); }); + }; + Expected 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/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -91,7 +91,7 @@ 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 addComdat(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr); @@ -103,7 +103,9 @@ 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 localImportChunks; diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -520,15 +520,69 @@ 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> 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(file)) + return getSourceLocationObj(o, sc, offset, name); + if (auto *b = dyn_cast(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(existing); + if (d && isa(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::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 @@ replaceSymbol(s, f, n, /*IsCOMDAT*/ false, /*IsExternal*/ true, sym, c); else - reportDuplicate(s, f); + reportDuplicate(s, f, c, sectionOffset); return s; } diff --git a/lld/test/COFF/conflict-mangled.test b/lld/test/COFF/conflict-mangled.test --- a/lld/test/COFF/conflict-mangled.test +++ b/lld/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/lld/test/COFF/conflict.test b/lld/test/COFF/conflict.test --- a/lld/test/COFF/conflict.test +++ b/lld/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/lld/test/COFF/duplicate-cv.s b/lld/test/COFF/duplicate-cv.s new file mode 100644 --- /dev/null +++ b/lld/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/lld/test/COFF/duplicate-dwarf.s b/lld/test/COFF/duplicate-dwarf.s new file mode 100644 --- /dev/null +++ b/lld/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/lld/test/COFF/duplicate.test b/lld/test/COFF/duplicate.test --- a/lld/test/COFF/duplicate.test +++ b/lld/test/COFF/duplicate.test @@ -4,10 +4,14 @@ 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