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 @@ -362,14 +362,16 @@ private: struct ParsingState { - ParsingState(struct LineTable *LT); + ParsingState(struct LineTable *LT, uint64_t TableOffset, + function_ref ErrorHandler); void resetRowAndSequence(); void appendRowToMatrix(); /// Advance the address by the \p OperationAdvance value. \returns the /// amount advanced by. - uint64_t advanceAddr(uint64_t OperationAdvance); + uint64_t advanceAddr(uint64_t OperationAdvance, uint8_t Opcode, + uint64_t OpcodeOffset); struct AddrAndAdjustedOpcode { uint64_t AddrDelta; @@ -378,7 +380,8 @@ /// Advance the address as required by the specified \p Opcode. /// \returns the amount advanced by and the calculated adjusted opcode. - AddrAndAdjustedOpcode advanceAddrForOpcode(uint8_t Opcode); + AddrAndAdjustedOpcode advanceAddrForOpcode(uint8_t Opcode, + uint64_t OpcodeOffset); struct AddrAndLineDelta { uint64_t Address; @@ -387,12 +390,18 @@ /// Advance the line and address as required by the specified special \p /// Opcode. \returns the address and line delta. - AddrAndLineDelta handleSpecialOpcode(uint8_t Opcode); + AddrAndLineDelta handleSpecialOpcode(uint8_t Opcode, uint64_t OpcodeOffset); /// Line table we're currently parsing. struct LineTable *LineTable; struct Row Row; struct Sequence Sequence; + + private: + uint64_t LineTableOffset; + + bool ReportAdvanceAddrProblem = true; + function_ref ErrorHandler; }; using LineTableMapTy = std::map; 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 @@ -507,8 +507,10 @@ Sequences.clear(); } -DWARFDebugLine::ParsingState::ParsingState(struct LineTable *LT) - : LineTable(LT) { +DWARFDebugLine::ParsingState::ParsingState( + struct LineTable *LT, uint64_t TableOffset, + function_ref ErrorHandler) + : LineTable(LT), LineTableOffset(TableOffset), ErrorHandler(ErrorHandler) { resetRowAndSequence(); } @@ -566,14 +568,39 @@ return LT; } -uint64_t DWARFDebugLine::ParsingState::advanceAddr(uint64_t OperationAdvance) { +static StringRef getOpcodeName(uint8_t Opcode, uint8_t OpcodeBase) { + assert(Opcode != 0); + if (Opcode < OpcodeBase) + return LNStandardString(Opcode); + return "special"; +} + +uint64_t DWARFDebugLine::ParsingState::advanceAddr(uint64_t OperationAdvance, + uint8_t Opcode, + uint64_t OpcodeOffset) { + StringRef OpcodeName = getOpcodeName(Opcode, LineTable->Prologue.OpcodeBase); + // For versions less than 4, the MaxOpsPerInst member is set to 0, as the + // maximum_operations_per_instruction field wasn't introduced until DWARFv4. + // Don't warn about bad values in this situation. + if (ReportAdvanceAddrProblem && LineTable->Prologue.getVersion() >= 4 && + LineTable->Prologue.MaxOpsPerInst != 1) + ErrorHandler(createStringError( + errc::not_supported, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue maximum_operations_per_instruction value is %" PRId8 + ", which is unsupported. Assuming a value of 1 instead", + LineTableOffset, OpcodeName.data(), OpcodeOffset, + LineTable->Prologue.MaxOpsPerInst)); + ReportAdvanceAddrProblem = false; uint64_t AddrOffset = OperationAdvance * LineTable->Prologue.MinInstLength; Row.Address.Address += AddrOffset; return AddrOffset; } DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode -DWARFDebugLine::ParsingState::advanceAddrForOpcode(uint8_t Opcode) { +DWARFDebugLine::ParsingState::advanceAddrForOpcode(uint8_t Opcode, + uint64_t OpcodeOffset) { assert(Opcode == DW_LNS_const_add_pc || Opcode >= LineTable->Prologue.OpcodeBase); uint8_t OpcodeValue = Opcode; @@ -581,12 +608,13 @@ OpcodeValue = 255; uint8_t AdjustedOpcode = OpcodeValue - LineTable->Prologue.OpcodeBase; uint64_t OperationAdvance = AdjustedOpcode / LineTable->Prologue.LineRange; - uint64_t AddrOffset = advanceAddr(OperationAdvance); + uint64_t AddrOffset = advanceAddr(OperationAdvance, Opcode, OpcodeOffset); return {AddrOffset, AdjustedOpcode}; } DWARFDebugLine::ParsingState::AddrAndLineDelta -DWARFDebugLine::ParsingState::handleSpecialOpcode(uint8_t Opcode) { +DWARFDebugLine::ParsingState::handleSpecialOpcode(uint8_t Opcode, + uint64_t OpcodeOffset) { // A special opcode value is chosen based on the amount that needs // to be added to the line and address registers. The maximum line // increment for a special opcode is the value of the line_base @@ -619,7 +647,7 @@ // line increment = line_base + (adjusted opcode % line_range) DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode AddrAdvanceResult = - advanceAddrForOpcode(Opcode); + advanceAddrForOpcode(Opcode, OpcodeOffset); int32_t LineOffset = LineTable->Prologue.LineBase + (AddrAdvanceResult.AdjustedOpcode % LineTable->Prologue.LineRange); @@ -673,12 +701,13 @@ assert(Prologue.getAddressSize() == 0 || Prologue.getAddressSize() == DebugLineData.getAddressSize()); - ParsingState State(this); + ParsingState State(this, DebugLineOffset, RecoverableErrorHandler); while (*OffsetPtr < EndOffset) { if (OS) *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr); + uint64_t OpcodeOffset = *OffsetPtr; uint8_t Opcode = DebugLineData.getU8(OffsetPtr); if (OS) @@ -853,8 +882,8 @@ // min_inst_length field of the prologue, and adds the // result to the address register of the state machine. { - uint64_t AddrOffset = - State.advanceAddr(DebugLineData.getULEB128(OffsetPtr)); + uint64_t AddrOffset = State.advanceAddr( + DebugLineData.getULEB128(OffsetPtr), Opcode, OpcodeOffset); if (OS) *OS << " (" << AddrOffset << ")"; } @@ -909,7 +938,8 @@ // than twice that range will it need to use both DW_LNS_advance_pc // and a special opcode, requiring three or more bytes. { - uint64_t AddrOffset = State.advanceAddrForOpcode(Opcode).AddrDelta; + uint64_t AddrOffset = + State.advanceAddrForOpcode(Opcode, OpcodeOffset).AddrDelta; if (OS) *OS << format(" (0x%16.16" PRIx64 ")", AddrOffset); } @@ -972,7 +1002,8 @@ } } else { // Special Opcodes. - ParsingState::AddrAndLineDelta Delta = State.handleSpecialOpcode(Opcode); + ParsingState::AddrAndLineDelta Delta = + State.handleSpecialOpcode(Opcode, OpcodeOffset); if (OS) { *OS << "address += " << Delta.Address << ", line += " << Delta.Line 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 @@ -754,6 +754,213 @@ EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); } +struct AdjustAddressFixtureBase : public CommonFixture { + virtual ~AdjustAddressFixtureBase() {} + + // Create and update the prologue as specified by the subclass, then return + // the length of the table. + virtual uint64_t editPrologue(LineTable <) = 0; + + virtual uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncrs, + uint64_t SpecialIncrs, + uint64_t AdvanceIncrs) { + return Base + ConstIncrs + SpecialIncrs + AdvanceIncrs; + } + + virtual uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) { + return Base + Incr; + } + + uint64_t setupNoProblemTable() { + LineTable &NoProblem = Gen->addLineTable(); + NoProblem.addExtendedOpcode(9, DW_LNE_set_address, + {{0xabcd, LineTable::Quad}}); + NoProblem.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + return editPrologue(NoProblem); + } + + uint64_t setupConstAddPcFirstTable() { + LineTable &ConstAddPCFirst = Gen->addLineTable(); + ConstAddPCFirst.addExtendedOpcode(9, DW_LNE_set_address, + {{ConstAddPCAddr, LineTable::Quad}}); + ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + ConstAddPCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x10, LineTable::ULEB}}); + ConstAddPCFirst.addByte(0x21); // Special opcode, +1 op, +1 line. + ConstAddPCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + return editPrologue(ConstAddPCFirst); + } + + uint64_t setupSpecialFirstTable() { + LineTable &SpecialFirst = Gen->addLineTable(); + SpecialFirst.addExtendedOpcode(9, DW_LNE_set_address, + {{SpecialAddr, LineTable::Quad}}); + SpecialFirst.addByte(0x22); // Special opcode, +1 op, +2 line. + SpecialFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + SpecialFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x20, LineTable::ULEB}}); + SpecialFirst.addByte(0x23); // Special opcode, +1 op, +3 line. + SpecialFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + return editPrologue(SpecialFirst); + } + + uint64_t setupAdvancePcFirstTable() { + LineTable &AdvancePCFirst = Gen->addLineTable(); + AdvancePCFirst.addExtendedOpcode(9, DW_LNE_set_address, + {{AdvancePCAddr, LineTable::Quad}}); + AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x30, LineTable::ULEB}}); + AdvancePCFirst.addStandardOpcode(DW_LNS_const_add_pc, {}); + AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc, + {{0x40, LineTable::ULEB}}); + AdvancePCFirst.addByte(0x24); // Special opcode, +1 op, +4 line. + AdvancePCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + return editPrologue(AdvancePCFirst); + } + + void setupTables(bool AddAdvancePCFirstTable) { + LineTable &Padding = Gen->addLineTable(); + Padding.setCustomPrologue({{0, LineTable::Byte}}); + NoProblemOffset = 1; + + // Show that no warning is generated for the case where no + // DW_LNS_const_add_pc or special opcode is used. + ConstAddPCOffset = setupNoProblemTable() + NoProblemOffset; + + // Show that the warning is emitted for the first DW_LNS_const_add_pc opcode + // and then not again. + SpecialOffset = setupConstAddPcFirstTable() + ConstAddPCOffset; + + // Show that the warning is emitted for the first special opcode and then + // not again. + AdvancePCOffset = setupSpecialFirstTable() + SpecialOffset; + + // Show that the warning is emitted for the first DW_LNS_advance_pc opcode + // (if requested) and then not again. + if (AddAdvancePCFirstTable) + setupAdvancePcFirstTable(); + } + + Expected + checkTable(uint64_t Offset, StringRef OpcodeType, const Twine &MsgSuffix) { + auto ExpectedTable = Line.getOrParseLineTable(LineData, Offset, *Context, + nullptr, RecordRecoverable); + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + if (!IsErrorExpected) { + EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded()); + } else { + if (!ExpectedTable) + return ExpectedTable; + uint64_t ExpectedOffset = Offset + + (*ExpectedTable)->Prologue.getLength() + + 11; // 11 == size of DW_LNE_set_address. + std::string OffsetHex = Twine::utohexstr(Offset).str(); + std::string OffsetZeroes = std::string(8 - OffsetHex.size(), '0'); + std::string ExpectedHex = Twine::utohexstr(ExpectedOffset).str(); + std::string ExpectedZeroes = std::string(8 - ExpectedHex.size(), '0'); + EXPECT_THAT_ERROR( + std::move(Recoverable), + FailedWithMessage(("line table program at offset 0x" + OffsetZeroes + + OffsetHex + " contains a " + OpcodeType + + " opcode at offset 0x" + ExpectedZeroes + + ExpectedHex + ", " + MsgSuffix) + .str())); + } + return ExpectedTable; + } + + void runTest(bool CheckAdvancePC, Twine MsgSuffix) { + if (!setupGenerator(Version)) + return; + + setupTables(/*AddAdvancePCFirstTable=*/CheckAdvancePC); + + generate(); + + auto ExpectedNoProblem = Line.getOrParseLineTable( + LineData, NoProblemOffset, *Context, nullptr, RecordRecoverable); + EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded()); + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + ASSERT_THAT_EXPECTED(ExpectedNoProblem, Succeeded()); + + auto ExpectedConstAddPC = + checkTable(ConstAddPCOffset, "DW_LNS_const_add_pc", MsgSuffix); + ASSERT_THAT_EXPECTED(ExpectedConstAddPC, Succeeded()); + ASSERT_EQ((*ExpectedConstAddPC)->Rows.size(), 2u); + EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Address.Address, + getAdjustedAddr(ConstAddPCAddr, ConstIncr * 2, 0x1, 0x10)); + EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Line, getAdjustedLine(1, 1)); + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + + auto ExpectedSpecial = checkTable(SpecialOffset, "special", MsgSuffix); + ASSERT_THAT_EXPECTED(ExpectedSpecial, Succeeded()); + ASSERT_EQ((*ExpectedSpecial)->Rows.size(), 3u); + EXPECT_EQ((*ExpectedSpecial)->Rows[0].Address.Address, + getAdjustedAddr(SpecialAddr, 0, 1, 0)); + EXPECT_EQ((*ExpectedSpecial)->Rows[0].Line, getAdjustedLine(1, 2)); + EXPECT_EQ((*ExpectedSpecial)->Rows[1].Address.Address, + getAdjustedAddr(SpecialAddr, ConstIncr, 0x2, 0x20)); + EXPECT_EQ((*ExpectedSpecial)->Rows[1].Line, getAdjustedLine(1, 5)); + EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded()); + + if (!CheckAdvancePC) + return; + + auto ExpectedAdvancePC = + checkTable(AdvancePCOffset, "DW_LNS_advance_pc", MsgSuffix); + ASSERT_THAT_EXPECTED(ExpectedAdvancePC, Succeeded()); + ASSERT_EQ((*ExpectedAdvancePC)->Rows.size(), 2u); + EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Address.Address, + getAdjustedAddr(AdvancePCAddr, ConstIncr, 0x1, 0x70)); + EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Line, getAdjustedLine(1, 4)); + } + + uint64_t ConstIncr = 0x11; + uint64_t ConstAddPCAddr = 0x1234; + uint64_t SpecialAddr = 0x5678; + uint64_t AdvancePCAddr = 0xabcd; + uint64_t NoProblemOffset; + uint64_t ConstAddPCOffset; + uint64_t SpecialOffset; + uint64_t AdvancePCOffset; + + uint16_t Version = 4; + bool IsErrorExpected; +}; + +struct MaxOpsPerInstFixture + : public TestWithParam>, + public AdjustAddressFixtureBase { + void SetUp() override { + std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam(); + } + + uint64_t editPrologue(LineTable <) override { + DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue(); + Prologue.MaxOpsPerInst = MaxOpsPerInst; + LT.setPrologue(Prologue); + return Prologue.TotalLength + Prologue.sizeofTotalLength(); + } + + uint8_t MaxOpsPerInst; +}; + +TEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) { + runTest(/*CheckAdvancePC=*/true, + "but the prologue maximum_operations_per_instruction value is " + + Twine(unsigned(MaxOpsPerInst)) + + ", which is unsupported. Assuming a value of 1 instead"); +} + +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;