From c9f07b06a1f884acdb933ecfc46b2dafaff08bd9 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Fri, 6 Apr 2018 13:34:12 +0000 Subject: DWARFVerifier: validate information in name index entries Summary: This patch add checks to verify that the information in the name index entries is consistent with the debug_info section. Specifically, we check that entries point to valid DIEs, and their names, tags, and compile units match the information in the debug_info sections. These checks are only run if the previous checks did not find any errors in the name index headers. Attempting to proceed with the checks anyway would likely produce a lot of spurious errors and the verification code would need to be very careful to avoid crashing. I also add a couple of more checks to the abbreviation-validation code to verify that some attributes are always present (an index without a DW_IDX_die_offset attribute is fairly useless). The entry verification works only on indexes without any type units - I haven't attempted to extend it to type units, as we don't even have a DWARF v5-compatible type unit generator at the moment. Reviewers: JDevlieghere, aprantl, dblaikie Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D45323 llvm-svn: 329392 --- llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp | 6 +- llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp | 126 +++++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) (limited to 'llvm/lib/DebugInfo') diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index fccff271e1f..9d1d0708e08 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -601,7 +601,7 @@ Expected DWARFDebugNames::NameIndex::getEntry(uint32_t *Offset) const { const DWARFDataExtractor &AS = Section.AccelSection; if (!AS.isValidOffset(*Offset)) - return make_error("Incorrectly terminated entry list", + return make_error("Incorrectly terminated entry list.", inconvertibleErrorCode()); uint32_t AbbrevCode = AS.getULEB128(Offset); @@ -610,7 +610,7 @@ DWARFDebugNames::NameIndex::getEntry(uint32_t *Offset) const { const auto AbbrevIt = Abbrevs.find_as(AbbrevCode); if (AbbrevIt == Abbrevs.end()) - return make_error("Invalid abbreviation", + return make_error("Invalid abbreviation.", inconvertibleErrorCode()); Entry E(*this, *AbbrevIt); @@ -618,7 +618,7 @@ DWARFDebugNames::NameIndex::getEntry(uint32_t *Offset) const { dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (auto &Value : E.Values) { if (!Value.extractValue(AS, Offset, FormParams)) - return make_error("Error extracting index attribute values", + return make_error("Error extracting index attribute values.", inconvertibleErrorCode()); } return std::move(E); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index 92d956cfdd3..73aa5017888 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -1000,6 +1000,13 @@ unsigned DWARFVerifier::verifyNameIndexAttribute( unsigned DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { + if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) { + warn() << formatv("Name Index @ {0:x}: Verifying indexes of type units is " + "not currently supported.\n", + NI.getUnitOffset()); + return 0; + } + unsigned NumErrors = 0; for (const auto &Abbrev : NI.getAbbrevs()) { StringRef TagName = dwarf::TagString(Abbrev.Tag); @@ -1019,10 +1026,122 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { } NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc); } + + if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) { + error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units " + "and abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, + dwarf::DW_IDX_compile_unit); + ++NumErrors; + } + if (!Attributes.count(dwarf::DW_IDX_die_offset)) { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset); + ++NumErrors; + } } return NumErrors; } +static SmallVector getNames(const DWARFDie &DIE) { + SmallVector Result; + if (const char *Str = DIE.getName(DINameKind::ShortName)) + Result.emplace_back(Str); + else if (DIE.getTag() == dwarf::DW_TAG_namespace) + Result.emplace_back("(anonymous namespace)"); + + if (const char *Str = DIE.getName(DINameKind::LinkageName)) { + if (Result.empty() || Result[0] != Str) + Result.emplace_back(Str); + } + + return Result; +} + +unsigned +DWARFVerifier::verifyNameIndexEntries(const DWARFDebugNames::NameIndex &NI, + uint32_t Name, + const DataExtractor &StrData) { + // Verifying type unit indexes not supported. + if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) + return 0; + + DWARFDebugNames::NameTableEntry NTE = NI.getNameTableEntry(Name); + const char *CStr = StrData.getCStr(&NTE.StringOffset); + if (!CStr) { + error() << formatv( + "Name Index @ {0:x}: Unable to get string associated with name {1}.\n", + NI.getUnitOffset(), Name); + return 1; + } + StringRef Str(CStr); + + unsigned NumErrors = 0; + unsigned NumEntries = 0; + uint32_t EntryID = NTE.EntryOffset; + Expected EntryOr = NI.getEntry(&NTE.EntryOffset); + for (; EntryOr; ++NumEntries, EntryID = NTE.EntryOffset, + EntryOr = NI.getEntry(&NTE.EntryOffset)) { + uint32_t CUIndex = *EntryOr->getCUIndex(); + if (CUIndex > NI.getCUCount()) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an " + "invalid CU index ({2}).\n", + NI.getUnitOffset(), EntryID, CUIndex); + ++NumErrors; + continue; + } + uint32_t CUOffset = NI.getCUOffset(CUIndex); + uint64_t DIEOffset = *EntryOr->getDIESectionOffset(); + DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset); + if (!DIE) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a " + "non-existing DIE @ {2:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset); + ++NumErrors; + continue; + } + if (DIE.getDwarfUnit()->getOffset() != CUOffset) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of " + "DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, CUOffset, + DIE.getDwarfUnit()->getOffset()); + ++NumErrors; + } + if (DIE.getTag() != EntryOr->tag()) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of " + "DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(), + DIE.getTag()); + ++NumErrors; + } + + auto EntryNames = getNames(DIE); + if (!is_contained(EntryNames, Str)) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " + "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, Str, + make_range(EntryNames.begin(), EntryNames.end())); + } + } + handleAllErrors(EntryOr.takeError(), + [&](const DWARFDebugNames::SentinelError &) { + if (NumEntries > 0) + return; + error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is " + "not associated with any entries.\n", + NI.getUnitOffset(), Name, Str); + ++NumErrors; + }, + [&](const ErrorInfoBase &Info) { + error() << formatv( + "Name Index @ {0:x}: Name {1} ({2}): {3}\n", + NI.getUnitOffset(), Name, Str, Info.message()); + ++NumErrors; + }); + return NumErrors; +} + unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, const DataExtractor &StrData) { unsigned NumErrors = 0; @@ -1045,6 +1164,13 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, for (const auto &NI : AccelTable) NumErrors += verifyNameIndexAbbrevs(NI); + // Don't attempt Entry validation if any of the previous checks found errors + if (NumErrors > 0) + return NumErrors; + for (const auto &NI : AccelTable) + for (uint64_t Name = 1; Name <= NI.getNameCount(); ++Name) + NumErrors += verifyNameIndexEntries(NI, Name, StrData); + return NumErrors; } -- cgit v1.2.3