Index: llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFVerifier.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -11,19 +11,68 @@ #define LLVM_DEBUGINFO_DWARF_DWARFVERIFIER_H #include +#include +#include namespace llvm { class raw_ostream; +struct DWARFAttribute; class DWARFContext; +class DWARFDie; class DWARFUnit; /// A class that verifies DWARF debug information given a DWARF Context. class DWARFVerifier { raw_ostream &OS; DWARFContext &DCtx; + /// 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> ReferenceToDIEOffsets; uint32_t NumDebugInfoErrors; uint32_t NumDebugLineErrors; + /// Verifies the attribute's DWARF attribute and its value. + /// + /// This function currently checks for: + /// - DW_AT_ranges values is a valid .debug_ranges offset + /// - DW_AT_stmt_list is a valid .debug_line offset + /// + /// @param Die The DWARF DIE that owns the attribute value + /// @param AttrValue The DWARF attribute value to check + void verifyDebugInfoAttribute(DWARFDie &Die, DWARFAttribute &AttrValue); + + /// Verifies the attribute's DWARF form. + /// + /// This function currently checks for: + /// - All DW_FORM_ref values that are CU relative have valid CU offsets + /// - All DW_FORM_ref_addr values have valid .debug_info offsets + /// - All DW_FORM_strp values have valid .debug_str offsets + /// + /// @param Die The DWARF DIE that owns the attribute value + /// @param AttrValue The DWARF attribute value to check + void verifyDebugInfoForm(DWARFDie &Die, DWARFAttribute &AttrValue); + + /// Verifies the all valid references that were found when iterating through + /// all of the DIE attributes. + /// + /// This function will verify that all references point to DIEs whose DIE + /// offset matches. This helps to ensure if a DWARF link phase moved things + /// around, that it doesn't create invalid references by failing to relocate + /// CU relative and absolute references. + void veifyDebugInfoReferences(); + + /// Verify the the DW_AT_stmt_list encoding and value and ensure that no + /// compile units that have the same DW_AT_stmt_list value. + void verifyDebugLineStmtOffsets(); + + /// Verify that all of the rows in the line table are valid. + /// + /// This function currently checks for: + /// - addresses within a sequence that decrease in value + /// - invalid file indexes + void verifyDebugLineRows(); + public: DWARFVerifier(raw_ostream &S, DWARFContext &D) : OS(S), DCtx(D), NumDebugInfoErrors(0), NumDebugLineErrors(0) {} Index: llvm/trunk/lib/DebugInfo/DWARF/DWARFVerifier.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ llvm/trunk/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -23,132 +23,120 @@ using namespace dwarf; using namespace object; -bool DWARFVerifier::handleDebugInfo() { +void DWARFVerifier::verifyDebugInfoAttribute(DWARFDie &Die, + DWARFAttribute &AttrValue) { + const auto Attr = AttrValue.Attr; + 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 >= DCtx.getRangeSection().Data.size()) { + ++NumDebugInfoErrors; + OS << "error: DW_AT_ranges offset is beyond .debug_ranges " + "bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + } else { + ++NumDebugInfoErrors; + 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 >= DCtx.getLineSection().Data.size()) { + ++NumDebugInfoErrors; + OS << "error: DW_AT_stmt_list offset is beyond .debug_line " + "bounds: " + << format("0x%08" PRIx32, *SectionOffset) << "\n"; + Die.dump(OS, 0); + OS << "\n"; + } + } else { + ++NumDebugInfoErrors; + OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; - NumDebugInfoErrors = 0; - // 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> ReferenceToDIEOffsets; + default: + break; + } +} - OS << "Verifying .debug_info...\n"; - for (const auto &CU : DCtx.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 >= DCtx.getRangeSection().Data.size()) { - ++NumDebugInfoErrors; - OS << "error: DW_AT_ranges offset is beyond .debug_ranges " - "bounds:\n"; - Die.dump(OS, 0); - OS << "\n"; - } - } else { - ++NumDebugInfoErrors; - 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 >= DCtx.getLineSection().Data.size()) { - ++NumDebugInfoErrors; - 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 { - ++NumDebugInfoErrors; - 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 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) { - ++NumDebugInfoErrors; - 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 RefVal = AttrValue.Value.getAsReference(); - assert(RefVal); - if (RefVal) { - if (*RefVal >= DCtx.getInfoSection().Data.size()) { - ++NumDebugInfoErrors; - 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()) { - ++NumDebugInfoErrors; - OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n"; - Die.dump(OS, 0); - OS << "\n"; - } - break; - } - default: - break; - } +void DWARFVerifier::verifyDebugInfoForm(DWARFDie &Die, + DWARFAttribute &AttrValue) { + const auto Form = AttrValue.Value.getForm(); + 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 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) { + ++NumDebugInfoErrors; + 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 RefVal = AttrValue.Value.getAsReference(); + assert(RefVal); + if (RefVal) { + if (*RefVal >= DCtx.getInfoSection().Data.size()) { + ++NumDebugInfoErrors; + 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()) { + ++NumDebugInfoErrors; + OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; } + default: + break; + } +} +void DWARFVerifier::veifyDebugInfoReferences() { // 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"; @@ -166,62 +154,90 @@ } OS << "\n"; } +} + +bool DWARFVerifier::handleDebugInfo() { + NumDebugInfoErrors = 0; + OS << "Verifying .debug_info...\n"; + for (const auto &CU : DCtx.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()) { + verifyDebugInfoAttribute(Die, AttrValue); + verifyDebugInfoForm(Die, AttrValue); + } + } + } + veifyDebugInfoReferences(); return NumDebugInfoErrors == 0; } -bool DWARFVerifier::handleDebugLine() { +void DWARFVerifier::verifyDebugLineStmtOffsets() { std::map StmtListToDie; - NumDebugLineErrors = 0; - OS << "Verifying .debug_line...\n"; for (const auto &CU : DCtx.compile_units()) { - auto CUDie = CU->getUnitDIE(); + auto Die = CU->getUnitDIE(); // 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. - auto StmtSectionOffset = toSectionOffset(CUDie.find(DW_AT_stmt_list)); + auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list)); if (!StmtSectionOffset) continue; const uint32_t 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); + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + if (LineTableOffset < DCtx.getLineSection().Data.size()) { + if (!LineTable) { + ++NumDebugLineErrors; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + Die.dump(OS, 0); + OS << '\n'; + continue; + } + } else { + // Make sure we don't get a valid line table back if the offset is wrong. + assert(LineTable == 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 Iter = StmtListToDie.find(LineTableOffset); if (Iter != StmtListToDie.end()) { ++NumDebugLineErrors; OS << "error: two compile unit DIEs, " << format("0x%08" PRIx32, Iter->second.getOffset()) << " and " - << format("0x%08" PRIx32, CUDie.getOffset()) + << format("0x%08" PRIx32, Die.getOffset()) << ", have the same DW_AT_stmt_list section offset:\n"; Iter->second.dump(OS, 0); - CUDie.dump(OS, 0); + Die.dump(OS, 0); OS << '\n'; // Already verified this line table before, no need to do it again. continue; } - StmtListToDie[LineTableOffset] = CUDie; + StmtListToDie[LineTableOffset] = Die; + } +} +void DWARFVerifier::verifyDebugLineRows() { + for (const auto &CU : DCtx.compile_units()) { + auto Die = CU->getUnitDIE(); auto LineTable = DCtx.getLineTableForUnit(CU.get()); - if (!LineTable) { - ++NumDebugLineErrors; - OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) - << "] was not able to be parsed for CU:\n"; - CUDie.dump(OS, 0); - OS << '\n'; + // If there is no line table we will have created an error in the + // .debug_info verifier or in verifyDebugLineStmtOffsets(). + if (!LineTable) 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) { ++NumDebugLineErrors; - OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + OS << "error: .debug_line[" + << format("0x%08" PRIx32, + *toSectionOffset(Die.find(DW_AT_stmt_list))) << "] row[" << RowIndex << "] decreases in address from previous row:\n"; @@ -234,7 +250,9 @@ if (Row.File > MaxFileIndex) { ++NumDebugLineErrors; - OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + OS << "error: .debug_line[" + << format("0x%08" PRIx32, + *toSectionOffset(Die.find(DW_AT_stmt_list))) << "][" << RowIndex << "] has invalid file index " << Row.File << " (valid values are [1," << MaxFileIndex << "]):\n"; DWARFDebugLine::Row::dumpTableHeader(OS); @@ -248,5 +266,12 @@ ++RowIndex; } } +} + +bool DWARFVerifier::handleDebugLine() { + NumDebugLineErrors = 0; + OS << "Verifying .debug_line...\n"; + verifyDebugLineStmtOffsets(); + verifyDebugLineRows(); return NumDebugLineErrors == 0; }