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 @@ -685,13 +685,18 @@ (*OffsetPtr) += Len - 1; break; } - // Make sure the stated and parsed lengths are the same. - // Otherwise we have an unparseable line-number program. - if (*OffsetPtr - ExtOffset != Len) - return createStringError(errc::illegal_byte_sequence, - "unexpected line op length at offset 0x%8.8" PRIx64 - " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, - ExtOffset, Len, *OffsetPtr - ExtOffset); + // Make sure the length as recorded in the table and the standard length + // for the opcode match. If they don't, continue from the end as claimed + // by the table. + uint64_t End = ExtOffset + Len; + if (*OffsetPtr != End) { + RecoverableErrorCallback(createStringError( + errc::illegal_byte_sequence, + "unexpected line op length at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, + ExtOffset, Len, *OffsetPtr - ExtOffset)); + *OffsetPtr = End; + } } else if (Opcode < Prologue.OpcodeBase) { if (OS) *OS << LNStandardString(Opcode); diff --git a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s --- a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s @@ -114,7 +114,7 @@ .byte 0, 1, 1 # DW_LNE_end_sequence .Lunit_long_prologue_end: -# Over-long extended opcode. +# Incorrect length extended opcodes. .long .Lunit_long_opcode_end - .Lunit_long_opcode_start # unit length .Lunit_long_opcode_start: .short 4 # version @@ -139,6 +139,9 @@ .byte 0, 9, 2 # DW_LNE_set_address .quad 0xabbadaba .byte 0, 2, 1 # DW_LNE_end_sequence (too long) +.byte 6 # DW_LNS_negate_stmt (but will be consumed with the end sequence above). +.byte 0, 1, 4 # DW_LNE_set_discriminator (too short) +.byte 0xa # Parsed as argument for set_discriminator and also DW_LNS_set_prologue_end. .byte 0, 9, 2 # DW_LNE_set_address .quad 0xbabb1e45 .byte 0, 1, 1 # DW_LNE_end_sequence diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -36,7 +36,7 @@ # RUN: FileCheck %s --input-file=%t-malformed-off-first.err --check-prefix=ALL ## Don't stop looking for the later unit if non-fatal issues are found. -# RUN: llvm-dwarfdump -debug-line=0x2af %t-malformed.o 2> %t-malformed-off-last.err \ +# RUN: llvm-dwarfdump -debug-line=0x2b4 %t-malformed.o 2> %t-malformed-off-last.err \ # RUN: | FileCheck %s --check-prefix=LAST --implicit-check-not='debug_line[{{.*}}]' # RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL @@ -96,21 +96,18 @@ ## Case 6: Extended opcode with incorrect length versus expected. # NONFATAL: debug_line[0x00000111] -## Dumping prints the line table prologue and any valid operations up to the -## point causing the problem. # NONFATAL-NEXT: Line table prologue -# NONFATAL: 0x00000000abbadaba {{.*}} end_sequence -# NONFATAL-NOT: is_stmt +# NONFATAL: 0x00000000abbadaba {{.*}} end_sequence +# NONFATAL: 0x00000000babb1e45 {{.*}} 10 is_stmt prologue_end end_sequence{{$}} -## For minor issues, we can dump the whole table. ## Case 7: No end of sequence. -# NONFATAL: debug_line[0x00000167] +# NONFATAL: debug_line[0x0000016c] # NONFATAL-NEXT: Line table prologue # NONFATAL: 0x00000000deadfade {{.*}} is_stmt # NONFATAL-NOT: end_sequence ## Case 8: Very short prologue length for V5 (ends during parameters). -# NONFATAL: debug_line[0x000001ad] +# NONFATAL: debug_line[0x000001b2] # NONFATAL-NEXT: Line table prologue # NONFATAL: standard_opcode_lengths[DW_LNS_set_isa] = 1 # NONFATAL-NEXT: include_directories[ 0] = "/tmp" @@ -120,7 +117,7 @@ # NONFATAL-NOT: Address ## Case 9: V5 prologue ends during file table. -# NONFATAL: debug_line[0x000001ed] +# NONFATAL: debug_line[0x000001f2] # NONFATAL-NEXT: Line table prologue # NONFATAL: include_directories[ 0] = "/tmp" # NONFATAL-NEXT: file_names[ 0]: @@ -129,7 +126,7 @@ # NONFATAL-NOT: Address ## Case 10: V5 prologue ends during directory table. -# NONFATAL: debug_line[0x0000022d] +# NONFATAL: debug_line[0x00000232] # NONFATAL-NEXT: Line table prologue # NONFATAL: include_directories[ 0] = "/tmp" # NONFATAL-NEXT: file_names[ 0]: @@ -138,13 +135,13 @@ # NONFATAL-NOT: Address ## Case 11: V5 invalid MD5 hash form. -# NONFATAL: debug_line[0x0000026d] +# NONFATAL: debug_line[0x00000272] # NONFATAL-NEXT: Line table prologue # NONFATAL: include_directories[ 0] = "/tmp" # NONFATAL-NOT: file_names # NONFATAL-NOT: Address -# LAST: debug_line[0x000002af] +# LAST: debug_line[0x000002b4] # LAST: 0x00000000cafebabe {{.*}} end_sequence # RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe @@ -158,10 +155,11 @@ # ALL-NEXT: warning: parsing line table prologue at 0x00000081 should have ended at 0x000000b9 but it ended at 0x000000ba # ALL-NEXT: warning: parsing line table prologue at 0x000000c9 should have ended at 0x00000104 but it ended at 0x00000103 # OTHER-NEXT: warning: unexpected line op length at offset 0x00000158 expected 0x02 found 0x01 -# OTHER-NEXT: warning: last sequence in debug line table at offset 0x00000167 is not terminated -# ALL-NEXT: warning: parsing line table prologue at 0x000001ad should have ended at 0x000001c8 but it ended at 0x000001df -# ALL-NEXT: warning: parsing line table prologue at 0x000001ed should have ended at 0x00000218 but it ended at 0x0000021f -# ALL-NEXT: warning: parsing line table prologue at 0x0000022d should have ended at 0x0000024f but it ended at 0x0000025f -# ALL-NEXT: warning: parsing line table prologue at 0x0000026d found an invalid directory or file table description at 0x000002a2 +# OTHER-NEXT: warning: unexpected line op length at offset 0x0000015c expected 0x01 found 0x02 +# OTHER-NEXT: warning: last sequence in debug line table at offset 0x0000016c is not terminated +# ALL-NEXT: warning: parsing line table prologue at 0x000001b2 should have ended at 0x000001cd but it ended at 0x000001e4 +# ALL-NEXT: warning: parsing line table prologue at 0x000001f2 should have ended at 0x0000021d but it ended at 0x00000224 +# ALL-NEXT: warning: parsing line table prologue at 0x00000232 should have ended at 0x00000254 but it ended at 0x00000264 +# ALL-NEXT: warning: parsing line table prologue at 0x00000272 found an invalid directory or file table description at 0x000002a7 # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid # ALL-NOT: warning: diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -428,19 +428,62 @@ std::make_pair(4, DWARF64), // Test v4 fields and DWARF64. std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)), ); -TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) { +TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { if (!setupGenerator()) return; LineTable < = Gen->addLineTable(); + LT.addByte(0xaa); + // The Length should be 1 + sizeof(ULEB) for a set discriminator opcode. + // The operand will be read for both the discriminator opcode and then parsed + // again as DW_LNS_negate_stmt, to respect the claimed length. + LT.addExtendedOpcode(1, DW_LNE_set_discriminator, + {{DW_LNS_negate_stmt, LineTable::ULEB}}); + LT.addByte(0xbb); + LT.addStandardOpcode(DW_LNS_const_add_pc, {}); + LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + + generate(); + + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + checkError( + "unexpected line op length at offset 0x00000031 expected 0x01 found 0x02", + std::move(Recoverable)); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u); + EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); + EXPECT_EQ((*ExpectedLineTable)->Rows[1].IsStmt, 0u); + EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt); +} + +TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { + if (!setupGenerator()) + return; + + LineTable < = Gen->addLineTable(); + LT.addByte(0xaa); + LT.addStandardOpcode(DW_LNS_const_add_pc, {}); // The Length should be 1 for an end sequence opcode. LT.addExtendedOpcode(2, DW_LNE_end_sequence, {}); + // The negate statement opcode will be skipped. + LT.addStandardOpcode(DW_LNS_negate_stmt, {}); + LT.addByte(0xbb); + LT.addStandardOpcode(DW_LNS_const_add_pc, {}); + LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); generate(); - checkGetOrParseLineTableEmitsFatalError( - "unexpected line op length at offset " - "0x00000030 expected 0x02 found 0x01"); + auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, + nullptr, RecordRecoverable); + checkError( + "unexpected line op length at offset 0x00000032 expected 0x02 found 0x01", + std::move(Recoverable)); + ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); + ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 4u); + EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 2u); + ASSERT_EQ((*ExpectedLineTable)->Sequences[1].FirstRowIndex, 2u); + EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u); } TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) { @@ -695,11 +738,11 @@ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); Parser.parseNext(RecordRecoverable, RecordUnrecoverable); - EXPECT_FALSE(Recoverable); + EXPECT_FALSE(Unrecoverable); ASSERT_FALSE(Parser.done()); checkError( "unexpected line op length at offset 0x00000030 expected 0x42 found 0x01", - std::move(Unrecoverable)); + std::move(Recoverable)); // Reset the error state so that it does not confuse the next set of checks. Unrecoverable = Error::success();