From 6707046f9018c34274cdfbc317eb6041631e7f2c Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Tue, 2 May 2017 22:48:52 +0000 Subject: [PATCH] Add line table verification to lldb-dwarfdump --verify This patch verifies the .debug_line: - verify all addresses in a line table sequence have ascending addresses - verify that all line table file indexes are valid Unit tests added for both cases. Differential Revision: https://reviews.llvm.org/D32765 llvm-svn: 301984 --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h | 2 +- llvm/lib/DebugInfo/DWARF/DWARFContext.cpp | 73 +++++++ llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp | 10 +- .../DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 243 ++++++++++++++------- 4 files changed, 251 insertions(+), 77 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h index 5cf3ce4..e21245b 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -104,7 +104,7 @@ public: void postAppend(); void reset(bool DefaultIsStmt); void dump(raw_ostream &OS) const; - + static void dumpTableHeader(raw_ostream &OS); static bool orderByAddress(const Row &LHS, const Row &RHS) { return LHS.Address < RHS.Address; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index bfc705a..84aa090 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -445,6 +445,75 @@ public: } return Success; } + + bool HandleDebugLine() { + bool Success = true; + OS << "Verifying .debug_line...\n"; + for (const auto &CU : DCtx.compile_units()) { + uint32_t LineTableOffset = 0; + auto StmtFormValue = CU->getUnitDIE().find(DW_AT_stmt_list); + if (!StmtFormValue) { + // No line table for this compile unit. + continue; + } + // Get the attribute value as a section offset. No need to produce an + // error here if the encoding isn't correct because we validate this in + // the .debug_info verifier. + if (auto StmtSectionOffset = toSectionOffset(StmtFormValue)) { + LineTableOffset = *StmtSectionOffset; + if (LineTableOffset >= DCtx.getLineSection().Data.size()) { + // Make sure we don't get a valid line table back if the offset + // is wrong. + assert(DCtx.getLineTableForUnit(CU.get()) == nullptr); + // Skip this line table as it isn't valid. No need to create an error + // here because we validate this in the .debug_info verifier. + continue; + } + } + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + if (!LineTable) { + Success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + CU->getUnitDIE().dump(OS, 0); + OS << '\n'; + continue; + } + uint32_t MaxFileIndex = LineTable->Prologue.FileNames.size(); + uint64_t PrevAddress = 0; + uint32_t RowIndex = 0; + for (const auto &Row : LineTable->Rows) { + if (Row.Address < PrevAddress) { + Success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] row[" << RowIndex + << "] decreases in address from previous row:\n"; + + DWARFDebugLine::Row::dumpTableHeader(OS); + if (RowIndex > 0) + LineTable->Rows[RowIndex - 1].dump(OS); + Row.dump(OS); + OS << '\n'; + } + + if (Row.File > MaxFileIndex) { + Success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "][" << RowIndex << "] has invalid file index " << Row.File + << " (valid values are [1," << MaxFileIndex << "]):\n"; + DWARFDebugLine::Row::dumpTableHeader(OS); + Row.dump(OS); + OS << '\n'; + } + if (Row.EndSequence) + PrevAddress = 0; + else + PrevAddress = Row.Address; + ++RowIndex; + } + } + return Success; + } }; } // anonymous namespace @@ -456,6 +525,10 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) { if (!verifier.HandleDebugInfo()) Success = false; } + if (DumpType == DIDT_All || DumpType == DIDT_Line) { + if (!verifier.HandleDebugLine()) + Success = false; + } return Success; } const DWARFUnitIndex &DWARFContext::getCUIndex() { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index 45e92cf..f32e8fe 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -287,6 +287,12 @@ void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { EpilogueBegin = false; } +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { + OS << "Address Line Column File ISA Discriminator Flags\n" + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; +} + void DWARFDebugLine::Row::dump(raw_ostream &OS) const { OS << format("0x%16.16" PRIx64 " %6u %6u", Address, Line, Column) << format(" %6u %3u %13u ", File, Isa, Discriminator) @@ -313,9 +319,7 @@ void DWARFDebugLine::LineTable::dump(raw_ostream &OS) const { OS << '\n'; if (!Rows.empty()) { - OS << "Address Line Column File ISA Discriminator Flags\n" - << "------------------ ------ ------ ------ --- ------------- " - "-------------\n"; + Row::dumpTableHeader(OS); for (const Row &R : Rows) { R.dump(OS); } diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index af16378..8e11c80 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -10,6 +10,7 @@ #include "DwarfGenerator.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/Config/llvm-config.h" @@ -18,8 +19,8 @@ #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/Object/ObjectFile.h" -#include "llvm/ObjectYAML/DWARFYAML.h" #include "llvm/ObjectYAML/DWARFEmitter.h" +#include "llvm/ObjectYAML/DWARFYAML.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" @@ -1191,10 +1192,7 @@ TEST(DWARFDebugInfo, TestEmptyChildren) { auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); // Verify the number of compile units is correct. uint32_t NumCUs = DwarfContext.getNumCompileUnits(); @@ -1667,6 +1665,13 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2); } +void VerifyError(DWARFContext &DwarfContext, StringRef Error) { + SmallString<1024> Str; + raw_svector_ostream Strm(Str); + EXPECT_FALSE(DwarfContext.verify(Strm, DIDT_All)); + EXPECT_TRUE(Str.str().contains(Error)); +} + TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) { // Create a single compile unit with a single function that has a DW_AT_type // that is CU relative. The CU offset is not valid becuase it is larger than @@ -1711,17 +1716,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); - - std::string str; - raw_string_ostream strm(str); - EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All)); - const char *err = "error: DW_FORM_ref4 CU offset 0x00001234 is invalid " - "(must be less than CU size of 0x0000001a):"; - EXPECT_TRUE(strm.str().find(err) != std::string::npos); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError(DwarfContext, "error: DW_FORM_ref4 CU offset 0x00001234 is " + "invalid (must be less than CU size of " + "0x0000001a):"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) { @@ -1766,17 +1764,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); - - std::string str; - raw_string_ostream strm(str); - EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All)); - strm.flush(); - const char *err = "error: DW_FORM_ref_addr offset beyond .debug_info bounds:"; - EXPECT_TRUE(strm.str().find(err) != std::string::npos); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError(DwarfContext, + "error: DW_FORM_ref_addr offset beyond .debug_info bounds:"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) { @@ -1810,18 +1800,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); - - std::string str; - raw_string_ostream strm(str); - EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All)); - strm.flush(); - const char *err = "error: DW_AT_ranges offset is beyond .debug_ranges " - "bounds:"; - EXPECT_TRUE(strm.str().find(err) != std::string::npos); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError(DwarfContext, + "error: DW_AT_ranges offset is beyond .debug_ranges bounds:"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) { @@ -1855,18 +1836,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); - - std::string str; - raw_string_ostream strm(str); - EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All)); - strm.flush(); - const char *err = "error: DW_AT_stmt_list offset is beyond .debug_line " - "bounds: 0x00001000"; - EXPECT_TRUE(strm.str().find(err) != std::string::npos); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError( + DwarfContext, + "error: DW_AT_stmt_list offset is beyond .debug_line bounds: 0x00001000"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) { @@ -1895,17 +1868,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); - - std::string str; - raw_string_ostream strm(str); - EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All)); - strm.flush(); - const char *err = "error: DW_FORM_strp offset beyond .debug_str bounds:"; - EXPECT_TRUE(strm.str().find(err) != std::string::npos); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError(DwarfContext, + "error: DW_FORM_strp offset beyond .debug_str bounds:"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) { @@ -1950,18 +1915,150 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError( + DwarfContext, + "error: invalid DIE reference 0x00000011. Offset is in between DIEs:"); +} - auto &DebugSections = *ErrOrSections; - - DWARFContextInMemory DwarfContext(DebugSections, 8); +TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineSequence) { + // Create a single compile unit whose line table has a sequence in it where + // the address decreases. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + debug_info: + - Length: + TotalLength: 16 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - Value: 0x0000000000000000 + debug_line: + - Length: + TotalLength: 68 + Version: 2 + PrologueLength: 34 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - /tmp + Files: + - Name: main.c + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4112 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 4112 + - Opcode: DW_LNS_copy + Data: 4112 + - Opcode: DW_LNS_advance_pc + Data: 18446744073709551600 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 18446744073709551600 + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError(DwarfContext, "error: .debug_line[0x00000000] row[1] decreases " + "in address from previous row:"); +} - std::string str; - raw_string_ostream strm(str); - EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All)); - strm.flush(); - const char *err = "error: invalid DIE reference 0x00000011. Offset is in " - "between DIEs:"; - EXPECT_TRUE(strm.str().find(err) != std::string::npos); +TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineFileIndex) { + // Create a single compile unit whose line table has a line table row with + // an invalid file index. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + debug_info: + - Length: + TotalLength: 16 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - Value: 0x0000000000000000 + debug_line: + - Length: + TotalLength: 61 + Version: 2 + PrologueLength: 34 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - /tmp + Files: + - Name: main.c + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 4096 + - Opcode: DW_LNS_copy + Data: 4096 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_set_file + Data: 5 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 5 + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + VerifyError(DwarfContext, "error: .debug_line[0x00000000][1] has invalid " + "file index 5 (valid values are [1,1]):"); } - + } // end anonymous namespace -- 2.7.4