Skip to content

Commit aab79c4

Browse files
authored
[DebugInfo] Fix issue with debug line table offsets for empty functions (#142253)
This patch addresses an issue where an anonymous DWARF line table symbol could be inadvertently defined multiple times, leading to an "symbol '' is already defined" error during assembly or object file emission. This issue happens for empty functions when `-emit-func-debug-line-table-offsets` is enabled. The root cause is the creation of the "end sequence" entry for a DWARF line table. This entry was sometimes created by copying the last existing line table entry. If this last entry was a special one (created for the purpose of marking the position in the line table stream and having an anonymous symbol attached), the copied end-sequence entry would also incorrectly reference this same anonymous symbol. Consequently, when the line table was finally emitted, the DWARF emission logic would attempt to emit a label for this anonymous symbol twice, triggering the redefinition error. The fix ensures that when an end-sequence line table entry is created, it does not inherit any special stream label from the entry it might have been based on, thereby preventing the duplicate label emission.
1 parent 2e70da3 commit aab79c4

File tree

3 files changed

+63
-9
lines changed

3 files changed

+63
-9
lines changed

llvm/include/llvm/MC/MCDwarf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ class MCDwarfLineEntry : public MCDwarfLoc {
216216

217217
// Override the label with the given EndLabel.
218218
void setEndLabel(MCSymbol *EndLabel) {
219+
// If we're setting this to be an end entry, make sure we don't have
220+
// LineStreamLabel set.
221+
assert(LineStreamLabel == nullptr);
219222
Label = EndLabel;
220223
IsEndEntry = true;
221224
}

llvm/lib/MC/MCDwarf.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,33 @@ makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) {
144144
void MCLineSection::addEndEntry(MCSymbol *EndLabel) {
145145
auto *Sec = &EndLabel->getSection();
146146
// The line table may be empty, which we should skip adding an end entry.
147-
// There are two cases:
147+
// There are three cases:
148148
// (1) MCAsmStreamer - emitDwarfLocDirective emits a location directive in
149149
// place instead of adding a line entry if the target has
150150
// usesDwarfFileAndLocDirectives.
151151
// (2) MCObjectStreamer - if a function has incomplete debug info where
152152
// instructions don't have DILocations, the line entries are missing.
153+
// (3) It's also possible that there are no prior line entries if the section
154+
// itself is empty before this end label.
153155
auto I = MCLineDivisions.find(Sec);
154-
if (I != MCLineDivisions.end()) {
155-
auto &Entries = I->second;
156-
auto EndEntry = Entries.back();
157-
EndEntry.setEndLabel(EndLabel);
158-
Entries.push_back(EndEntry);
159-
}
156+
if (I == MCLineDivisions.end()) // If section not found, do nothing.
157+
return;
158+
159+
auto &Entries = I->second;
160+
// If no entries in this section's list, nothing to base the end entry on.
161+
if (Entries.empty())
162+
return;
163+
164+
// Create the end entry based on the last existing entry.
165+
MCDwarfLineEntry EndEntry = Entries.back();
166+
167+
// An end entry is just for marking the end of a sequence of code locations.
168+
// It should not carry forward a LineStreamLabel from a previous special entry
169+
// if Entries.back() happened to be such an entry. So here we clear
170+
// LineStreamLabel.
171+
EndEntry.LineStreamLabel = nullptr;
172+
EndEntry.setEndLabel(EndLabel);
173+
Entries.push_back(EndEntry);
160174
}
161175

162176
//
@@ -1227,7 +1241,7 @@ void MCGenDwarfInfo::Emit(MCStreamer *MCOS) {
12271241
// a dwarf label.
12281242
//
12291243
void MCGenDwarfLabelEntry::Make(MCSymbol *Symbol, MCStreamer *MCOS,
1230-
SourceMgr &SrcMgr, SMLoc &Loc) {
1244+
SourceMgr &SrcMgr, SMLoc &Loc) {
12311245
// We won't create dwarf labels for temporary symbols.
12321246
if (Symbol->isTemporary())
12331247
return;
@@ -1295,7 +1309,7 @@ static unsigned getSizeForEncoding(MCStreamer &streamer,
12951309
}
12961310

12971311
static void emitFDESymbol(MCObjectStreamer &streamer, const MCSymbol &symbol,
1298-
unsigned symbolEncoding, bool isEH) {
1312+
unsigned symbolEncoding, bool isEH) {
12991313
MCContext &context = streamer.getContext();
13001314
const MCAsmInfo *asmInfo = context.getAsmInfo();
13011315
const MCExpr *v = asmInfo->getExprForFDESymbol(&symbol,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; RUN: llc -filetype=obj -emit-func-debug-line-table-offsets < %s -o %t_yes_off.o
2+
; RUN: llc -filetype=obj < %s -o %t_no_off.o
3+
; RUN: llvm-dwarfdump -v -all %t_no_off.o | FileCheck --check-prefix=CHECK-NO-OFF %s
4+
; RUN: llvm-dwarfdump -v -all %t_yes_off.o | FileCheck --check-prefix=CHECK-YES-OFF %s
5+
6+
; We don't need a line table for an empty function
7+
; CHECK-NO-OFF-NOT: DW_LNE_set_address
8+
9+
; CHECK-YES-OFF: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] ({{.*}})
10+
; CHECK-YES-OFF: Address
11+
; CHECK-YES-OFF-NEXT: ------------------
12+
; CHECK-YES-OFF-NEXT: DW_LNE_set_address ({{.*}})
13+
; CHECK-YES-OFF-NEXT: DW_LNE_end_sequence
14+
; CHECK-YES-OFF-NEXT: 0x0000000000000000 {{.*}} is_stmt end_sequence
15+
16+
; IR generated by llvm-reduce from LTO repro
17+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
18+
target triple = "x86_64-unknown-linux-gnu"
19+
20+
define ptr @_my_test_function() !dbg !4 {
21+
entry:
22+
unreachable
23+
}
24+
25+
!llvm.dbg.cu = !{!0}
26+
!llvm.module.flags = !{!3}
27+
28+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 21.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, imports: !2, splitDebugInlining: false, nameTableKind: None)
29+
!1 = !DIFile(filename: "repro_bug.cpp", directory: "/tmp/repro", checksumkind: CSK_MD5, checksum: "74e33e88b3108a4f94403da9fdb362f5")
30+
!2 = !{}
31+
!3 = !{i32 2, !"Debug Info Version", i32 3}
32+
!4 = distinct !DISubprogram(name: "east", linkageName: "_my_test_function", scope: !6, file: !5, line: 114, type: !8, scopeLine: 114, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !9, retainedNodes: !2)
33+
!5 = !DIFile(filename: "repro_bug.cpp", directory: "/tmp/repro", checksumkind: CSK_MD5, checksum: "74e33e88b3108a4f94403da9fdb362f5")
34+
!6 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "rectangle", file: !5, line: 103, size: 256, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !2, vtableHolder: !7)
35+
!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "shape", file: !5, line: 58, size: 128, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !2, vtableHolder: !7, identifier: "_ZTS5shape")
36+
!8 = distinct !DISubroutineType(types: !2)
37+
!9 = !DISubprogram(name: "east", linkageName: "_my_test_function", scope: !6, file: !5, line: 114, type: !8, scopeLine: 114, containingType: !6, virtualIndex: 2, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagVirtual | DISPFlagOptimized)

0 commit comments

Comments
 (0)