]> granicus.if.org Git - llvm/commitdiff
Verify that all references point to actual DIEs in "llvm-dwarfdump --verify"
authorGreg Clayton <gclayton@apple.com>
Tue, 2 May 2017 20:28:33 +0000 (20:28 +0000)
committerGreg Clayton <gclayton@apple.com>
Tue, 2 May 2017 20:28:33 +0000 (20:28 +0000)
LTO and other fancy linking previously led to DWARF that contained invalid references. We already validate that CU relative references fall into the CU, and the DW_FORM_ref_addr references fall inside the .debug_info section, but we didn't validate that the references pointed to correct DIE offsets. This new verification will ensure that all references refer to actual DIEs and not an offset in between.

This caught a bug in DWARFUnit::getDIEForOffset() where if you gave it any offset, it would match the DIE that mathes the offset _or_ the next DIE. This has been fixed.

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301971 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/DWARF/DWARFContext.h
include/llvm/DebugInfo/DWARF/DWARFUnit.h
lib/DebugInfo/DWARF/DWARFContext.cpp
unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp

index 3c04c6716ea39ba379f4eabe8fea118bca58614c..b9f3425d5deb35cd1112e633273d44d15f32dfa8 100644 (file)
@@ -172,6 +172,9 @@ public:
     return DWOCUs[index].get();
   }
 
+  /// Get a DIE given an exact offset.
+  DWARFDie getDIEForOffset(uint32_t Offset);
+
   const DWARFUnitIndex &getCUIndex();
   DWARFGdbIndex &getGdbIndex();
   const DWARFUnitIndex &getTUIndex();
index e29ba523238c155c0476f61be6ee2a53a039e543..68e541bac73c69328faab3477e42261fadf4d5fb 100644 (file)
@@ -312,9 +312,9 @@ public:
         [](const DWARFDebugInfoEntry &LHS, uint32_t Offset) {
           return LHS.getOffset() < Offset;
         });
-    if (it == DieArray.end())
-      return DWARFDie();
-    return DWARFDie(this, &*it);
+    if (it != DieArray.end() && it->getOffset() == Offset)
+      return DWARFDie(this, &*it);
+    return DWARFDie();
   }
 
   uint32_t getLineTableOffset() const {
index b4ecbf805d1e3be7869507b41571e8ed9bd8474e..bfc705a13fd2f005869f330b53507dc0c4edebc8 100644 (file)
@@ -42,6 +42,8 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cstdint>
+#include <map>
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -284,11 +286,30 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH,
                      getStringSection(), isLittleEndian());
 }
 
-bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
-  bool Success = true;
-  if (DumpType == DIDT_All || DumpType == DIDT_Info) {
+DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) {
+  parseCompileUnits();
+  if (auto *CU = CUs.getUnitForOffset(Offset))
+    return CU->getDIEForOffset(Offset);
+  return DWARFDie();
+}
+
+namespace {
+  
+class Verifier {
+  raw_ostream &OS;
+  DWARFContext &DCtx;
+public:
+  Verifier(raw_ostream &S, DWARFContext &D) : OS(S), DCtx(D) {}
+  
+  bool HandleDebugInfo() {
+    bool Success = true;
+    // A map that tracks all references (converted absolute references) so we
+    // can verify each reference points to a valid DIE and not an offset that
+    // lies between to valid DIEs.
+    std::map<uint64_t, std::set<uint32_t>> ReferenceToDIEOffsets;
+
     OS << "Verifying .debug_info...\n";
-    for (const auto &CU : compile_units()) {
+    for (const auto &CU : DCtx.compile_units()) {
       unsigned NumDies = CU->getNumDIEs();
       for (unsigned I = 0; I < NumDies; ++I) {
         auto Die = CU->getDIEAtIndex(I);
@@ -299,101 +320,141 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
           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()) {
+            case DW_AT_ranges:
+              // Make sure the offset in the DW_AT_ranges attribute is valid.
+              if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
+                if (*SectionOffset >= DCtx.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: DW_AT_ranges offset is beyond .debug_ranges "
-                      "bounds:\n";
+                OS << "error: DIE has invalid DW_AT_ranges encoding:\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()) {
+              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 >= DCtx.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: DW_AT_stmt_list offset is beyond .debug_line "
-                      "bounds: "
-                   << format("0x%08" PRIx32, *SectionOffset) << "\n";
-                CU->getUnitDIE().dump(OS, 0);
+                OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
+                Die.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;
+              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) {
+            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";
+                } else {
+                  // Valid reference, but we will verify it points to an actual
+                  // DIE later.
+                  ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset());
+                }
+              }
+              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) {
+                if(*RefVal >= DCtx.getInfoSection().Data.size()) {
+                  Success = false;
+                  OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
+                        "bounds:\n";
+                  Die.dump(OS, 0);
+                  OS << "\n";
+                } else {
+                  // Valid reference, but we will verify it points to an actual
+                  // DIE later.
+                  ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset());
+                }
+              }
+              break;
+            }
+            case DW_FORM_strp: {
+              auto SecOffset = AttrValue.Value.getAsSectionOffset();
+              assert(SecOffset); // DW_FORM_strp is a section offset.
+              if (SecOffset && *SecOffset >= DCtx.getStringSection().size()) {
                 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";
+                OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
                 Die.dump(OS, 0);
                 OS << "\n";
               }
+              break;
             }
-            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;
+            default:
+              break;
           }
         }
       }
     }
+
+    // Take all references and make sure they point to an actual DIE by
+    // getting the DIE by offset and emitting an error
+    OS << "Verifying .debug_info references...\n";
+    for (auto Pair: ReferenceToDIEOffsets) {
+      auto Die = DCtx.getDIEForOffset(Pair.first);
+      if (Die)
+        continue;
+      Success = false;
+      OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first)
+         << ". Offset is in between DIEs:\n";
+      for (auto Offset: Pair.second) {
+        auto ReferencingDie = DCtx.getDIEForOffset(Offset);
+        ReferencingDie.dump(OS, 0);
+        OS << "\n";
+      }
+      OS << "\n";
+    }
+    return Success;
+  }
+};
+  
+} // anonymous namespace
+
+bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
+  bool Success = true;
+  Verifier verifier(OS, *this);
+  if (DumpType == DIDT_All || DumpType == DIDT_Info) {
+    if (!verifier.HandleDebugInfo())
+      Success = false;
   }
   return Success;
 }
index 3f0e3dab72bdf68487a1fa10d0b7dafd2bc259dd..af1637827df060c33287097d2036d1d05f104fcf 100644 (file)
@@ -1908,4 +1908,60 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
   EXPECT_TRUE(strm.str().find(err) != std::string::npos);
 }
 
+TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) {
+  // Create a single compile unit with a single function that has a DW_AT_type
+  // with a valid .debug_info offset, but the offset is between two DIEs.
+  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:           0x0000000000000011
+          - 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: invalid DIE reference 0x00000011. Offset is in "
+      "between DIEs:";
+  EXPECT_TRUE(strm.str().find(err) != std::string::npos);
+}
+  
 } // end anonymous namespace