From 6747d44bda8c460c90c2276ac6e2402d434e0780 Mon Sep 17 00:00:00 2001 From: Kyungwoo Lee Date: Sun, 14 Nov 2021 20:18:36 -0800 Subject: [PATCH] [DebugInfo] Fix end_sequence of debug_line in LTO Object In a LTO build, the `end_sequence` in debug_line table for each compile unit (CU) points the end of text section which merged all CUs. The `end_sequence` needs to point to the end of each CU's range. This bug often causes invalid `debug_line` table in the final `.dSYM` binary for MachO after running `dsymutil` which tries to compensate an out-of-range address of `end_sequence`. The fix is to sync the line table termination with the range operations that are already maintained in DwarfDebug. When CU or section changes, or nodebug functions appear or module is finished, the prior pending line table is terminated using the last range label. In the MC path where no range is tracked, the old logic is conservatively used to end the line table using the section end symbol. Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D108261 --- llvm/include/llvm/MC/MCDwarf.h | 13 +++++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 6 ++- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 16 +++++++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h | 3 ++ llvm/lib/MC/MCDwarf.cpp | 59 ++++++++++++++++++----- llvm/test/DebugInfo/XCOFF/empty.ll | 8 ++-- llvm/test/DebugInfo/XCOFF/explicit-section.ll | 8 ++-- llvm/test/DebugInfo/XCOFF/function-sections.ll | 8 ++-- llvm/test/DebugInfo/debugline-endsequence.ll | 61 ++++++++++++++++++++++++ llvm/test/DebugInfo/debugline-endsequence.s | 19 ++++++++ 10 files changed, 177 insertions(+), 24 deletions(-) create mode 100644 llvm/test/DebugInfo/debugline-endsequence.ll create mode 100644 llvm/test/DebugInfo/debugline-endsequence.s diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h index 84386f1..7e72d56 100644 --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -188,6 +188,15 @@ public: MCSymbol *getLabel() const { return Label; } + // This indicates the line entry is synthesized for an end entry. + bool IsEndEntry = false; + + // Override the label with the given EndLabel. + void setEndLabel(MCSymbol *EndLabel) { + Label = EndLabel; + IsEndEntry = true; + } + // This is called when an instruction is assembled into the specified // section and if there is information from the last .loc directive that // has yet to have a line entry made for it is made. @@ -205,6 +214,10 @@ public: MCLineDivisions[Sec].push_back(LineEntry); } + // Add an end entry by cloning the last entry, if exists, for the section + // the given EndLabel belongs to. The label is replaced by the given EndLabel. + void addEndEntry(MCSymbol *EndLabel); + using MCDwarfLineEntryCollection = std::vector; using iterator = MCDwarfLineEntryCollection::iterator; using const_iterator = MCDwarfLineEntryCollection::const_iterator; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index 8c0a1f6..5615245 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -367,7 +367,8 @@ DIE *DwarfCompileUnit::getOrCreateCommonBlock( void DwarfCompileUnit::addRange(RangeSpan Range) { DD->insertSectionLabel(Range.Begin); - bool SameAsPrevCU = this == DD->getPrevCU(); + auto *PrevCU = DD->getPrevCU(); + bool SameAsPrevCU = this == PrevCU; DD->setPrevCU(this); // If we have no current ranges just add the range and return, otherwise, // check the current section and CU against the previous section and CU we @@ -376,6 +377,9 @@ void DwarfCompileUnit::addRange(RangeSpan Range) { if (CURanges.empty() || !SameAsPrevCU || (&CURanges.back().End->getSection() != &Range.End->getSection())) { + // Before a new range is added, always terminate the prior line table. + if (PrevCU) + DD->terminateLineTable(PrevCU); CURanges.push_back(Range); return; } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 9388cd6..047676d 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1407,6 +1407,10 @@ void DwarfDebug::finalizeModuleInfo() { // Emit all Dwarf sections that should come after the content. void DwarfDebug::endModule() { + // Terminate the pending line table. + if (PrevCU) + terminateLineTable(PrevCU); + PrevCU = nullptr; assert(CurFn == nullptr); assert(CurMI == nullptr); @@ -2172,10 +2176,22 @@ DwarfDebug::getDwarfCompileUnitIDForLineTable(const DwarfCompileUnit &CU) { return CU.getUniqueID(); } +void DwarfDebug::terminateLineTable(const DwarfCompileUnit *CU) { + const auto &CURanges = CU->getRanges(); + auto &LineTable = Asm->OutStreamer->getContext().getMCDwarfLineTable( + getDwarfCompileUnitIDForLineTable(*CU)); + // Add the last range label for the given CU. + LineTable.getMCLineSections().addEndEntry( + const_cast(CURanges.back().End)); +} + void DwarfDebug::skippedNonDebugFunction() { // If we don't have a subprogram for this function then there will be a hole // in the range information. Keep note of this by setting the previously used // section to nullptr. + // Terminate the pending line table. + if (PrevCU) + terminateLineTable(PrevCU); PrevCU = nullptr; CurFn = nullptr; } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h index 6de0956..8ea865cf 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -781,6 +781,9 @@ public: const DwarfCompileUnit *getPrevCU() const { return PrevCU; } void setPrevCU(const DwarfCompileUnit *PrevCU) { this->PrevCU = PrevCU; } + /// Terminate the line table by adding the last range label. + void terminateLineTable(const DwarfCompileUnit *CU); + /// Returns the entries for the .debug_loc section. const DebugLocStream &getDebugLocs() const { return DebugLocs; } diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp index ef0cc81..1c9cfb9 100644 --- a/llvm/lib/MC/MCDwarf.cpp +++ b/llvm/lib/MC/MCDwarf.cpp @@ -141,6 +141,24 @@ makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) { return Res; } +void MCLineSection::addEndEntry(MCSymbol *EndLabel) { + auto *Sec = &EndLabel->getSection(); + // The line table may be empty, which we should skip adding an end entry. + // There are two cases: + // (1) MCAsmStreamer - emitDwarfLocDirective emits a location directive in + // place instead of adding a line entry if the target has + // usesDwarfFileAndLocDirectives. + // (2) MCObjectStreamer - if a function has incomplete debug info where + // instructions don't have DILocations, the line entries are missing. + auto I = MCLineDivisions.find(Sec); + if (I != MCLineDivisions.end()) { + auto &Entries = I->second; + auto EndEntry = Entries.back(); + EndEntry.setEndLabel(EndLabel); + Entries.push_back(EndEntry); + } +} + // // This emits the Dwarf line table for the specified section from the entries // in the LineSection. @@ -148,16 +166,33 @@ makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) { void MCDwarfLineTable::emitOne( MCStreamer *MCOS, MCSection *Section, const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { - unsigned FileNum = 1; - unsigned LastLine = 1; - unsigned Column = 0; - unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; - unsigned Isa = 0; - unsigned Discriminator = 0; - MCSymbol *LastLabel = nullptr; + + unsigned FileNum, LastLine, Column, Flags, Isa, Discriminator; + MCSymbol *LastLabel; + auto init = [&]() { + FileNum = 1; + LastLine = 1; + Column = 0; + Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; + Isa = 0; + Discriminator = 0; + LastLabel = nullptr; + }; + init(); // Loop through each MCDwarfLineEntry and encode the dwarf line number table. + bool EndEntryEmitted = false; for (const MCDwarfLineEntry &LineEntry : LineEntries) { + MCSymbol *Label = LineEntry.getLabel(); + const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo(); + if (LineEntry.IsEndEntry) { + MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, Label, + asmInfo->getCodePointerSize()); + init(); + EndEntryEmitted = true; + continue; + } + int64_t LineDelta = static_cast(LineEntry.getLine()) - LastLine; if (FileNum != LineEntry.getFileNum()) { @@ -195,12 +230,9 @@ void MCDwarfLineTable::emitOne( if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin); - MCSymbol *Label = LineEntry.getLabel(); - // At this point we want to emit/create the sequence to encode the delta in // line numbers and the increment of the address from the previous Label // and the current Label. - const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo(); MCOS->emitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label, asmInfo->getCodePointerSize()); @@ -210,7 +242,12 @@ void MCDwarfLineTable::emitOne( } // Generate DWARF line end entry. - MCOS->emitDwarfLineEndEntry(Section, LastLabel); + // We do not need this for DwarfDebug that explicitly terminates the line + // table using ranges whenever CU or section changes. However, the MC path + // does not track ranges nor terminate the line table. In that case, + // conservatively use the section end symbol to end the line table. + if (!EndEntryEmitted) + MCOS->emitDwarfLineEndEntry(Section, LastLabel); } // diff --git a/llvm/test/DebugInfo/XCOFF/empty.ll b/llvm/test/DebugInfo/XCOFF/empty.ll index 0bd1a1b..d70d1c4 100644 --- a/llvm/test/DebugInfo/XCOFF/empty.ll +++ b/llvm/test/DebugInfo/XCOFF/empty.ll @@ -227,10 +227,10 @@ entry: ; ASM32-NEXT: .byte 3 # Advance line 1 ; ASM32-NEXT: .byte 1 ; ASM32-NEXT: .byte 1 -; ASM32-NEXT: .byte 0 # Set address to L..sec_end0 +; ASM32-NEXT: .byte 0 # Set address to L..func_end0 ; ASM32-NEXT: .byte 5 ; ASM32-NEXT: .byte 2 -; ASM32-NEXT: .vbyte 4, L..sec_end0 +; ASM32-NEXT: .vbyte 4, L..func_end0 ; ASM32-NEXT: .byte 0 # End sequence ; ASM32-NEXT: .byte 1 ; ASM32-NEXT: .byte 1 @@ -428,10 +428,10 @@ entry: ; ASM64-NEXT: .byte 3 # Advance line 1 ; ASM64-NEXT: .byte 1 ; ASM64-NEXT: .byte 1 -; ASM64-NEXT: .byte 0 # Set address to L..sec_end0 +; ASM64-NEXT: .byte 0 # Set address to L..func_end0 ; ASM64-NEXT: .byte 9 ; ASM64-NEXT: .byte 2 -; ASM64-NEXT: .vbyte 8, L..sec_end0 +; ASM64-NEXT: .vbyte 8, L..func_end0 ; ASM64-NEXT: .byte 0 # End sequence ; ASM64-NEXT: .byte 1 ; ASM64-NEXT: .byte 1 diff --git a/llvm/test/DebugInfo/XCOFF/explicit-section.ll b/llvm/test/DebugInfo/XCOFF/explicit-section.ll index 73a091b..2082fdd 100644 --- a/llvm/test/DebugInfo/XCOFF/explicit-section.ll +++ b/llvm/test/DebugInfo/XCOFF/explicit-section.ll @@ -296,10 +296,10 @@ entry: ; CHECK-NEXT: .byte 3 # Advance line 0 ; CHECK-NEXT: .byte 0 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end0 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end0 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 @@ -328,10 +328,10 @@ entry: ; CHECK-NEXT: .byte 3 # Advance line 0 ; CHECK-NEXT: .byte 0 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end1 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end1 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 diff --git a/llvm/test/DebugInfo/XCOFF/function-sections.ll b/llvm/test/DebugInfo/XCOFF/function-sections.ll index 0769fe3..661bf2f 100644 --- a/llvm/test/DebugInfo/XCOFF/function-sections.ll +++ b/llvm/test/DebugInfo/XCOFF/function-sections.ll @@ -283,10 +283,10 @@ entry: ; CHECK-NEXT: .byte 3 # Advance line 1 ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end0 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end0 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 @@ -305,10 +305,10 @@ entry: ; CHECK-NEXT: .byte 3 # Advance line 1 ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end1 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end1 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 diff --git a/llvm/test/DebugInfo/debugline-endsequence.ll b/llvm/test/DebugInfo/debugline-endsequence.ll new file mode 100644 index 0000000..a672278 --- /dev/null +++ b/llvm/test/DebugInfo/debugline-endsequence.ll @@ -0,0 +1,61 @@ +; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump --debug-line - | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx12.0.0" + +; Check if the end_sequences are emitted for each debug range. + +; CU1 Line table +; CHECK: 0x0000000000000004 [[T:.*]] end_sequence +; CHECK: 0x0000000000000010 [[T:.*]] end_sequence +; +; CU2 Line table +; CHECK: 0x0000000000000008 [[T:.*]] end_sequence + +; CU1 (0x0 ~ 0x4) +define void @f1() !dbg !15 { + ret void, !dbg !18 +} + +; CU2 (0x4 ~ 0x8) +define void @f2() !dbg !21 { + ret void, !dbg !22 +} + +; CU2 (nodebug) - (0x8 ~ 0xc) +define void @f3() { + ret void +} + +; CU1 (0xc ~ 0x10) +define void @f4() !dbg !19 { + ret void, !dbg !20 +} + +!llvm.dbg.cu = !{!0, !3} +!llvm.ident = !{!5, !5} +!llvm.module.flags = !{!6, !7, !8, !9, !10, !11, !12, !13, !14} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "LLVM", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None, sysroot: "/") +!1 = !DIFile(filename: "", directory: "/") +!2 = !{} +!3 = distinct !DICompileUnit(language: DW_LANG_C99, file: !4, producer: "LLVM", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None, sysroot: "/") +!4 = !DIFile(filename: "", directory: "/") +!5 = !{!"Apple clang version 13.0.0 (clang-1300.0.29.3)"} +!6 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 3]} +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 1, !"branch-target-enforcement", i32 0} +!11 = !{i32 1, !"sign-return-address", i32 0} +!12 = !{i32 1, !"sign-return-address-all", i32 0} +!13 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!14 = !{i32 7, !"PIC Level", i32 2} +!15 = distinct !DISubprogram(name: "f1", scope: !1, file: !1, line: 1, type: !16, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!16 = !DISubroutineType(types: !17) +!17 = !{null} +!18 = !DILocation(line: 2, column: 1, scope: !15) +!19 = distinct !DISubprogram(name: "f4", scope: !1, file: !1, line: 4, type: !16, scopeLine: 4, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!20 = !DILocation(line: 5, column: 1, scope: !19) +!21 = distinct !DISubprogram(name: "f2", scope: !4, file: !4, line: 1, type: !16, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !2) +!22 = !DILocation(line: 2, column: 1, scope: !21) diff --git a/llvm/test/DebugInfo/debugline-endsequence.s b/llvm/test/DebugInfo/debugline-endsequence.s new file mode 100644 index 0000000..69fc184 --- /dev/null +++ b/llvm/test/DebugInfo/debugline-endsequence.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o - | llvm-dwarfdump --debug-line - | FileCheck %s + +# The line table is open in the MC path. +# The end sequence is emitted using the section end label. + +# CHECK: 0x0000000000000001 [[T:.*]] end_sequence +# CHECK: 0x0000000000000001 [[T:.*]] end_sequence + + .text + .section .text.f1 +f1: + .file 1 "/" "t1.c" + .loc 1 1 0 + nop + + .section .text.f2 +f2: + .loc 1 2 0 + nop -- 2.7.4