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 @@ -609,6 +609,26 @@ ParsingState State(this); + // For versions less than 4, the MaxOpsPerInst member is set to 0, as it is + // not as the maximum_operations_per_instruction field wasn't introduced until + // DWARFv4. Don't warn about bad values in this situation. + bool IgnoreBadMaxOps = Prologue.getVersion() < 4; + auto CheckMaxOpsPerInst = [&IgnoreBadMaxOps, DebugLineOffset, + RecoverableErrorHandler](uint64_t Offset, + uint8_t MaxOpsPerInst) { + if (IgnoreBadMaxOps || MaxOpsPerInst == 1) + return; + IgnoreBadMaxOps = true; + RecoverableErrorHandler(createStringError( + errc::not_supported, + "line table program at offset 0x%8.8" PRIx64 + " contains an opcode at offset 0x%8.8" PRIx64 + " that advances the address, but the prologue " + "maximum_operations_per_instruction value is %" PRId8 + ", which is unsupported. Assuming a value of 1 instead", + DebugLineOffset, Offset, MaxOpsPerInst)); + }; + while (*OffsetPtr < EndOffset) { if (OS) *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr); @@ -787,6 +807,7 @@ // min_inst_length field of the prologue, and adds the // result to the address register of the state machine. { + CheckMaxOpsPerInst(*OffsetPtr - 1, Prologue.MaxOpsPerInst); uint64_t AddrOffset = DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; State.Row.Address.Address += AddrOffset; @@ -844,6 +865,7 @@ // than twice that range will it need to use both DW_LNS_advance_pc // and a special opcode, requiring three or more bytes. { + CheckMaxOpsPerInst(*OffsetPtr - 1, Prologue.MaxOpsPerInst); uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; uint64_t AddrOffset = (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; @@ -943,6 +965,8 @@ // // line increment = line_base + (adjusted opcode % line_range) + CheckMaxOpsPerInst(*OffsetPtr - 1, Prologue.MaxOpsPerInst); + uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; uint64_t AddrOffset = (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; 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 @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "DwarfGenerator.h" #include "DwarfUtils.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" @@ -763,6 +763,192 @@ EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); } +struct MaxOpsPerInstFixture + : public TestWithParam>, + public CommonFixture { + void SetUp() { std::tie(Version, MaxOpsPerInst, ExpectedError) = GetParam(); } + + uint16_t Version; + uint8_t MaxOpsPerInst; + bool ExpectedError; +}; + +TEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) { + if (!setupGenerator(Version)) + return; + + LineTable &Padding = Gen->addLineTable(); + Padding.setCustomPrologue({{0, LineTable::Byte}}); + + // Show that no warning is generated for the case where no + // DW_LNS_const_add_pc or special opcode is used. + LineTable &NoProblem = Gen->addLineTable(); + NoProblem.addExtendedOpcode(9, DW_LNE_set_address, + {{0xabcd, LineTable::Quad}}); + NoProblem.addStandardOpcode(DW_LNS_copy, {}); + // Change the address, so that we have a valid sequence. + NoProblem.addExtendedOpcode(9, DW_LNE_set_address, + {{0xabce, LineTable::Quad}}); + NoProblem.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + DWARFDebugLine::Prologue NoProblemPrologue = NoProblem.createBasicPrologue(); + NoProblemPrologue.MaxOpsPerInst = MaxOpsPerInst; + NoProblem.setPrologue(NoProblemPrologue); + + // Show that the warning is emitted for the first DW_LNS_const_add_pc opcode + // and then not again. + LineTable &ConstAddPCFirst = Gen->addLineTable(); + uint64_t Addr1 = 0x1234; + uint64_t ConstIncr = 0x11; + ConstAddPCFirst.addExtendedOpcode(9, DW_LNE_set_address, + {{Addr1, LineTable::Quad}}); + ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + ConstAddPCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x10, LineTable::Quad}}); + ConstAddPCFirst.addByte(0x21); // Special opcode, +1 op, +1 line. + ConstAddPCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + DWARFDebugLine::Prologue ConstAddPCPrologue = + ConstAddPCFirst.createBasicPrologue(); + ConstAddPCPrologue.MaxOpsPerInst = MaxOpsPerInst; + ConstAddPCFirst.setPrologue(ConstAddPCPrologue); + + // Show that the warning is emitted for the first DW_LNS_const_add_pc opcode + // and then not again. + LineTable &AdvancePCFirst = Gen->addLineTable(); + uint64_t Addr1 = 0x1111; + AdvancePCFirst.addExtendedOpcode(9, DW_LNE_set_address, + {{Addr1, LineTable::Quad}}); + AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x20, LineTable::Quad}}); + AdvancePCFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x30, LineTable::Quad}}); + AdvancePCFirst.addByte(0x22); // Special opcode, +1 op, +2 line. + AdvancePCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + DWARFDebugLine::Prologue AdvancePCPrologue = + AdvancePCFirst.createBasicPrologue(); + AdvancePCPrologue.MaxOpsPerInst = MaxOpsPerInst; + AdvancePCFirst.setPrologue(AdvancePCPrologue); + + // Show that the warning is emitted for the first special opcode and then + // not again. + LineTable &SpecialFirst = Gen->addLineTable(); + uint64_t Addr2 = 0x5678; + SpecialFirst.addExtendedOpcode(9, DW_LNE_set_address, + {{Addr2, LineTable::Quad}}); + SpecialFirst.addByte(0x23); // Special opcode, +1 op, +3 line. + SpecialFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x40, LineTable::Quad}}); + SpecialFirst.addByte(0x24); // Special opcode, +1 op, +4 line. + SpecialFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + DWARFDebugLine::Prologue SpecialPrologue = SpecialFirst.createBasicPrologue(); + SpecialPrologue.MaxOpsPerInst = MaxOpsPerInst; + SpecialFirst.setPrologue(SpecialPrologue); + + generate(); + + auto ExpectedNoProblem = Line.getOrParseLineTable(LineData, 1, *Context, + nullptr, RecordRecoverable); + EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded()); + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + ASSERT_THAT_EXPECTED(ExpectedNoProblem, Succeeded()); + + uint64_t NextOffset = 1 + (*ExpectedNoProblem)->Prologue.TotalLength + + (*ExpectedNoProblem)->Prologue.sizeofTotalLength(); + auto ExpectedConstAddPC = Line.getOrParseLineTable( + LineData, NextOffset, *Context, nullptr, RecordRecoverable); + ASSERT_THAT_EXPECTED(ExpectedConstAddPC, Succeeded()); + if (!ExpectedError) { + EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded()); + } else { + uint64_t ExpectedOffset = NextOffset + + (*ExpectedConstAddPC)->Prologue.getLength() + + 11; // 11 == size of DW_LNE_set_address. + checkError(("line table program with offset 0x000000" + + Twine::utohexstr(NextOffset) + + " contains an opcode at offset 0x000000" + + Twine::utohexstr(ExpectedOffset) + + " that advances the address, but the prologue " + "maximum_operations_per_instruction value is " + + Twine(unsigned(MaxOpsPerInst)) + + ", which is unsupported. Assuming a value of 1 instead") + .str(), + std::move(Recoverable)); + } + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + ASSERT_EQ((*ExpectedConstAddPC)->Rows.size(), 2u); + EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Address.Address, + Addr1 + ConstIncr * 2 + 0x11); + EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Line, 2u); + + NextOffset = NextOffset + (*ExpectedConstAddPC)->Prologue.TotalLength + + (*ExpectedConstAddPC)->Prologue.sizeofTotalLength(); + auto ExpectedAdvancePC = Line.getOrParseLineTable( + LineData, NextOffset, *Context, nullptr, RecordRecoverable); + ASSERT_THAT_EXPECTED(ExpectedAdvancePC, Succeeded()); + if (!ExpectedError) { + EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded()); + } else { + uint64_t ExpectedOffset = NextOffset + + (*ExpectedAdvancePC)->Prologue.getLength() + + 11; // 11 == size of DW_LNE_set_address. + checkError(("line table program with offset 0x000000" + + Twine::utohexstr(NextOffset) + + " contains an opcode at offset 0x000000" + + Twine::utohexstr(ExpectedOffset) + + " that advances the address, but the prologue " + "maximum_operations_per_instruction value is " + + Twine(unsigned(MaxOpsPerInst)) + + ", which is unsupported. Assuming a value of 1 instead") + .str(), + std::move(Recoverable)); + } + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + ASSERT_EQ((*ExpectedAdvancePC)->Rows.size(), 2u); + EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Address.Address, + Addr1 + ConstIncr * 2 + 0x51); + EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Line, 3u); + + NextOffset = NextOffset + (*ExpectedAdvancePC)->Prologue.TotalLength + + (*ExpectedAdvancePC)->Prologue.sizeofTotalLength(); + auto ExpectedSpecial = Line.getOrParseLineTable( + LineData, NextOffset, *Context, nullptr, RecordRecoverable); + ASSERT_THAT_EXPECTED(ExpectedSpecial, Succeeded()); + if (!ExpectedError) { + EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded()); + } else { + uint64_t ExpectedOffset = NextOffset + + (*ExpectedSpecial)->Prologue.getLength() + + 11; // 11 == size of DW_LNE_set_address. + checkError( + ("line table program with offset 0x000000" + + Twine::utohexstr(NextOffset) + + " contains a special opcode or DW_LNS_const_add_pc opcode at " + "offset 0x000000" + + Twine::utohexstr(ExpectedOffset) + + ", but the prologue maximum_operations_per_instruction value is " + + Twine(unsigned(MaxOpsPerInst)) + + ", which is unsupported. Assuming a value of 1 instead") + .str(), + std::move(Recoverable)); + } + ASSERT_EQ((*ExpectedSpecial)->Rows.size(), 3u); + EXPECT_EQ((*ExpectedSpecial)->Rows[0].Address.Address, Addr2 + 1); + EXPECT_EQ((*ExpectedSpecial)->Rows[0].Line, 4u); + EXPECT_EQ((*ExpectedSpecial)->Rows[1].Address.Address, + Addr2 + 0x72 + ConstIncr); + EXPECT_EQ((*ExpectedSpecial)->Rows[1].Line, 8u); +} + +INSTANTIATE_TEST_CASE_P( + MaxOpsPerInstParams, MaxOpsPerInstFixture, + Values(std::make_tuple(3, 0, false), // Test for version < 4 (no error). + std::make_tuple(4, 0, true), // Test zero value for V4 (error). + std::make_tuple(4, 1, false), // Test good value for V4 (no error). + std::make_tuple( + 4, 2, true)), ); // Test one higher than permitted V4 (error). + TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { if (!setupGenerator()) return;