Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -17,6 +17,7 @@ #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/IR/Comdat.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" @@ -25,7 +26,6 @@ #include namespace llvm { -class DWARFDebugLine; class TarWriter; struct DILineInfo; namespace lto { @@ -215,8 +215,10 @@ // reporting. Linker may find reasonable number of errors in a // single object file, so we cache debugging information in order to // parse it only once for each object file we link. + std::vector LineTables; std::unique_ptr DwarfLine; struct VarLoc { + const llvm::DWARFDebugLine::LineTable *LT; unsigned File; unsigned Line; }; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -121,45 +121,46 @@ DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE, Config->Wordsize); - // The second parameter is offset in .debug_line section - // for compilation unit (CU) of interest. We have only one - // CU (object file), so offset is always 0. - const DWARFDebugLine::LineTable *LT = - DwarfLine->getOrParseLineTable(LineData, 0, Dwarf, nullptr); - if (!LT) - return; - - // Return if there is no debug information about CU available. - if (!Dwarf.getNumCompileUnits()) - return; - - // Loop over variable records and insert them to VariableLoc. - DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0); - for (const auto &Entry : CU->dies()) { - DWARFDie Die(CU, &Entry); - // Skip all tags that are not variables. - if (Die.getTag() != dwarf::DW_TAG_variable) + for (std::unique_ptr &CU : Dwarf.compile_units()) { + DWARFDie UnitDIE = CU->getUnitDIE(); + Optional Offset = + dwarf::toSectionOffset(UnitDIE.find(dwarf::DW_AT_stmt_list)); + if (!Offset) 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)) + const DWARFDebugLine::LineTable *LT = + DwarfLine->getOrParseLineTable(LineData, *Offset, Dwarf, nullptr); + if (!LT) continue; + LineTables.push_back(LT); - // 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; + // 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 line number on which the variable is declared. - unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); + // 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); - // Get the name of the variable and add the collected information to - // VariableLoc. Usually Name is non-empty, but it can be empty if the input - // object file lacks some debug info. - StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), ""); - if (!Name.empty()) - VariableLoc.insert({Name, {File, Line}}); + // Get the name of the variable and add the collected information to + // VariableLoc. Usually Name is non-empty, but it can be empty if the + // input object file lacks some debug info. + StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), ""); + if (!Name.empty()) + VariableLoc.insert({Name, {LT, File, Line}}); + } } } @@ -170,11 +171,6 @@ ObjFile::getVariableLoc(StringRef Name) { llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); - // There is always only one CU so it's offset is 0. - const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0); - if (!LT) - return None; - // Return if we have no debug information about data object. auto It = VariableLoc.find(Name); if (It == VariableLoc.end()) @@ -182,7 +178,7 @@ // Take file name string from line table. std::string FileName; - if (!LT->getFileNameByIndex( + if (!It->second.LT->getFileNameByIndex( It->second.File, nullptr, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName)) return None; @@ -197,20 +193,17 @@ uint64_t Offset) { llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); - // The offset to CU is 0. - const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0); - if (!Tbl) - return None; - // Use fake address calcuated by adding section file offset and offset in // section. See comments for ObjectInfo class. DILineInfo Info; - Tbl->getFileLineInfoForAddress( - S->getOffsetInFile() + Offset, nullptr, - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info); - if (Info.Line == 0) - return None; - return Info; + for (const llvm::DWARFDebugLine::LineTable *LT : LineTables) { + LT->getFileLineInfoForAddress( + S->getOffsetInFile() + Offset, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info); + if (Info.Line != 0) + return Info; + } + return None; } // Returns source line information for a given offset using DWARF debug info. Index: test/ELF/Inputs/multiple-cu.s =================================================================== --- /dev/null +++ test/ELF/Inputs/multiple-cu.s @@ -0,0 +1,24 @@ + .file 1 "test2.c" + .loc 1 2 0 + jmp bar + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + .section .debug_info,"",@progbits + .long .Lend0 - .Lbegin0 # Length of Unit +.Lbegin0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit + .long .debug_line # DW_AT_stmt_list +.Lend0: + .section .debug_line,"",@progbits Index: test/ELF/multiple-cu.s =================================================================== --- /dev/null +++ test/ELF/multiple-cu.s @@ -0,0 +1,38 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/multiple-cu.s -o %t2.o +# RUN: ld.lld -r -o %t.o %t1.o %t2.o +# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s + +# CHECK: error: undefined symbol: foo +# CHECK-NEXT: referenced by test1.c:2 + +# CHECK: error: undefined symbol: bar +# CHECK-NEXT: referenced by test2.c:2 + + .globl _start +_start: + .file 1 "test1.c" + .loc 1 2 0 + jmp foo + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + .section .debug_info,"",@progbits + .long .Lend0 - .Lbegin0 # Length of Unit +.Lbegin0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit + .long .debug_line # DW_AT_stmt_list +.Lend0: + .section .debug_line,"",@progbits