diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -355,6 +355,7 @@ private: DWARFUnit *prepareToParse(uint64_t Offset); void moveToNextTable(uint64_t OldOffset, const Prologue &P); + bool hasValidVersion(uint64_t Offset); LineToUnitMap LineToUnit; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -1505,6 +1505,21 @@ return U; } +bool DWARFDebugLine::SectionParser::hasValidVersion(uint64_t Offset) { + DataExtractor::Cursor Cursor(Offset); + auto [TotalLength, _] = DebugLineData.getInitialLength(Cursor); + DWARFDataExtractor HeaderData(DebugLineData, Cursor.tell() + TotalLength); + uint16_t Version = HeaderData.getU16(Cursor); + if (!Cursor) { + // Ignore any error here. + // If this is not the end of the section parseNext() will still be + // attempted, where this error will occur again (and can be handled). + consumeError(Cursor.takeError()); + return false; + } + return versionIsSupported(Version); +} + void DWARFDebugLine::SectionParser::moveToNextTable(uint64_t OldOffset, const Prologue &P) { // If the length field is not valid, we don't know where the next table is, so @@ -1518,5 +1533,30 @@ Offset = OldOffset + P.TotalLength + P.sizeofTotalLength(); if (!DebugLineData.isValidOffset(Offset)) { Done = true; + return; + } + + // Heuristic: If the version is valid, then this is probably a line table, + // otherwise, the offset might need alignment. + if (hasValidVersion(Offset)) + return; + + // Certain compilers align each line table to word boundaries and pad out the + // .debug_line section to a word multiple. Note that in the specification this + // does not seem forbidden since each unit has a DW_AT_stmt_list. + std::array PossibleAlignments{4, 8}; + for (unsigned Align : PossibleAlignments) { + uint64_t AlignedOffset = alignTo(Offset, Align); + if (!DebugLineData.isValidOffset(AlignedOffset)) { + // This is almost certainly not another line table but some alignment + // padding. This assumes the alignments tested are ordered, and are + // smaller than the header size (which is true for 4 and 8). + Done = true; + return; + } + if (hasValidVersion(AlignedOffset)) { + Offset = AlignedOffset; + break; + } } } diff --git a/llvm/test/tools/llvm-dwarfdump/ARM/aligned_line_tables.s b/llvm/test/tools/llvm-dwarfdump/ARM/aligned_line_tables.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/ARM/aligned_line_tables.s @@ -0,0 +1,102 @@ +# RUN: llvm-mc %s -filetype obj -triple arm-none-eabi -o %t.o +# RUN: llvm-dwarfdump -debug-line %t.o | FileCheck %s + +// This test is based on a real example from ARM C/C++ Compiler. +// It verifies llvm-dwarfdump is able to dump line tables even if they've been +// placed at aligned offsets. + +# CHECK: Address Line Column File ISA Discriminator Flags +# CHECK: ------------------ ------ ------ ------ --- ------------- ------------- +# CHECK: 0x0000000000000000 1 0 1 0 0 is_stmt end_sequence +# CHECK-EMPTY: +# CHECK: debug_line[0x00000028] +# CHECK: Line table prologue: +# CHECK: total_length: 0x0000003a{{$}} +# CHECK: format: DWARF32 +# CHECK: version: 2{{$}} +# CHECK: prologue_length: 0x0000001a +# CHECK: min_inst_length: 2 +# CHECK-NOT: warning: +# CHECK: default_is_stmt: 1 + +.section .debug_line +// First line table +.long 35 // Unit total length +.short 2 // Version +.long 26 // Header length +.byte 4 // Min instruction length +.byte 1 // Max operations per instruction +.byte 0 // Default is statement +.byte 6 // Line range +.byte 10 // Opcode base +.byte 0 // standard_opcode_lengths[DW_LNS_copy] = 0 +.byte 1 // standard_opcode_lengths[DW_LNS_advance_pc] = 1 +.byte 1 // standard_opcode_lengths[DW_LNS_advance_line] = 1 +.byte 1 // standard_opcode_lengths[DW_LNS_set_file] = 1 +.byte 1 // standard_opcode_lengths[DW_LNS_set_column] = 1 +.byte 0 // standard_opcode_lengths[DW_LNS_negate_stmt] = 0 +.byte 0 // standard_opcode_lengths[DW_LNS_set_basic_block] = 0 +.byte 0 // standard_opcode_lengths[DW_LNS_const_add_pc] = 0 +.byte 0 // standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 0 +.byte 0 // No include directories +.asciz "test.c" // File name +.byte 0 // Dir idx +.byte 0 // Mod time +.byte 0 // Length +.byte 0 // End files +// Line table operations +.byte 0 // Extended opcode +.byte 1 // Length 1 +.byte 1 // DW_LNE_end_sequence +// End first line table +.byte 0 // Padding byte (to align table to 4 byte boundary) +// Next line table +.long 58 // Unit total length +.short 2 // Version +.long 26 // Header length +.byte 2 // Min instruction length +.byte 1 // Max operations per instruction +.byte 0 // Default is statement +.byte 6 // Line range +.byte 10 // Opcode base +.byte 0 // standard_opcode_lengths[DW_LNS_copy] = 0 +.byte 1 // standard_opcode_lengths[DW_LNS_advance_pc] = 1 +.byte 1 // standard_opcode_lengths[DW_LNS_advance_line] = 1 +.byte 1 // standard_opcode_lengths[DW_LNS_set_file] = 1 +.byte 1 // standard_opcode_lengths[DW_LNS_set_column] = 1 +.byte 0 // standard_opcode_lengths[DW_LNS_negate_stmt] = 0 +.byte 0 // standard_opcode_lengths[DW_LNS_set_basic_block] = 0 +.byte 0 // standard_opcode_lengths[DW_LNS_const_add_pc] = 0 +.byte 0 // standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 0 +.byte 0 // No include directories +.asciz "test.c" // File name +.byte 0 // Dir idx +.byte 0 // Mod time +.byte 0 // Length +.byte 0 // End files +// Line table operations +.byte 4 // DW_LNS_set_file +.byte 1 // File 1 +.byte 5 // DW_LNS_set_column +.byte 1 // Column 1 +.byte 0 // Extended opcode +.byte 5 // Length 5 +.byte 2 // DW_LNE_set_address +.long 32896 // Address = 0x00008080 +.byte 3 // DW_LNS_advance_line +.byte 6 // Line += 6 +.byte 1 // DW_LNS_copy +.byte 5 // DW_LNS_set_column +.byte 2 // Column 2 +.byte 12 // Special opcode (address += 0, line += 2) +.byte 30 // Special opcode (address += 6, line += 2) +.byte 5 // DW_LNS_set_column +.byte 1 // Column 1 +.byte 17 // Special opcode (address += 2, line += 1) +.byte 2 // DW_LNS_advance_pc +.byte 4 // += (4 * min instruction length) +.byte 0 // Extended opcode +.byte 1 // Length 1 +.byte 1 // DW_LNE_end_sequence +// End second line table +.short 0 // Padding (to make section a word multiple)