forked from OSchip/llvm-project
[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
This commit is contained in:
parent
cda72e9297
commit
6747d44bda
|
@ -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<MCDwarfLineEntry>;
|
||||
using iterator = MCDwarfLineEntryCollection::iterator;
|
||||
using const_iterator = MCDwarfLineEntryCollection::const_iterator;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<MCSymbol *>(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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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<int64_t>(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);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: "<stdin>", 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: "<stdin>", 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)
|
|
@ -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
|
Loading…
Reference in New Issue