[llvm] r301844 - Adds initial llvm-dwarfdump --verify support with unit tests.

Greg Clayton via llvm-commits llvm-commits at lists.llvm.org
Mon May 1 15:07:02 PDT 2017


Author: gclayton
Date: Mon May  1 17:07:02 2017
New Revision: 301844

URL: http://llvm.org/viewvc/llvm-project?rev=301844&view=rev
Log:
Adds initial llvm-dwarfdump --verify support with unit tests.

lldb-dwarfdump gets a new "--verify" option that will verify a single file's DWARF debug info and will print out any errors that it finds. It will return an non-zero exit status if verification fails, and a zero exit status if verification succeeds. Adding the --quiet option will suppress any output the STDOUT or STDERR.

The first part of the verify does the following:

- verifies that all CU relative references (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8, DW_FORM_ref_udata) have valid CU offsets
- verifies that all DW_FORM_ref_addr references have valid .debug_info offsets
- verifies that all DW_AT_ranges attributes have valid .debug_ranges offsets
- verifies that all DW_AT_stmt_list attributes have valid .debug_line offsets
- verifies that all DW_FORM_strp attributes have valid .debug_str offsets

Unit tests were added for each of the above cases.

Differential Revision: https://reviews.llvm.org/D32707


Modified:
    llvm/trunk/include/llvm/DebugInfo/DIContext.h
    llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h
    llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
    llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp
    llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp
    llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
    llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp

Modified: llvm/trunk/include/llvm/DebugInfo/DIContext.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/DIContext.h?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/DIContext.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/DIContext.h Mon May  1 17:07:02 2017
@@ -161,6 +161,10 @@ public:
   virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
                     bool DumpEH = false, bool SummarizeTypes = false) = 0;
 
+  virtual bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) {
+    // No verifier? Just say things went well.
+    return true;
+  }
   virtual DILineInfo getLineInfoForAddress(uint64_t Address,
       DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
   virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address,

Modified: llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h Mon May  1 17:07:02 2017
@@ -106,6 +106,8 @@ public:
   void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
             bool DumpEH = false, bool SummarizeTypes = false) override;
 
+  bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) override;
+
   typedef DWARFUnitSection<DWARFCompileUnit>::iterator_range cu_iterator_range;
   typedef DWARFUnitSection<DWARFTypeUnit>::iterator_range tu_iterator_range;
   typedef iterator_range<decltype(TUs)::iterator> tu_section_iterator_range;

Modified: llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h Mon May  1 17:07:02 2017
@@ -59,6 +59,7 @@ public:
   DWARFFormValue(dwarf::Form F = dwarf::Form(0)) : Form(F) {}
 
   dwarf::Form getForm() const { return Form; }
+  uint64_t getRawUValue() const { return Value.uval; }
   void setForm(dwarf::Form F) { Form = F; }
   void setUValue(uint64_t V) { Value.uval = V; }
   void setSValue(int64_t V) { Value.sval = V; }

Modified: llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp (original)
+++ llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp Mon May  1 17:07:02 2017
@@ -284,6 +284,119 @@ void DWARFContext::dump(raw_ostream &OS,
                      getStringSection(), isLittleEndian());
 }
 
+bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
+  bool Success = true;
+  if (DumpType == DIDT_All || DumpType == DIDT_Info) {
+    OS << "Verifying .debug_info...\n";
+    for (const auto &CU : compile_units()) {
+      unsigned NumDies = CU->getNumDIEs();
+      for (unsigned I = 0; I < NumDies; ++I) {
+        auto Die = CU->getDIEAtIndex(I);
+        const auto Tag = Die.getTag();
+        if (Tag == DW_TAG_null)
+          continue;
+        for (auto AttrValue : Die.attributes()) {
+          const auto Attr = AttrValue.Attr;
+          const auto Form = AttrValue.Value.getForm();
+          switch (Attr) {
+          case DW_AT_ranges:
+            // Make sure the offset in the DW_AT_ranges attribute is valid.
+            if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
+              if (*SectionOffset >= getRangeSection().Data.size()) {
+                Success = false;
+                OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
+                      "bounds:\n";
+                Die.dump(OS, 0);
+                OS << "\n";
+              }
+            } else {
+              Success = false;
+              OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
+              Die.dump(OS, 0);
+              OS << "\n";
+            }
+            break;
+          case DW_AT_stmt_list:
+            // Make sure the offset in the DW_AT_stmt_list attribute is valid.
+            if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
+              if (*SectionOffset >= getLineSection().Data.size()) {
+                Success = false;
+                OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
+                      "bounds: "
+                   << format("0x%08" PRIx32, *SectionOffset) << "\n";
+                CU->getUnitDIE().dump(OS, 0);
+                OS << "\n";
+              }
+            } else {
+              Success = false;
+              OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
+              Die.dump(OS, 0);
+              OS << "\n";
+            }
+            break;
+
+          default:
+            break;
+          }
+          switch (Form) {
+          case DW_FORM_ref1:
+          case DW_FORM_ref2:
+          case DW_FORM_ref4:
+          case DW_FORM_ref8:
+          case DW_FORM_ref_udata: {
+            // Verify all CU relative references are valid CU offsets.
+            Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
+            assert(RefVal);
+            if (RefVal) {
+              auto DieCU = Die.getDwarfUnit();
+              auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
+              auto CUOffset = AttrValue.Value.getRawUValue();
+              if (CUOffset >= CUSize) {
+                Success = false;
+                OS << "error: " << FormEncodingString(Form) << " CU offset "
+                   << format("0x%08" PRIx32, CUOffset)
+                   << " is invalid (must be less than CU size of "
+                   << format("0x%08" PRIx32, CUSize) << "):\n";
+                Die.dump(OS, 0);
+                OS << "\n";
+              }
+            }
+            break;
+          }
+          case DW_FORM_ref_addr: {
+            // Verify all absolute DIE references have valid offsets in the
+            // .debug_info section.
+            Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
+            assert(RefVal);
+            if (RefVal && *RefVal >= getInfoSection().Data.size()) {
+              Success = false;
+              OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
+                    "bounds:\n";
+              Die.dump(OS, 0);
+              OS << "\n";
+            }
+            break;
+          }
+          case DW_FORM_strp: {
+            auto SecOffset = AttrValue.Value.getAsSectionOffset();
+            assert(SecOffset); // DW_FORM_strp is a section offset.
+            if (SecOffset && *SecOffset >= getStringSection().size()) {
+              Success = false;
+              OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
+              Die.dump(OS, 0);
+              OS << "\n";
+            }
+            break;
+          }
+          default:
+            break;
+          }
+        }
+      }
+    }
+  }
+  return Success;
+}
 const DWARFUnitIndex &DWARFContext::getCUIndex() {
   if (CUIndex)
     return *CUIndex;

Modified: llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp (original)
+++ llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp Mon May  1 17:07:02 2017
@@ -309,8 +309,10 @@ bool DWARFFormValue::isFormClass(DWARFFo
   }
   // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset.
   // Don't check for DWARF version here, as some producers may still do this
-  // by mistake.
-  return (Form == DW_FORM_data4 || Form == DW_FORM_data8) &&
+  // by mistake. Also accept DW_FORM_strp since this is .debug_str section
+  // offset.
+  return (Form == DW_FORM_data4 || Form == DW_FORM_data8 ||
+          Form == DW_FORM_strp) &&
          FC == FC_SectionOffset;
 }
 

Modified: llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp (original)
+++ llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp Mon May  1 17:07:02 2017
@@ -78,6 +78,11 @@ static cl::opt<bool>
     SummarizeTypes("summarize-types",
                    cl::desc("Abbreviate the description of type unit entries"));
 
+static cl::opt<bool> Verify("verify", cl::desc("Verify the DWARF debug info"));
+
+static cl::opt<bool> Quiet("quiet",
+                           cl::desc("Use with -verify to not emit to STDOUT."));
+
 static void error(StringRef Filename, std::error_code EC) {
   if (!EC)
     return;
@@ -116,6 +121,46 @@ static void DumpInput(StringRef Filename
     }
 }
 
+static bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) {
+  std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
+  
+  // Verify the DWARF and exit with non-zero exit status if verification
+  // fails.
+  raw_ostream &stream = Quiet ? nulls() : outs();
+  stream << "Verifying " << Filename.str() << ":\tfile format "
+  << Obj.getFileFormatName() << "\n";
+  bool Result = DICtx->verify(stream, DumpType);
+  if (Result)
+    stream << "No errors.\n";
+  else
+    stream << "Errors detected.\n";
+  return Result;
+}
+
+static bool VerifyInput(StringRef Filename) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+  MemoryBuffer::getFileOrSTDIN(Filename);
+  error(Filename, BuffOrErr.getError());
+  std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
+  
+  Expected<std::unique_ptr<Binary>> BinOrErr =
+  object::createBinary(Buff->getMemBufferRef());
+  if (!BinOrErr)
+    error(Filename, errorToErrorCode(BinOrErr.takeError()));
+  
+  bool Result = true;
+  if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get()))
+    Result = VerifyObjectFile(*Obj, Filename);
+  else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
+    for (auto &ObjForArch : Fat->objects()) {
+      auto MachOOrErr = ObjForArch.getAsObjectFile();
+      error(Filename, errorToErrorCode(MachOOrErr.takeError()));
+      if (!VerifyObjectFile(**MachOOrErr, Filename + " (" + ObjForArch.getArchFlagName() + ")"))
+        Result = false;
+    }
+  return Result;
+}
+
 /// If the input path is a .dSYM bundle (as created by the dsymutil tool),
 /// replace it with individual entries for each of the object files inside the
 /// bundle otherwise return the input path.
@@ -168,7 +213,13 @@ int main(int argc, char **argv) {
     Objects.insert(Objects.end(), Objs.begin(), Objs.end());
   }
 
-  std::for_each(Objects.begin(), Objects.end(), DumpInput);
+  if (Verify) {
+    // If we encountered errors during verify, exit with a non-zero exit status.
+    if (!std::all_of(Objects.begin(), Objects.end(), VerifyInput))
+      exit(1);
+  } else {
+    std::for_each(Objects.begin(), Objects.end(), DumpInput);
+  }
 
   return EXIT_SUCCESS;
 }

Modified: llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp?rev=301844&r1=301843&r2=301844&view=diff
==============================================================================
--- llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp (original)
+++ llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp Mon May  1 17:07:02 2017
@@ -1667,4 +1667,245 @@ TEST(DWARFDebugInfo, TestImplicitConstAb
   EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2);
 }
 
+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
+  // the compile unit itself.
+
+  const char *yamldata = R"(
+    debug_str:
+      - ''
+      - /tmp/main.c
+      - main
+    debug_abbrev:
+      - Code:            0x00000001
+        Tag:             DW_TAG_compile_unit
+        Children:        DW_CHILDREN_yes
+        Attributes:
+          - Attribute:       DW_AT_name
+            Form:            DW_FORM_strp
+      - Code:            0x00000002
+        Tag:             DW_TAG_subprogram
+        Children:        DW_CHILDREN_no
+        Attributes:
+          - Attribute:       DW_AT_name
+            Form:            DW_FORM_strp
+          - Attribute:       DW_AT_type
+            Form:            DW_FORM_ref4
+    debug_info:
+      - Length:
+          TotalLength:     22
+        Version:         4
+        AbbrOffset:      0
+        AddrSize:        8
+        Entries:
+          - AbbrCode:        0x00000001
+            Values:
+              - Value:           0x0000000000000001
+          - AbbrCode:        0x00000002
+            Values:
+              - Value:           0x000000000000000D
+              - Value:           0x0000000000001234
+          - AbbrCode:        0x00000000
+            Values:
+  )";
+  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);
+}
+
+TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) {
+  // Create a single compile unit with a single function that has an invalid
+  // DW_AT_type with an invalid .debug_info offset in its DW_FORM_ref_addr.
+  const char *yamldata = R"(
+    debug_str:
+      - ''
+      - /tmp/main.c
+      - main
+    debug_abbrev:
+      - Code:            0x00000001
+        Tag:             DW_TAG_compile_unit
+        Children:        DW_CHILDREN_yes
+        Attributes:
+          - Attribute:       DW_AT_name
+            Form:            DW_FORM_strp
+      - Code:            0x00000002
+        Tag:             DW_TAG_subprogram
+        Children:        DW_CHILDREN_no
+        Attributes:
+          - Attribute:       DW_AT_name
+            Form:            DW_FORM_strp
+          - Attribute:       DW_AT_type
+            Form:            DW_FORM_ref_addr
+    debug_info:
+      - Length:
+          TotalLength:     22
+        Version:         4
+        AbbrOffset:      0
+        AddrSize:        8
+        Entries:
+          - AbbrCode:        0x00000001
+            Values:
+              - Value:           0x0000000000000001
+          - AbbrCode:        0x00000002
+            Values:
+              - Value:           0x000000000000000D
+              - Value:           0x0000000000001234
+          - AbbrCode:        0x00000000
+            Values:
+  )";
+  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);
+}
+
+TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) {
+  // Create a single compile unit with a DW_AT_ranges whose section offset
+  // isn't valid.
+  const char *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_ranges
+            Form:            DW_FORM_sec_offset
+    debug_info:
+      - Length:
+          TotalLength:     16
+        Version:         4
+        AbbrOffset:      0
+        AddrSize:        8
+        Entries:
+          - AbbrCode:        0x00000001
+            Values:
+              - Value:           0x0000000000000001
+              - Value:           0x0000000000001000
+
+  )";
+  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);
+}
+
+TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) {
+  // Create a single compile unit with a DW_AT_stmt_list whose section offset
+  // isn't valid.
+  const char *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:           0x0000000000001000
+
+  )";
+  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);
+}
+
+TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
+  // Create a single compile unit with a single function that has an invalid
+  // DW_FORM_strp for the DW_AT_name.
+  const char *yamldata = R"(
+    debug_str:
+      - ''
+    debug_abbrev:
+      - Code:            0x00000001
+        Tag:             DW_TAG_compile_unit
+        Children:        DW_CHILDREN_no
+        Attributes:
+          - Attribute:       DW_AT_name
+            Form:            DW_FORM_strp
+    debug_info:
+      - Length:
+          TotalLength:     12
+        Version:         4
+        AbbrOffset:      0
+        AddrSize:        8
+        Entries:
+          - AbbrCode:        0x00000001
+            Values:
+              - Value:           0x0000000000001234
+  )";
+  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);
+}
+
 } // end anonymous namespace




More information about the llvm-commits mailing list