Skip to content

Commit 8eb464f

Browse files
committed
[DebugInfo] Allow parsing line tables aligned to 4 or 8-byte boundaries
This allows the DWARFDebugLine::SectionParser to try parsing line tables at 4 or 8-byte boundaries if the unaligned offset appears invalid. If aligning the offset does not reduce errors the offset is used unchanged. This is needed for llvm-dwarfdump to be able to extract the line tables (with --debug-lines) from binaries produced by certain compilers that like to align each line table in the .debug_line section. Note that this alignment does not seem to be invalid since the units do point to the correct line table offsets via the DW_AT_stmt_list attribute. Differential Revision: https://reviews.llvm.org/D143513
1 parent dd0bbae commit 8eb464f

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ class DWARFDebugLine {
355355
private:
356356
DWARFUnit *prepareToParse(uint64_t Offset);
357357
void moveToNextTable(uint64_t OldOffset, const Prologue &P);
358+
bool hasValidVersion(uint64_t Offset);
358359

359360
LineToUnitMap LineToUnit;
360361

llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,21 @@ DWARFUnit *DWARFDebugLine::SectionParser::prepareToParse(uint64_t Offset) {
15051505
return U;
15061506
}
15071507

1508+
bool DWARFDebugLine::SectionParser::hasValidVersion(uint64_t Offset) {
1509+
DataExtractor::Cursor Cursor(Offset);
1510+
auto [TotalLength, _] = DebugLineData.getInitialLength(Cursor);
1511+
DWARFDataExtractor HeaderData(DebugLineData, Cursor.tell() + TotalLength);
1512+
uint16_t Version = HeaderData.getU16(Cursor);
1513+
if (!Cursor) {
1514+
// Ignore any error here.
1515+
// If this is not the end of the section parseNext() will still be
1516+
// attempted, where this error will occur again (and can be handled).
1517+
consumeError(Cursor.takeError());
1518+
return false;
1519+
}
1520+
return versionIsSupported(Version);
1521+
}
1522+
15081523
void DWARFDebugLine::SectionParser::moveToNextTable(uint64_t OldOffset,
15091524
const Prologue &P) {
15101525
// If the length field is not valid, we don't know where the next table is, so
@@ -1518,5 +1533,29 @@ void DWARFDebugLine::SectionParser::moveToNextTable(uint64_t OldOffset,
15181533
Offset = OldOffset + P.TotalLength + P.sizeofTotalLength();
15191534
if (!DebugLineData.isValidOffset(Offset)) {
15201535
Done = true;
1536+
return;
1537+
}
1538+
1539+
// Heuristic: If the version is valid, then this is probably a line table.
1540+
// Otherwise, the offset might need alignment (to a 4 or 8 byte boundary).
1541+
if (hasValidVersion(Offset))
1542+
return;
1543+
1544+
// ARM C/C++ Compiler aligns each line table to word boundaries and pads out
1545+
// the .debug_line section to a word multiple. Note that in the specification
1546+
// this does not seem forbidden since each unit has a DW_AT_stmt_list.
1547+
for (unsigned Align : {4, 8}) {
1548+
uint64_t AlignedOffset = alignTo(Offset, Align);
1549+
if (!DebugLineData.isValidOffset(AlignedOffset)) {
1550+
// This is almost certainly not another line table but some alignment
1551+
// padding. This assumes the alignments tested are ordered, and are
1552+
// smaller than the header size (which is true for 4 and 8).
1553+
Done = true;
1554+
return;
1555+
}
1556+
if (hasValidVersion(AlignedOffset)) {
1557+
Offset = AlignedOffset;
1558+
break;
1559+
}
15211560
}
15221561
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// RUN: llvm-mc %s -defsym ALIGN_4=1 -save-temp-labels -filetype obj -triple arm-none-eabi -o %t.o
2+
// RUN: llvm-nm %t.o | FileCheck %s --check-prefix=L4
3+
// RUN: llvm-dwarfdump -debug-line %t.o 2>&1 | FileCheck %s --implicit-check-not='warning:' --check-prefix=MULT4
4+
5+
// RUN: llvm-mc %s -defsym ALIGN_8=1 -save-temp-labels -filetype obj -triple arm-none-eabi -o %t.o
6+
// RUN: llvm-nm %t.o | FileCheck %s --check-prefix=L8
7+
// RUN: llvm-dwarfdump -debug-line %t.o 2>&1 | FileCheck %s --implicit-check-not='warning:' --check-prefix=MULT8
8+
9+
// RUN: llvm-mc %s -defsym UNALIGNED_PADDING=1 -save-temp-labels -filetype obj -triple arm-none-eabi -o %t.o
10+
// RUN: llvm-nm %t.o | FileCheck %s --check-prefix=LUNALIGN
11+
// RUN: llvm-dwarfdump -debug-line %t.o 2>&1 | FileCheck %s --check-prefix=UNALIGN
12+
13+
/// This test is based on a real example from ARM C/C++ Compiler.
14+
/// It verifies llvm-dwarfdump is able to dump line tables even if they've been
15+
/// placed at aligned offsets.
16+
17+
// L4: 0000002b N .Ltable0_end
18+
// MULT4: Address Line Column File ISA Discriminator Flags
19+
// MULT4-NEXT: ------------------ ------ ------ ------ --- ------------- -------------
20+
// MULT4-NEXT: 0x0000000000000000 1 0 1 0 0 is_stmt end_sequence
21+
// MULT4-EMPTY:
22+
// MULT4-NEXT: debug_line[0x0000002c]
23+
// MULT4-NEXT: Line table prologue:
24+
// MULT4-NEXT: total_length: 0x0000003a{{$}}
25+
// MULT4-NEXT: format: DWARF32
26+
// MULT4-NEXT: version: 2{{$}}
27+
// MULT4-NEXT: prologue_length: 0x0000001a
28+
// MULT4-NEXT: min_inst_length: 2
29+
// MULT4-NEXT: default_is_stmt: 1
30+
31+
// L8: 00000027 N .Ltable0_end
32+
// MULT8: Address Line Column File ISA Discriminator Flags
33+
// MULT8-NEXT: ------------------ ------ ------ ------ --- ------------- -------------
34+
// MULT8-NEXT: 0x0000000000000000 1 0 1 0 0 is_stmt end_sequence
35+
// MULT8-EMPTY:
36+
// MULT8-NEXT: debug_line[0x00000028]
37+
// MULT8-NEXT: Line table prologue:
38+
// MULT8-NEXT: total_length: 0x0000003a{{$}}
39+
// MULT8-NEXT: format: DWARF32
40+
// MULT8-NEXT: version: 2{{$}}
41+
// MULT8-NEXT: prologue_length: 0x0000001a
42+
// MULT8-NEXT: min_inst_length: 2
43+
// MULT8-NEXT: default_is_stmt: 1
44+
45+
/// This should fail to dump:
46+
// LUNALIGN: 00000027 N .Ltable0_end
47+
// UNALIGN: warning: parsing line table prologue at offset 0x00000027: unsupported version
48+
49+
.section .debug_line
50+
/// First line table
51+
/// Unit total length:
52+
.long .Ltable0_end - .Ltable0_start
53+
.Ltable0_start:
54+
.short 2 /// Version
55+
/// Header length:
56+
.long .Ltable0_header_end - .Ltable0_header_start
57+
.Ltable0_header_start:
58+
.byte 4 /// Min instruction length
59+
.byte 1 /// Max operations per instruction
60+
.byte 0 /// Default is statement
61+
.byte 6 /// Line range
62+
.byte 10 /// Opcode base
63+
.byte 0 /// standard_opcode_lengths[DW_LNS_copy] = 0
64+
.byte 1 /// standard_opcode_lengths[DW_LNS_advance_pc] = 1
65+
.byte 1 /// standard_opcode_lengths[DW_LNS_advance_line] = 1
66+
.byte 1 /// standard_opcode_lengths[DW_LNS_set_file] = 1
67+
.byte 1 /// standard_opcode_lengths[DW_LNS_set_column] = 1
68+
.byte 0 /// standard_opcode_lengths[DW_LNS_negate_stmt] = 0
69+
.byte 0 /// standard_opcode_lengths[DW_LNS_set_basic_block] = 0
70+
.byte 0 /// standard_opcode_lengths[DW_LNS_const_add_pc] = 0
71+
.byte 0 /// standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 0
72+
.byte 0 /// No include directories
73+
/// File name:
74+
.ifdef ALIGN_4
75+
/// Pad out filename so next 4 byte aligned offset is a multiple of 4 and not 8.
76+
.asciz "foobar.cpp"
77+
.else
78+
.asciz "test.c"
79+
.endif
80+
.byte 0 /// Dir idx
81+
.byte 0 /// Mod time
82+
.byte 0 /// Length
83+
.byte 0 /// End files
84+
.Ltable0_header_end:
85+
/// Line table operations
86+
.byte 0 /// Extended opcode
87+
.byte 1 /// Length 1
88+
.byte 1 /// DW_LNE_end_sequence
89+
.Ltable0_end:
90+
/// End first line table
91+
/// Padding:
92+
.ifdef UNALIGNED_PADDING
93+
.short 0
94+
.else
95+
.byte 0
96+
.endif
97+
/// Second line table
98+
/// Unit total length:
99+
.long .Ltable1_end - .Ltable1_start
100+
.Ltable1_start:
101+
.short 2 /// Version
102+
/// Header length:
103+
.long .Ltable1_header_end - .Ltable1_header_start
104+
.Ltable1_header_start:
105+
.byte 2 /// Min instruction length
106+
.byte 1 /// Max operations per instruction
107+
.byte 0 /// Default is statement
108+
.byte 6 /// Line range
109+
.byte 10 /// Opcode base
110+
.byte 0 /// standard_opcode_lengths[DW_LNS_copy] = 0
111+
.byte 1 /// standard_opcode_lengths[DW_LNS_advance_pc] = 1
112+
.byte 1 /// standard_opcode_lengths[DW_LNS_advance_line] = 1
113+
.byte 1 /// standard_opcode_lengths[DW_LNS_set_file] = 1
114+
.byte 1 /// standard_opcode_lengths[DW_LNS_set_column] = 1
115+
.byte 0 /// standard_opcode_lengths[DW_LNS_negate_stmt] = 0
116+
.byte 0 /// standard_opcode_lengths[DW_LNS_set_basic_block] = 0
117+
.byte 0 /// standard_opcode_lengths[DW_LNS_const_add_pc] = 0
118+
.byte 0 /// standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 0
119+
.byte 0 /// No include directories
120+
.asciz "test.c" /// File name
121+
.byte 0 /// Dir idx
122+
.byte 0 /// Mod time
123+
.byte 0 /// Length
124+
.byte 0 /// End files
125+
.Ltable1_header_end:
126+
/// Line table operations
127+
.byte 4 /// DW_LNS_set_file
128+
.byte 1 /// File 1
129+
.byte 5 /// DW_LNS_set_column
130+
.byte 1 /// Column 1
131+
.byte 0 /// Extended opcode
132+
.byte 5 /// Length 5
133+
.byte 2 /// DW_LNE_set_address
134+
.long 32896 /// Address = 0x00008080
135+
.byte 3 /// DW_LNS_advance_line
136+
.byte 6 /// Line += 6
137+
.byte 1 /// DW_LNS_copy
138+
.byte 5 /// DW_LNS_set_column
139+
.byte 2 /// Column 2
140+
.byte 12 /// Special opcode (address += 0, line += 2)
141+
.byte 30 /// Special opcode (address += 6, line += 2)
142+
.byte 5 /// DW_LNS_set_column
143+
.byte 1 /// Column 1
144+
.byte 17 /// Special opcode (address += 2, line += 1)
145+
.byte 2 /// DW_LNS_advance_pc
146+
.byte 4 /// += (4 * min instruction length)
147+
.byte 0 /// Extended opcode
148+
.byte 1 /// Length 1
149+
.byte 1 /// DW_LNE_end_sequence
150+
.Ltable1_end:
151+
/// End second line table
152+
.short 0 /// Padding (to make section a word multiple)

0 commit comments

Comments
 (0)