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 @@ -355,10 +355,21 @@ LineRange = DebugLineData.getU8(OffsetPtr); OpcodeBase = DebugLineData.getU8(OffsetPtr); - StandardOpcodeLengths.reserve(OpcodeBase - 1); - for (uint32_t I = 1; I < OpcodeBase; ++I) { - uint8_t OpLen = DebugLineData.getU8(OffsetPtr); - StandardOpcodeLengths.push_back(OpLen); + if (OpcodeBase == 0) { + // If the opcode base is 0, we cannot read the standard opcode lengths (of + // which there are supposed to be one fewer than the opcode base). Assume + // there are no standard opcodes and continue parsing. + RecoverableErrorCallback(createStringError( + errc::invalid_argument, + "parsing line table prologue at offset 0x%8.8" PRIx64 + " found opcode base of 0. Assuming no standard opcodes", + PrologueOffset)); + } else { + StandardOpcodeLengths.reserve(OpcodeBase - 1); + for (uint32_t I = 1; I < OpcodeBase; ++I) { + uint8_t OpLen = DebugLineData.getU8(OffsetPtr); + StandardOpcodeLengths.push_back(OpLen); + } } if (getVersion() >= 5) { 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 @@ -383,6 +383,33 @@ .byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_md5_end1: +# Zero opcode base. +.long .Lzero_opcode_base_end - .Lzero_opcode_base_start # unit length +.Lzero_opcode_base_start: +.short 4 # version +.long .Lzero_opcode_base_prologue_end-.Lzero_opcode_base_prologue_start # Length of Prologue +.Lzero_opcode_base_prologue_start: +.byte 1 # Minimum Instruction Length +.byte 1 # Maximum Operations per Instruction +.byte 1 # Default is_stmt +.byte 0 # Line Base +.byte 1 # Line Range +.byte 0 # Opcode Base +.asciz "dir1" # Include table +.asciz "dir2" +.byte 0 +.asciz "file1" # File table +.byte 0, 0, 0 +.asciz "file2" +.byte 1, 2, 3 +.byte 0 +.Lzero_opcode_base_prologue_end: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0xffffeeeeddddcccc +.byte 0x1 # Special opcode +.byte 0, 1, 1 # DW_LNE_end_sequence +.Lzero_opcode_base_end: + # Trailing good section. .long .Lunit_good_end - .Lunit_good_start # Length of Unit (DWARF-32 format) .Lunit_good_start: 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=0x2ec %t-malformed.o 2> %t-malformed-off-last.err \ +# RUN: llvm-dwarfdump -debug-line=0x329 %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 @@ -150,7 +150,25 @@ # NONFATAL: 0x0000000000000000 {{.*}} epilogue_begin # NONFATAL: 0x4321432143214321 {{.*}} is_stmt end_sequence -# LAST: debug_line[0x000002ec] +## Case 13: Opcode base field of value zero. +# NONFATAL: debug_line[0x000002ec] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: include_directories[ 1] = "dir1" +# NONFATAL: include_directories[ 2] = "dir2" +# NONFATAL: file_names[ 1]: +# NONFATAL-NEXT: name: "file1" +# NONFATAL-NEXT: dir_index: 0 +# NONFATAL-NEXT: mod_time: 0x00000000 +# NONFATAL-NEXT: length: 0x00000000 +# NONFATAL-NEXT: file_names[ 2]: +# NONFATAL-NEXT: name: "file2" +# NONFATAL-NEXT: dir_index: 1 +# NONFATAL-NEXT: mod_time: 0x00000002 +# NONFATAL-NEXT: length: 0x00000003 +# NONFATAL: 0xffffeeeeddddcccd 1 0 1 0 0 is_stmt{{$}} +# NONFATAL: 0xffffeeeeddddcccd 1 0 1 0 0 is_stmt end_sequence{{$}} + +# LAST: debug_line[0x00000329] # LAST: 0x00000000cafebabe {{.*}} end_sequence # RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe @@ -173,4 +191,5 @@ # ALL-NEXT: warning: parsing line table prologue at 0x000002ae found an invalid directory or file table description at 0x000002e0 # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid # ALL-NEXT: warning: parsing line table prologue at 0x000002ae should have ended at 0x000002d9 but it ended at 0x000002e0 +# ALL-NEXT: warning: parsing line table prologue at offset 0x000002ec found opcode base of 0. Assuming no standard opcodes # 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 @@ -354,6 +354,42 @@ "0x01"); } +TEST_F(DebugLineBasicFixture, ErrorAndContinueForZeroOpcodeBase) { + if (!setupGenerator()) + return; + + LineTable &Padding = Gen->addLineTable(); + Padding.setCustomPrologue({{0, LineTable::Byte}}); + LineTable < = Gen->addLineTable(); + DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue(); + // Save default state to allow for using checkDefaultPrologue later. + uint64_t DefaultOpcodeBase = Prologue.OpcodeBase; + auto DefaultLengths = Prologue.StandardOpcodeLengths; + Prologue.OpcodeBase = 0; + Prologue.StandardOpcodeLengths.clear(); + Prologue.TotalLength -= DefaultLengths.size(); + Prologue.PrologueLength -= DefaultLengths.size(); + LT.setPrologue(Prologue); + + generate(); + + DWARFDebugLine::Prologue Result; + uint64_t Offset = 1; + Error Err = Result.parse(LineData, &Offset, RecordRecoverable, *Context); + EXPECT_THAT_ERROR(std::move(Err), Succeeded()); + checkError("parsing line table prologue at offset 0x00000001 found opcode " + "base of 0. Assuming no standard opcodes", + std::move(Recoverable)); + EXPECT_EQ(0, Result.OpcodeBase); + EXPECT_TRUE(Result.StandardOpcodeLengths.empty()); + + Result.OpcodeBase = DefaultOpcodeBase; + Result.StandardOpcodeLengths = DefaultLengths; + Result.TotalLength += DefaultLengths.size(); + Result.PrologueLength += DefaultLengths.size(); + checkDefaultPrologue(Result.getVersion(), DWARF32, Result, 0); +} + TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { if (!setupGenerator(5)) return;