Skip to content

Commit b94191f

Browse files
committed
[DebugInfo] Make most debug line prologue errors non-fatal to parsing
Many of the debug line prologue errors are not inherently fatal. In most cases, we can make reasonable assumptions and carry on. This patch does exactly that. In the case of length problems, the approach of "the claimed length is correct" is taken to be consistent with other instances such as the SectionParser, which ignores the read length. Reviewed by: dblaikie Differential Revision: https://reviews.llvm.org/D72158
1 parent dea1147 commit b94191f

File tree

6 files changed

+177
-114
lines changed

6 files changed

+177
-114
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class DWARFDebugLine {
138138
void clear();
139139
void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const;
140140
Error parse(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr,
141+
function_ref<void(Error)> RecoverableErrorCallback,
141142
const DWARFContext &Ctx, const DWARFUnit *U = nullptr);
142143
};
143144

@@ -341,9 +342,12 @@ class DWARFDebugLine {
341342
/// Skip the current line table and go to the following line table (if
342343
/// present) immediately.
343344
///
344-
/// \param ErrorCallback - report any prologue parsing issues via this
345-
/// callback.
346-
void skip(function_ref<void(Error)> ErrorCallback);
345+
/// \param RecoverableErrorCallback - report any recoverable prologue
346+
/// parsing issues via this callback.
347+
/// \param UnrecoverableErrorCallback - report any unrecoverable prologue
348+
/// parsing issues via this callback.
349+
void skip(function_ref<void(Error)> RecoverableErrorCallback,
350+
function_ref<void(Error)> UnrecoverableErrorCallback);
347351

348352
/// Indicates if the parser has parsed as much as possible.
349353
///

llvm/lib/DebugInfo/DWARF/DWARFContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ void DWARFContext::dump(
467467
Optional<uint64_t> DumpOffset) {
468468
while (!Parser.done()) {
469469
if (DumpOffset && Parser.getOffset() != *DumpOffset) {
470-
Parser.skip(dumpWarning);
470+
Parser.skip(dumpWarning, dumpWarning);
471471
continue;
472472
}
473473
OS << "debug_line[" << format("0x%8.8" PRIx64, Parser.getOffset())

llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,10 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData,
299299
return Error::success();
300300
}
301301

302-
Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
303-
uint64_t *OffsetPtr,
304-
const DWARFContext &Ctx,
305-
const DWARFUnit *U) {
302+
Error DWARFDebugLine::Prologue::parse(
303+
const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr,
304+
function_ref<void(Error)> RecoverableErrorCallback, const DWARFContext &Ctx,
305+
const DWARFUnit *U) {
306306
const uint64_t PrologueOffset = *OffsetPtr;
307307

308308
clear();
@@ -311,13 +311,18 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
311311
FormParams.Format = dwarf::DWARF64;
312312
TotalLength = DebugLineData.getU64(OffsetPtr);
313313
} else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) {
314+
// Treat this error as unrecoverable - we have no way of knowing where the
315+
// table ends.
314316
return createStringError(errc::invalid_argument,
315317
"parsing line table prologue at offset 0x%8.8" PRIx64
316318
" unsupported reserved unit length found of value 0x%8.8" PRIx64,
317319
PrologueOffset, TotalLength);
318320
}
319321
FormParams.Version = DebugLineData.getU16(OffsetPtr);
320322
if (getVersion() < 2)
323+
// Treat this error as unrecoverable - we cannot be sure what any of
324+
// the data represents including the length field, so cannot skip it or make
325+
// any reasonable assumptions.
321326
return createStringError(errc::not_supported,
322327
"parsing line table prologue at offset 0x%8.8" PRIx64
323328
" found unsupported version 0x%2.2" PRIx16,
@@ -352,25 +357,32 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
352357
if (Error E =
353358
parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U,
354359
ContentTypes, IncludeDirectories, FileNames)) {
355-
return joinErrors(
360+
RecoverableErrorCallback(joinErrors(
356361
createStringError(
357362
errc::invalid_argument,
358363
"parsing line table prologue at 0x%8.8" PRIx64
359364
" found an invalid directory or file table description at"
360365
" 0x%8.8" PRIx64,
361366
PrologueOffset, *OffsetPtr),
362-
std::move(E));
367+
std::move(E)));
368+
// Skip to the end of the prologue, since the chances are that the parser
369+
// did not read the whole table. This prevents the length check below from
370+
// executing.
371+
if (*OffsetPtr < EndPrologueOffset)
372+
*OffsetPtr = EndPrologueOffset;
363373
}
364374
} else
365375
parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
366376
ContentTypes, IncludeDirectories, FileNames);
367377

368-
if (*OffsetPtr != EndPrologueOffset)
369-
return createStringError(errc::invalid_argument,
370-
"parsing line table prologue at 0x%8.8" PRIx64
371-
" should have ended at 0x%8.8" PRIx64
372-
" but it ended at 0x%8.8" PRIx64,
373-
PrologueOffset, EndPrologueOffset, *OffsetPtr);
378+
if (*OffsetPtr != EndPrologueOffset) {
379+
RecoverableErrorCallback(createStringError(
380+
errc::invalid_argument,
381+
"parsing line table prologue at 0x%8.8" PRIx64
382+
" should have ended at 0x%8.8" PRIx64 " but it ended at 0x%8.8" PRIx64,
383+
PrologueOffset, EndPrologueOffset, *OffsetPtr));
384+
*OffsetPtr = EndPrologueOffset;
385+
}
374386
return Error::success();
375387
}
376388

@@ -516,7 +528,8 @@ Error DWARFDebugLine::LineTable::parse(
516528

517529
clear();
518530

519-
Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U);
531+
Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr,
532+
RecoverableErrorCallback, Ctx, U);
520533

521534
if (OS) {
522535
// The presence of OS signals verbose dumping.
@@ -1158,14 +1171,16 @@ DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext(
11581171
}
11591172

11601173
void DWARFDebugLine::SectionParser::skip(
1161-
function_ref<void(Error)> ErrorCallback) {
1174+
function_ref<void(Error)> RecoverableErrorCallback,
1175+
function_ref<void(Error)> UnrecoverableErrorCallback) {
11621176
assert(DebugLineData.isValidOffset(Offset) &&
11631177
"parsing should have terminated");
11641178
DWARFUnit *U = prepareToParse(Offset);
11651179
uint64_t OldOffset = Offset;
11661180
LineTable LT;
1167-
if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U))
1168-
ErrorCallback(std::move(Err));
1181+
if (Error Err = LT.Prologue.parse(DebugLineData, &Offset,
1182+
RecoverableErrorCallback, Context, U))
1183+
UnrecoverableErrorCallback(std::move(Err));
11691184
moveToNextTable(OldOffset, LT.Prologue);
11701185
}
11711186

llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
.long .Lunit_short_prologue_end - .Lunit_short_prologue_start # unit length
6565
.Lunit_short_prologue_start:
6666
.short 4 # version
67-
.long .Lprologue_short_prologue_end-.Lprologue_short_prologue_start - 2 # Length of Prologue
67+
.long .Lprologue_short_prologue_end-.Lprologue_short_prologue_start # Length of Prologue
6868
.Lprologue_short_prologue_start:
6969
.byte 1 # Minimum Instruction Length
7070
.byte 1 # Maximum Operations per Instruction
@@ -79,9 +79,13 @@
7979
.asciz "file1" # File table
8080
.byte 0, 0, 0
8181
.asciz "file2"
82-
.byte 1, 2, 3
83-
.byte 0
82+
.byte 1, 2
8483
.Lprologue_short_prologue_end:
84+
.byte 6 # Read as part of the prologue,
85+
# then later again as DW_LNS_negate_stmt.
86+
# FIXME: There should be an additional 0 byte here, but the file name parsing
87+
# code does not recognise a missing null terminator.
88+
# Header end
8589
.byte 0, 9, 2 # DW_LNE_set_address
8690
.quad 0x1122334455667788
8791
.byte 0, 1, 1 # DW_LNE_end_sequence
@@ -91,7 +95,7 @@
9195
.long .Lunit_long_prologue_end - .Lunit_long_prologue_start # unit length
9296
.Lunit_long_prologue_start:
9397
.short 4 # version
94-
.long .Lprologue_long_prologue_end-.Lprologue_long_prologue_start + 1 # Length of Prologue
98+
.long .Lprologue_long_prologue_end-.Lprologue_long_prologue_start # Length of Prologue
9599
.Lprologue_long_prologue_start:
96100
.byte 1 # Minimum Instruction Length
97101
.byte 1 # Maximum Operations per Instruction
@@ -108,6 +112,8 @@
108112
.asciz "file2"
109113
.byte 1, 2, 3
110114
.byte 0
115+
# Skipped byte (treated as part of prologue).
116+
.byte 6
111117
.Lprologue_long_prologue_end:
112118
.byte 0, 9, 2 # DW_LNE_set_address
113119
.quad 0x1111222233334444
@@ -180,34 +186,35 @@
180186
.short 5 # DWARF version number
181187
.byte 8 # Address Size
182188
.byte 0 # Segment Selector Size
183-
.long 15 # Length of Prologue (invalid)
189+
.long .Linvalid_description_header_end0 - .Linvalid_description_params0 # Length of Prologue (invalid)
184190
.Linvalid_description_params0:
185191
.byte 1 # Minimum Instruction Length
186192
.byte 1 # Maximum Operations per Instruction
187193
.byte 1 # Default is_stmt
188194
.byte -5 # Line Base
189195
.byte 14 # Line Range
190196
.byte 13 # Opcode Base
191-
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
197+
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0 # Standard Opcode Lengths
198+
.Linvalid_description_header_end0:
199+
# The bytes from here onwards will also be read as part of the main body.
200+
# --- Prologue interpretation --- | --- Main body interpretation ---
201+
.byte 0, 1 # More standard opcodes | First part of DW_LNE_end_sequence
192202
# Directory table format
193-
.byte 1 # One element per directory entry
194-
.byte 1 # DW_LNCT_path
195-
.byte 0x08 # DW_FORM_string
203+
.byte 1 # One element per directory entry | End of DW_LNE_end_sequence
204+
.byte 1 # DW_LNCT_path | DW_LNS_copy
205+
.byte 0x08 # DW_FORM_string | DW_LNS_const_add_pc
196206
# Directory table entries
197-
.byte 1 # 1 directory
198-
.asciz "/tmp"
207+
.byte 1 # 1 directory | DW_LNS_copy
208+
.asciz "/tmp" # Directory name | four special opcodes + start of DW_LNE_end_sequence
199209
# File table format
200-
.byte 2 # 2 elements per file entry
201-
.byte 1 # DW_LNCT_path
202-
.byte 0x08 # DW_FORM_string
203-
.byte 2 # DW_LNCT_directory_index
204-
.byte 0x0b # DW_FORM_data1
210+
.byte 1 # 1 element per file entry | DW_LNE_end_sequence length
211+
.byte 1 # DW_LNCT_path | DW_LNE_end_sequence opcode
212+
.byte 0x08 # DW_FORM_string | DW_LNS_const_add_pc
205213
# File table entries
206-
.byte 1 # 1 file
207-
.asciz "a.c"
208-
.byte 1
209-
.Linvalid_description_header_end0:
210-
.byte 0, 9, 2 # DW_LNE_set_address
214+
.byte 1 # 1 file | DW_LNS_copy
215+
.asciz "xyz" # File name | three special opcodes + start of DW_LNE_set_address
216+
# Header end
217+
.byte 9, 2 # Remainder of DW_LNE_set_address
211218
.quad 0xbabb1ebabb1e
212219
.byte 0, 1, 1 # DW_LNE_end_sequence
213220
.Linvalid_description_end0:
@@ -218,7 +225,7 @@
218225
.short 5 # DWARF version number
219226
.byte 8 # Address Size
220227
.byte 0 # Segment Selector Size
221-
.long .Linvalid_file_header_end0-.Linvalid_file_params0-7 # Length of Prologue (invalid)
228+
.long .Linvalid_file_header_end0 - .Linvalid_file_params0 # Length of Prologue (invalid)
222229
.Linvalid_file_params0:
223230
.byte 1 # Minimum Instruction Length
224231
.byte 1 # Maximum Operations per Instruction
@@ -239,12 +246,16 @@
239246
.byte 1 # DW_LNCT_path
240247
.byte 0x08 # DW_FORM_string
241248
.byte 2 # DW_LNCT_directory_index
242-
.byte 0x0b # DW_FORM_data1
243-
# File table entries
244-
.byte 1 # 1 file
245-
.asciz "a.c"
246-
.byte 1
247249
.Linvalid_file_header_end0:
250+
# The bytes from here onwards will also be read as part of the main body.
251+
# --- Prologue interpretation --- | --- Main body interpretation ---
252+
.byte 0x0b # DW_FORM_data1 | DW_LNS_set_epilogue_begin
253+
# File table entries
254+
.byte 1 # 1 file | DW_LNS_copy
255+
.asciz "xyz" # File name | 3 special opcodes + start of DW_LNE_end_sequence
256+
.byte 1 # Dir index | DW_LNE_end_sequence length
257+
# Header end
258+
.byte 1 # DW_LNE_end_sequence opcode
248259
.byte 0, 9, 2 # DW_LNE_set_address
249260
.quad 0xab4acadab4a
250261
.byte 0, 1, 1 # DW_LNE_end_sequence
@@ -256,7 +267,7 @@
256267
.short 5 # DWARF version number
257268
.byte 8 # Address Size
258269
.byte 0 # Segment Selector Size
259-
.long .Linvalid_dir_header_end0-.Linvalid_dir_params0-16 # Length of Prologue (invalid)
270+
.long .Linvalid_dir_header_end0 - .Linvalid_dir_params0 # Length of Prologue (invalid)
260271
.Linvalid_dir_params0:
261272
.byte 1 # Minimum Instruction Length
262273
.byte 1 # Maximum Operations per Instruction
@@ -271,19 +282,19 @@
271282
.byte 0x08 # DW_FORM_string
272283
# Directory table entries
273284
.byte 1 # 1 directory
274-
.asciz "/tmp"
285+
.Linvalid_dir_header_end0:
286+
# The bytes from here onwards will also be read as part of the main body.
287+
# --- Prologue interpretation --- | --- Main body interpretation ---
288+
.asciz "/tmp" # Directory name | 4 special opcodes + start of DW_LNE_end_sequence
275289
# File table format
276-
.byte 2 # 2 elements per file entry
277-
.byte 1 # DW_LNCT_path
278-
.byte 0x08 # DW_FORM_string
279-
.byte 2 # DW_LNCT_directory_index
280-
.byte 0x0b # DW_FORM_data1
290+
.byte 1 # 1 element per file entry | DW_LNE_end_sequence length
291+
.byte 1 # DW_LNCT_path | DW_LNE_end_sequence length opcode
292+
.byte 0x08 # DW_FORM_string | DW_LNS_const_add_pc
281293
# File table entries
282-
.byte 1 # 1 file
283-
.asciz "a.c"
284-
.byte 1
285-
.Linvalid_dir_header_end0:
286-
.byte 0, 9, 2 # DW_LNE_set_address
294+
.byte 1 # 1 file | DW_LNS_copy
295+
.asciz "xyz" # File name | start of DW_LNE_set_address
296+
# Header end
297+
.byte 9, 2 # DW_LNE_set_address length + opcode
287298
.quad 0x4444333322221111
288299
.byte 0, 1, 1 # DW_LNE_end_sequence
289300
.Linvalid_dir_end0:
@@ -323,7 +334,7 @@
323334
.asciz "a.c"
324335
.byte 0
325336
# Data to show that the rest of the prologue is skipped.
326-
.byte 6
337+
.byte 1
327338
.Linvalid_md5_header_end0:
328339
.byte 0, 9, 2 # DW_LNE_set_address
329340
.quad 0x1234123412341234
@@ -337,7 +348,7 @@
337348
.short 5 # DWARF version number
338349
.byte 8 # Address Size
339350
.byte 0 # Segment Selector Size
340-
.long .Linvalid_md5_header_end1-.Linvalid_md5_params1 - 10 # Length of Prologue
351+
.long .Linvalid_md5_header_end1 - .Linvalid_md5_params1 # Length of Prologue
341352
.Linvalid_md5_params1:
342353
.byte 1 # Minimum Instruction Length
343354
.byte 1 # Maximum Operations per Instruction
@@ -354,20 +365,20 @@
354365
.byte 1 # 1 directory
355366
.asciz "/tmp"
356367
# File table format
357-
.byte 3 # 2 elements per file entry
368+
.byte 2 # 2 elements per file entry
358369
.byte 1 # DW_LNCT_path
359370
.byte 0x08 # DW_FORM_string
360371
.byte 5 # DW_LNCT_MD5
361-
.byte 0x0b # DW_FORM_data1
362-
.byte 2 # DW_LNCT_directory_index
363-
.byte 0x0b # DW_FORM_data1
364-
# File table entries
365-
.byte 1 # 1 file
366-
.asciz "a.c"
367-
.byte 6 # This byte will be consumed when reading the MD5 value.
368-
.byte 0xb # This byte will not be read as part of the prologue.
369372
.Linvalid_md5_header_end1:
370-
.byte 0, 9, 2 # DW_LNE_set_address
373+
# The bytes from here onwards will also be read as part of the main body.
374+
# --- Prologue interpretation --- | --- Main body interpretation ---
375+
.byte 0x0b # DW_FORM_data1 | DW_LNS_set_epilogue_begin
376+
# File table entries
377+
.byte 1 # 1 file | DW_LNS_copy
378+
.asciz "xyz" # File name | 3 special opcodes + DW_LNE_set_address start
379+
.byte 9 # MD5 hash value | DW_LNE_set_address length
380+
# Header end
381+
.byte 2 # DW_LNE_set_address opcode
371382
.quad 0x4321432143214321
372383
.byte 0, 1, 1 # DW_LNE_end_sequence
373384
.Linvalid_md5_end1:

0 commit comments

Comments
 (0)