Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
35269 views
//===- DWARFVerifier.cpp --------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"8#include "llvm/ADT/IntervalMap.h"9#include "llvm/ADT/STLExtras.h"10#include "llvm/ADT/SmallSet.h"11#include "llvm/BinaryFormat/Dwarf.h"12#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"13#include "llvm/DebugInfo/DWARF/DWARFAttribute.h"14#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"15#include "llvm/DebugInfo/DWARF/DWARFContext.h"16#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"17#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"18#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"19#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"20#include "llvm/DebugInfo/DWARF/DWARFDie.h"21#include "llvm/DebugInfo/DWARF/DWARFExpression.h"22#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"23#include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"24#include "llvm/DebugInfo/DWARF/DWARFObject.h"25#include "llvm/DebugInfo/DWARF/DWARFSection.h"26#include "llvm/DebugInfo/DWARF/DWARFUnit.h"27#include "llvm/Object/Error.h"28#include "llvm/Support/DJB.h"29#include "llvm/Support/Error.h"30#include "llvm/Support/ErrorHandling.h"31#include "llvm/Support/FileSystem.h"32#include "llvm/Support/FormatVariadic.h"33#include "llvm/Support/JSON.h"34#include "llvm/Support/WithColor.h"35#include "llvm/Support/raw_ostream.h"36#include <map>37#include <set>38#include <vector>3940using namespace llvm;41using namespace dwarf;42using namespace object;4344namespace llvm {45class DWARFDebugInfoEntry;46}4748std::optional<DWARFAddressRange>49DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) {50auto Begin = Ranges.begin();51auto End = Ranges.end();52auto Pos = std::lower_bound(Begin, End, R);5354if (Pos != End) {55DWARFAddressRange Range(*Pos);56if (Pos->merge(R))57return Range;58}59if (Pos != Begin) {60auto Iter = Pos - 1;61DWARFAddressRange Range(*Iter);62if (Iter->merge(R))63return Range;64}6566Ranges.insert(Pos, R);67return std::nullopt;68}6970DWARFVerifier::DieRangeInfo::die_range_info_iterator71DWARFVerifier::DieRangeInfo::insert(const DieRangeInfo &RI) {72if (RI.Ranges.empty())73return Children.end();7475auto End = Children.end();76auto Iter = Children.begin();77while (Iter != End) {78if (Iter->intersects(RI))79return Iter;80++Iter;81}82Children.insert(RI);83return Children.end();84}8586bool DWARFVerifier::DieRangeInfo::contains(const DieRangeInfo &RHS) const {87auto I1 = Ranges.begin(), E1 = Ranges.end();88auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end();89if (I2 == E2)90return true;9192DWARFAddressRange R = *I2;93while (I1 != E1) {94bool Covered = I1->LowPC <= R.LowPC;95if (R.LowPC == R.HighPC || (Covered && R.HighPC <= I1->HighPC)) {96if (++I2 == E2)97return true;98R = *I2;99continue;100}101if (!Covered)102return false;103if (R.LowPC < I1->HighPC)104R.LowPC = I1->HighPC;105++I1;106}107return false;108}109110bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const {111auto I1 = Ranges.begin(), E1 = Ranges.end();112auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end();113while (I1 != E1 && I2 != E2) {114if (I1->intersects(*I2))115return true;116if (I1->LowPC < I2->LowPC)117++I1;118else119++I2;120}121return false;122}123124bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData,125uint64_t *Offset, unsigned UnitIndex,126uint8_t &UnitType, bool &isUnitDWARF64) {127uint64_t AbbrOffset, Length;128uint8_t AddrSize = 0;129uint16_t Version;130bool Success = true;131132bool ValidLength = false;133bool ValidVersion = false;134bool ValidAddrSize = false;135bool ValidType = true;136bool ValidAbbrevOffset = true;137138uint64_t OffsetStart = *Offset;139DwarfFormat Format;140std::tie(Length, Format) = DebugInfoData.getInitialLength(Offset);141isUnitDWARF64 = Format == DWARF64;142Version = DebugInfoData.getU16(Offset);143144if (Version >= 5) {145UnitType = DebugInfoData.getU8(Offset);146AddrSize = DebugInfoData.getU8(Offset);147AbbrOffset = isUnitDWARF64 ? DebugInfoData.getU64(Offset) : DebugInfoData.getU32(Offset);148ValidType = dwarf::isUnitType(UnitType);149} else {150UnitType = 0;151AbbrOffset = isUnitDWARF64 ? DebugInfoData.getU64(Offset) : DebugInfoData.getU32(Offset);152AddrSize = DebugInfoData.getU8(Offset);153}154155Expected<const DWARFAbbreviationDeclarationSet *> AbbrevSetOrErr =156DCtx.getDebugAbbrev()->getAbbreviationDeclarationSet(AbbrOffset);157if (!AbbrevSetOrErr) {158ValidAbbrevOffset = false;159// FIXME: A problematic debug_abbrev section is reported below in the form160// of a `note:`. We should propagate this error there (or elsewhere) to161// avoid losing the specific problem with the debug_abbrev section.162consumeError(AbbrevSetOrErr.takeError());163}164165ValidLength = DebugInfoData.isValidOffset(OffsetStart + Length + 3);166ValidVersion = DWARFContext::isSupportedVersion(Version);167ValidAddrSize = DWARFContext::isAddressSizeSupported(AddrSize);168if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset ||169!ValidType) {170Success = false;171bool HeaderShown = false;172auto ShowHeaderOnce = [&]() {173if (!HeaderShown) {174error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n",175UnitIndex, OffsetStart);176HeaderShown = true;177}178};179if (!ValidLength)180ErrorCategory.Report(181"Unit Header Length: Unit too large for .debug_info provided", [&]() {182ShowHeaderOnce();183note() << "The length for this unit is too "184"large for the .debug_info provided.\n";185});186if (!ValidVersion)187ErrorCategory.Report(188"Unit Header Length: 16 bit unit header version is not valid", [&]() {189ShowHeaderOnce();190note() << "The 16 bit unit header version is not valid.\n";191});192if (!ValidType)193ErrorCategory.Report(194"Unit Header Length: Unit type encoding is not valid", [&]() {195ShowHeaderOnce();196note() << "The unit type encoding is not valid.\n";197});198if (!ValidAbbrevOffset)199ErrorCategory.Report(200"Unit Header Length: Offset into the .debug_abbrev section is not "201"valid",202[&]() {203ShowHeaderOnce();204note() << "The offset into the .debug_abbrev section is "205"not valid.\n";206});207if (!ValidAddrSize)208ErrorCategory.Report("Unit Header Length: Address size is unsupported",209[&]() {210ShowHeaderOnce();211note() << "The address size is unsupported.\n";212});213}214*Offset = OffsetStart + Length + (isUnitDWARF64 ? 12 : 4);215return Success;216}217218bool DWARFVerifier::verifyName(const DWARFDie &Die) {219// FIXME Add some kind of record of which DIE names have already failed and220// don't bother checking a DIE that uses an already failed DIE.221222std::string ReconstructedName;223raw_string_ostream OS(ReconstructedName);224std::string OriginalFullName;225Die.getFullName(OS, &OriginalFullName);226OS.flush();227if (OriginalFullName.empty() || OriginalFullName == ReconstructedName)228return false;229230ErrorCategory.Report(231"Simplified template DW_AT_name could not be reconstituted", [&]() {232error()233<< "Simplified template DW_AT_name could not be reconstituted:\n"234<< formatv(" original: {0}\n"235" reconstituted: {1}\n",236OriginalFullName, ReconstructedName);237dump(Die) << '\n';238dump(Die.getDwarfUnit()->getUnitDIE()) << '\n';239});240return true;241}242243unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit,244ReferenceMap &UnitLocalReferences,245ReferenceMap &CrossUnitReferences) {246unsigned NumUnitErrors = 0;247unsigned NumDies = Unit.getNumDIEs();248for (unsigned I = 0; I < NumDies; ++I) {249auto Die = Unit.getDIEAtIndex(I);250251if (Die.getTag() == DW_TAG_null)252continue;253254for (auto AttrValue : Die.attributes()) {255NumUnitErrors += verifyDebugInfoAttribute(Die, AttrValue);256NumUnitErrors += verifyDebugInfoForm(Die, AttrValue, UnitLocalReferences,257CrossUnitReferences);258}259260NumUnitErrors += verifyName(Die);261262if (Die.hasChildren()) {263if (Die.getFirstChild().isValid() &&264Die.getFirstChild().getTag() == DW_TAG_null) {265warn() << dwarf::TagString(Die.getTag())266<< " has DW_CHILDREN_yes but DIE has no children: ";267Die.dump(OS);268}269}270271NumUnitErrors += verifyDebugInfoCallSite(Die);272}273274DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false);275if (!Die) {276ErrorCategory.Report("Compilation unit missing DIE", [&]() {277error() << "Compilation unit without DIE.\n";278});279NumUnitErrors++;280return NumUnitErrors;281}282283if (!dwarf::isUnitType(Die.getTag())) {284ErrorCategory.Report("Compilation unit root DIE is not a unit DIE", [&]() {285error() << "Compilation unit root DIE is not a unit DIE: "286<< dwarf::TagString(Die.getTag()) << ".\n";287});288NumUnitErrors++;289}290291uint8_t UnitType = Unit.getUnitType();292if (!DWARFUnit::isMatchingUnitTypeAndTag(UnitType, Die.getTag())) {293ErrorCategory.Report("Mismatched unit type", [&]() {294error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType)295<< ") and root DIE (" << dwarf::TagString(Die.getTag())296<< ") do not match.\n";297});298NumUnitErrors++;299}300301// According to DWARF Debugging Information Format Version 5,302// 3.1.2 Skeleton Compilation Unit Entries:303// "A skeleton compilation unit has no children."304if (Die.getTag() == dwarf::DW_TAG_skeleton_unit && Die.hasChildren()) {305ErrorCategory.Report("Skeleton CU has children", [&]() {306error() << "Skeleton compilation unit has children.\n";307});308NumUnitErrors++;309}310311DieRangeInfo RI;312NumUnitErrors += verifyDieRanges(Die, RI);313314return NumUnitErrors;315}316317unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) {318if (Die.getTag() != DW_TAG_call_site && Die.getTag() != DW_TAG_GNU_call_site)319return 0;320321DWARFDie Curr = Die.getParent();322for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) {323if (Curr.getTag() == DW_TAG_inlined_subroutine) {324ErrorCategory.Report(325"Call site nested entry within inlined subroutine", [&]() {326error() << "Call site entry nested within inlined subroutine:";327Curr.dump(OS);328});329return 1;330}331}332333if (!Curr.isValid()) {334ErrorCategory.Report(335"Call site entry not nested within valid subprogram", [&]() {336error() << "Call site entry not nested within a valid subprogram:";337Die.dump(OS);338});339return 1;340}341342std::optional<DWARFFormValue> CallAttr = Curr.find(343{DW_AT_call_all_calls, DW_AT_call_all_source_calls,344DW_AT_call_all_tail_calls, DW_AT_GNU_all_call_sites,345DW_AT_GNU_all_source_call_sites, DW_AT_GNU_all_tail_call_sites});346if (!CallAttr) {347ErrorCategory.Report(348"Subprogram with call site entry has no DW_AT_call attribute", [&]() {349error()350<< "Subprogram with call site entry has no DW_AT_call attribute:";351Curr.dump(OS);352Die.dump(OS, /*indent*/ 1);353});354return 1;355}356357return 0;358}359360unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) {361if (!Abbrev)362return 0;363364Expected<const DWARFAbbreviationDeclarationSet *> AbbrDeclsOrErr =365Abbrev->getAbbreviationDeclarationSet(0);366if (!AbbrDeclsOrErr) {367std::string ErrMsg = toString(AbbrDeclsOrErr.takeError());368ErrorCategory.Report("Abbreviation Declaration error",369[&]() { error() << ErrMsg << "\n"; });370return 1;371}372373const auto *AbbrDecls = *AbbrDeclsOrErr;374unsigned NumErrors = 0;375for (auto AbbrDecl : *AbbrDecls) {376SmallDenseSet<uint16_t> AttributeSet;377for (auto Attribute : AbbrDecl.attributes()) {378auto Result = AttributeSet.insert(Attribute.Attr);379if (!Result.second) {380ErrorCategory.Report(381"Abbreviation declartion contains multiple attributes", [&]() {382error() << "Abbreviation declaration contains multiple "383<< AttributeString(Attribute.Attr) << " attributes.\n";384AbbrDecl.dump(OS);385});386++NumErrors;387}388}389}390return NumErrors;391}392393bool DWARFVerifier::handleDebugAbbrev() {394OS << "Verifying .debug_abbrev...\n";395396const DWARFObject &DObj = DCtx.getDWARFObj();397unsigned NumErrors = 0;398if (!DObj.getAbbrevSection().empty())399NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrev());400if (!DObj.getAbbrevDWOSection().empty())401NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrevDWO());402403return NumErrors == 0;404}405406unsigned DWARFVerifier::verifyUnits(const DWARFUnitVector &Units) {407unsigned NumDebugInfoErrors = 0;408ReferenceMap CrossUnitReferences;409410unsigned Index = 1;411for (const auto &Unit : Units) {412OS << "Verifying unit: " << Index << " / " << Units.getNumUnits();413if (const char* Name = Unit->getUnitDIE(true).getShortName())414OS << ", \"" << Name << '\"';415OS << '\n';416OS.flush();417ReferenceMap UnitLocalReferences;418NumDebugInfoErrors +=419verifyUnitContents(*Unit, UnitLocalReferences, CrossUnitReferences);420NumDebugInfoErrors += verifyDebugInfoReferences(421UnitLocalReferences, [&](uint64_t Offset) { return Unit.get(); });422++Index;423}424425NumDebugInfoErrors += verifyDebugInfoReferences(426CrossUnitReferences, [&](uint64_t Offset) -> DWARFUnit * {427if (DWARFUnit *U = Units.getUnitForOffset(Offset))428return U;429return nullptr;430});431432return NumDebugInfoErrors;433}434435unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S) {436const DWARFObject &DObj = DCtx.getDWARFObj();437DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0);438unsigned NumDebugInfoErrors = 0;439uint64_t Offset = 0, UnitIdx = 0;440uint8_t UnitType = 0;441bool isUnitDWARF64 = false;442bool isHeaderChainValid = true;443bool hasDIE = DebugInfoData.isValidOffset(Offset);444DWARFUnitVector TypeUnitVector;445DWARFUnitVector CompileUnitVector;446/// A map that tracks all references (converted absolute references) so we447/// can verify each reference points to a valid DIE and not an offset that448/// lies between to valid DIEs.449ReferenceMap CrossUnitReferences;450while (hasDIE) {451if (!verifyUnitHeader(DebugInfoData, &Offset, UnitIdx, UnitType,452isUnitDWARF64)) {453isHeaderChainValid = false;454if (isUnitDWARF64)455break;456}457hasDIE = DebugInfoData.isValidOffset(Offset);458++UnitIdx;459}460if (UnitIdx == 0 && !hasDIE) {461warn() << "Section is empty.\n";462isHeaderChainValid = true;463}464if (!isHeaderChainValid)465++NumDebugInfoErrors;466return NumDebugInfoErrors;467}468469unsigned DWARFVerifier::verifyIndex(StringRef Name,470DWARFSectionKind InfoColumnKind,471StringRef IndexStr) {472if (IndexStr.empty())473return 0;474OS << "Verifying " << Name << "...\n";475DWARFUnitIndex Index(InfoColumnKind);476DataExtractor D(IndexStr, DCtx.isLittleEndian(), 0);477if (!Index.parse(D))478return 1;479using MapType = IntervalMap<uint64_t, uint64_t>;480MapType::Allocator Alloc;481std::vector<std::unique_ptr<MapType>> Sections(Index.getColumnKinds().size());482for (const DWARFUnitIndex::Entry &E : Index.getRows()) {483uint64_t Sig = E.getSignature();484if (!E.getContributions())485continue;486for (auto E : enumerate(487InfoColumnKind == DW_SECT_INFO488? ArrayRef(E.getContributions(), Index.getColumnKinds().size())489: ArrayRef(E.getContribution(), 1))) {490const DWARFUnitIndex::Entry::SectionContribution &SC = E.value();491int Col = E.index();492if (SC.getLength() == 0)493continue;494if (!Sections[Col])495Sections[Col] = std::make_unique<MapType>(Alloc);496auto &M = *Sections[Col];497auto I = M.find(SC.getOffset());498if (I != M.end() && I.start() < (SC.getOffset() + SC.getLength())) {499StringRef Category = InfoColumnKind == DWARFSectionKind::DW_SECT_INFO500? "Overlapping CU index entries"501: "Overlapping TU index entries";502ErrorCategory.Report(Category, [&]() {503error() << llvm::formatv(504"overlapping index entries for entries {0:x16} "505"and {1:x16} for column {2}\n",506*I, Sig, toString(Index.getColumnKinds()[Col]));507});508return 1;509}510M.insert(SC.getOffset(), SC.getOffset() + SC.getLength() - 1, Sig);511}512}513514return 0;515}516517bool DWARFVerifier::handleDebugCUIndex() {518return verifyIndex(".debug_cu_index", DWARFSectionKind::DW_SECT_INFO,519DCtx.getDWARFObj().getCUIndexSection()) == 0;520}521522bool DWARFVerifier::handleDebugTUIndex() {523return verifyIndex(".debug_tu_index", DWARFSectionKind::DW_SECT_EXT_TYPES,524DCtx.getDWARFObj().getTUIndexSection()) == 0;525}526527bool DWARFVerifier::handleDebugInfo() {528const DWARFObject &DObj = DCtx.getDWARFObj();529unsigned NumErrors = 0;530531OS << "Verifying .debug_info Unit Header Chain...\n";532DObj.forEachInfoSections([&](const DWARFSection &S) {533NumErrors += verifyUnitSection(S);534});535536OS << "Verifying .debug_types Unit Header Chain...\n";537DObj.forEachTypesSections([&](const DWARFSection &S) {538NumErrors += verifyUnitSection(S);539});540541OS << "Verifying non-dwo Units...\n";542NumErrors += verifyUnits(DCtx.getNormalUnitsVector());543544OS << "Verifying dwo Units...\n";545NumErrors += verifyUnits(DCtx.getDWOUnitsVector());546return NumErrors == 0;547}548549unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,550DieRangeInfo &ParentRI) {551unsigned NumErrors = 0;552553if (!Die.isValid())554return NumErrors;555556DWARFUnit *Unit = Die.getDwarfUnit();557558auto RangesOrError = Die.getAddressRanges();559if (!RangesOrError) {560// FIXME: Report the error.561if (!Unit->isDWOUnit())562++NumErrors;563llvm::consumeError(RangesOrError.takeError());564return NumErrors;565}566567const DWARFAddressRangesVector &Ranges = RangesOrError.get();568// Build RI for this DIE and check that ranges within this DIE do not569// overlap.570DieRangeInfo RI(Die);571572// TODO support object files better573//574// Some object file formats (i.e. non-MachO) support COMDAT. ELF in575// particular does so by placing each function into a section. The DWARF data576// for the function at that point uses a section relative DW_FORM_addrp for577// the DW_AT_low_pc and a DW_FORM_data4 for the offset as the DW_AT_high_pc.578// In such a case, when the Die is the CU, the ranges will overlap, and we579// will flag valid conflicting ranges as invalid.580//581// For such targets, we should read the ranges from the CU and partition them582// by the section id. The ranges within a particular section should be583// disjoint, although the ranges across sections may overlap. We would map584// the child die to the entity that it references and the section with which585// it is associated. The child would then be checked against the range586// information for the associated section.587//588// For now, simply elide the range verification for the CU DIEs if we are589// processing an object file.590591if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) {592bool DumpDieAfterError = false;593for (const auto &Range : Ranges) {594if (!Range.valid()) {595++NumErrors;596ErrorCategory.Report("Invalid address range", [&]() {597error() << "Invalid address range " << Range << "\n";598DumpDieAfterError = true;599});600continue;601}602603// Verify that ranges don't intersect and also build up the DieRangeInfo604// address ranges. Don't break out of the loop below early, or we will605// think this DIE doesn't have all of the address ranges it is supposed606// to have. Compile units often have DW_AT_ranges that can contain one or607// more dead stripped address ranges which tend to all be at the same608// address: 0 or -1.609if (auto PrevRange = RI.insert(Range)) {610++NumErrors;611ErrorCategory.Report("DIE has overlapping DW_AT_ranges", [&]() {612error() << "DIE has overlapping ranges in DW_AT_ranges attribute: "613<< *PrevRange << " and " << Range << '\n';614DumpDieAfterError = true;615});616}617}618if (DumpDieAfterError)619dump(Die, 2) << '\n';620}621622// Verify that children don't intersect.623const auto IntersectingChild = ParentRI.insert(RI);624if (IntersectingChild != ParentRI.Children.end()) {625++NumErrors;626ErrorCategory.Report("DIEs have overlapping address ranges", [&]() {627error() << "DIEs have overlapping address ranges:";628dump(Die);629dump(IntersectingChild->Die) << '\n';630});631}632633// Verify that ranges are contained within their parent.634bool ShouldBeContained = !RI.Ranges.empty() && !ParentRI.Ranges.empty() &&635!(Die.getTag() == DW_TAG_subprogram &&636ParentRI.Die.getTag() == DW_TAG_subprogram);637if (ShouldBeContained && !ParentRI.contains(RI)) {638++NumErrors;639ErrorCategory.Report(640"DIE address ranges are not contained by parent ranges", [&]() {641error()642<< "DIE address ranges are not contained in its parent's ranges:";643dump(ParentRI.Die);644dump(Die, 2) << '\n';645});646}647648// Recursively check children.649for (DWARFDie Child : Die)650NumErrors += verifyDieRanges(Child, RI);651652return NumErrors;653}654655unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,656DWARFAttribute &AttrValue) {657unsigned NumErrors = 0;658auto ReportError = [&](StringRef category, const Twine &TitleMsg) {659++NumErrors;660ErrorCategory.Report(category, [&]() {661error() << TitleMsg << '\n';662dump(Die) << '\n';663});664};665666const DWARFObject &DObj = DCtx.getDWARFObj();667DWARFUnit *U = Die.getDwarfUnit();668const auto Attr = AttrValue.Attr;669switch (Attr) {670case DW_AT_ranges:671// Make sure the offset in the DW_AT_ranges attribute is valid.672if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {673unsigned DwarfVersion = U->getVersion();674const DWARFSection &RangeSection = DwarfVersion < 5675? DObj.getRangesSection()676: DObj.getRnglistsSection();677if (U->isDWOUnit() && RangeSection.Data.empty())678break;679if (*SectionOffset >= RangeSection.Data.size())680ReportError("DW_AT_ranges offset out of bounds",681"DW_AT_ranges offset is beyond " +682StringRef(DwarfVersion < 5 ? ".debug_ranges"683: ".debug_rnglists") +684" bounds: " + llvm::formatv("{0:x8}", *SectionOffset));685break;686}687ReportError("Invalid DW_AT_ranges encoding",688"DIE has invalid DW_AT_ranges encoding:");689break;690case DW_AT_stmt_list:691// Make sure the offset in the DW_AT_stmt_list attribute is valid.692if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {693if (*SectionOffset >= U->getLineSection().Data.size())694ReportError("DW_AT_stmt_list offset out of bounds",695"DW_AT_stmt_list offset is beyond .debug_line bounds: " +696llvm::formatv("{0:x8}", *SectionOffset));697break;698}699ReportError("Invalid DW_AT_stmt_list encoding",700"DIE has invalid DW_AT_stmt_list encoding:");701break;702case DW_AT_location: {703// FIXME: It might be nice if there's a way to walk location expressions704// without trying to resolve the address ranges - it'd be a more efficient705// API (since the API is currently unnecessarily resolving addresses for706// this use case which only wants to validate the expressions themselves) &707// then the expressions could be validated even if the addresses can't be708// resolved.709// That sort of API would probably look like a callback "for each710// expression" with some way to lazily resolve the address ranges when711// needed (& then the existing API used here could be built on top of that -712// using the callback API to build the data structure and return it).713if (Expected<std::vector<DWARFLocationExpression>> Loc =714Die.getLocations(DW_AT_location)) {715for (const auto &Entry : *Loc) {716DataExtractor Data(toStringRef(Entry.Expr), DCtx.isLittleEndian(), 0);717DWARFExpression Expression(Data, U->getAddressByteSize(),718U->getFormParams().Format);719bool Error =720any_of(Expression, [](const DWARFExpression::Operation &Op) {721return Op.isError();722});723if (Error || !Expression.verify(U))724ReportError("Invalid DWARF expressions",725"DIE contains invalid DWARF expression:");726}727} else if (Error Err = handleErrors(728Loc.takeError(), [&](std::unique_ptr<ResolverError> E) {729return U->isDWOUnit() ? Error::success()730: Error(std::move(E));731}))732ReportError("Invalid DW_AT_location", toString(std::move(Err)));733break;734}735case DW_AT_specification:736case DW_AT_abstract_origin: {737if (auto ReferencedDie = Die.getAttributeValueAsReferencedDie(Attr)) {738auto DieTag = Die.getTag();739auto RefTag = ReferencedDie.getTag();740if (DieTag == RefTag)741break;742if (DieTag == DW_TAG_inlined_subroutine && RefTag == DW_TAG_subprogram)743break;744if (DieTag == DW_TAG_variable && RefTag == DW_TAG_member)745break;746// This might be reference to a function declaration.747if (DieTag == DW_TAG_GNU_call_site && RefTag == DW_TAG_subprogram)748break;749ReportError("Incompatible DW_AT_abstract_origin tag reference",750"DIE with tag " + TagString(DieTag) + " has " +751AttributeString(Attr) +752" that points to DIE with "753"incompatible tag " +754TagString(RefTag));755}756break;757}758case DW_AT_type: {759DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type);760if (TypeDie && !isType(TypeDie.getTag())) {761ReportError("Incompatible DW_AT_type attribute tag",762"DIE has " + AttributeString(Attr) +763" with incompatible tag " + TagString(TypeDie.getTag()));764}765break;766}767case DW_AT_call_file:768case DW_AT_decl_file: {769if (auto FileIdx = AttrValue.Value.getAsUnsignedConstant()) {770if (U->isDWOUnit() && !U->isTypeUnit())771break;772const auto *LT = U->getContext().getLineTableForUnit(U);773if (LT) {774if (!LT->hasFileAtIndex(*FileIdx)) {775bool IsZeroIndexed = LT->Prologue.getVersion() >= 5;776if (std::optional<uint64_t> LastFileIdx =777LT->getLastValidFileIndex()) {778ReportError("Invalid file index in DW_AT_decl_file",779"DIE has " + AttributeString(Attr) +780" with an invalid file index " +781llvm::formatv("{0}", *FileIdx) +782" (valid values are [" +783(IsZeroIndexed ? "0-" : "1-") +784llvm::formatv("{0}", *LastFileIdx) + "])");785} else {786ReportError("Invalid file index in DW_AT_decl_file",787"DIE has " + AttributeString(Attr) +788" with an invalid file index " +789llvm::formatv("{0}", *FileIdx) +790" (the file table in the prologue is empty)");791}792}793} else {794ReportError(795"File index in DW_AT_decl_file reference CU with no line table",796"DIE has " + AttributeString(Attr) +797" that references a file with index " +798llvm::formatv("{0}", *FileIdx) +799" and the compile unit has no line table");800}801} else {802ReportError("Invalid encoding in DW_AT_decl_file",803"DIE has " + AttributeString(Attr) +804" with invalid encoding");805}806break;807}808case DW_AT_call_line:809case DW_AT_decl_line: {810if (!AttrValue.Value.getAsUnsignedConstant()) {811ReportError(812Attr == DW_AT_call_line ? "Invalid file index in DW_AT_decl_line"813: "Invalid file index in DW_AT_call_line",814"DIE has " + AttributeString(Attr) + " with invalid encoding");815}816break;817}818default:819break;820}821return NumErrors;822}823824unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,825DWARFAttribute &AttrValue,826ReferenceMap &LocalReferences,827ReferenceMap &CrossUnitReferences) {828auto DieCU = Die.getDwarfUnit();829unsigned NumErrors = 0;830const auto Form = AttrValue.Value.getForm();831switch (Form) {832case DW_FORM_ref1:833case DW_FORM_ref2:834case DW_FORM_ref4:835case DW_FORM_ref8:836case DW_FORM_ref_udata: {837// Verify all CU relative references are valid CU offsets.838std::optional<uint64_t> RefVal = AttrValue.Value.getAsRelativeReference();839assert(RefVal);840if (RefVal) {841auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();842auto CUOffset = AttrValue.Value.getRawUValue();843if (CUOffset >= CUSize) {844++NumErrors;845ErrorCategory.Report("Invalid CU offset", [&]() {846error() << FormEncodingString(Form) << " CU offset "847<< format("0x%08" PRIx64, CUOffset)848<< " is invalid (must be less than CU size of "849<< format("0x%08" PRIx64, CUSize) << "):\n";850Die.dump(OS, 0, DumpOpts);851dump(Die) << '\n';852});853} else {854// Valid reference, but we will verify it points to an actual855// DIE later.856LocalReferences[AttrValue.Value.getUnit()->getOffset() + *RefVal]857.insert(Die.getOffset());858}859}860break;861}862case DW_FORM_ref_addr: {863// Verify all absolute DIE references have valid offsets in the864// .debug_info section.865std::optional<uint64_t> RefVal = AttrValue.Value.getAsDebugInfoReference();866assert(RefVal);867if (RefVal) {868if (*RefVal >= DieCU->getInfoSection().Data.size()) {869++NumErrors;870ErrorCategory.Report("DW_FORM_ref_addr offset out of bounds", [&]() {871error() << "DW_FORM_ref_addr offset beyond .debug_info "872"bounds:\n";873dump(Die) << '\n';874});875} else {876// Valid reference, but we will verify it points to an actual877// DIE later.878CrossUnitReferences[*RefVal].insert(Die.getOffset());879}880}881break;882}883case DW_FORM_strp:884case DW_FORM_strx:885case DW_FORM_strx1:886case DW_FORM_strx2:887case DW_FORM_strx3:888case DW_FORM_strx4:889case DW_FORM_line_strp: {890if (Error E = AttrValue.Value.getAsCString().takeError()) {891++NumErrors;892std::string ErrMsg = toString(std::move(E));893ErrorCategory.Report("Invalid DW_FORM attribute", [&]() {894error() << ErrMsg << ":\n";895dump(Die) << '\n';896});897}898break;899}900default:901break;902}903return NumErrors;904}905906unsigned DWARFVerifier::verifyDebugInfoReferences(907const ReferenceMap &References,908llvm::function_ref<DWARFUnit *(uint64_t)> GetUnitForOffset) {909auto GetDIEForOffset = [&](uint64_t Offset) {910if (DWARFUnit *U = GetUnitForOffset(Offset))911return U->getDIEForOffset(Offset);912return DWARFDie();913};914unsigned NumErrors = 0;915for (const std::pair<const uint64_t, std::set<uint64_t>> &Pair :916References) {917if (GetDIEForOffset(Pair.first))918continue;919++NumErrors;920ErrorCategory.Report("Invalid DIE reference", [&]() {921error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first)922<< ". Offset is in between DIEs:\n";923for (auto Offset : Pair.second)924dump(GetDIEForOffset(Offset)) << '\n';925OS << "\n";926});927}928return NumErrors;929}930931void DWARFVerifier::verifyDebugLineStmtOffsets() {932std::map<uint64_t, DWARFDie> StmtListToDie;933for (const auto &CU : DCtx.compile_units()) {934auto Die = CU->getUnitDIE();935// Get the attribute value as a section offset. No need to produce an936// error here if the encoding isn't correct because we validate this in937// the .debug_info verifier.938auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list));939if (!StmtSectionOffset)940continue;941const uint64_t LineTableOffset = *StmtSectionOffset;942auto LineTable = DCtx.getLineTableForUnit(CU.get());943if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) {944if (!LineTable) {945++NumDebugLineErrors;946ErrorCategory.Report("Unparsable .debug_line entry", [&]() {947error() << ".debug_line[" << format("0x%08" PRIx64, LineTableOffset)948<< "] was not able to be parsed for CU:\n";949dump(Die) << '\n';950});951continue;952}953} else {954// Make sure we don't get a valid line table back if the offset is wrong.955assert(LineTable == nullptr);956// Skip this line table as it isn't valid. No need to create an error957// here because we validate this in the .debug_info verifier.958continue;959}960auto Iter = StmtListToDie.find(LineTableOffset);961if (Iter != StmtListToDie.end()) {962++NumDebugLineErrors;963ErrorCategory.Report("Identical DW_AT_stmt_list section offset", [&]() {964error() << "two compile unit DIEs, "965<< format("0x%08" PRIx64, Iter->second.getOffset()) << " and "966<< format("0x%08" PRIx64, Die.getOffset())967<< ", have the same DW_AT_stmt_list section offset:\n";968dump(Iter->second);969dump(Die) << '\n';970});971// Already verified this line table before, no need to do it again.972continue;973}974StmtListToDie[LineTableOffset] = Die;975}976}977978void DWARFVerifier::verifyDebugLineRows() {979for (const auto &CU : DCtx.compile_units()) {980auto Die = CU->getUnitDIE();981auto LineTable = DCtx.getLineTableForUnit(CU.get());982// If there is no line table we will have created an error in the983// .debug_info verifier or in verifyDebugLineStmtOffsets().984if (!LineTable)985continue;986987// Verify prologue.988bool isDWARF5 = LineTable->Prologue.getVersion() >= 5;989uint32_t MaxDirIndex = LineTable->Prologue.IncludeDirectories.size();990uint32_t MinFileIndex = isDWARF5 ? 0 : 1;991uint32_t FileIndex = MinFileIndex;992StringMap<uint16_t> FullPathMap;993for (const auto &FileName : LineTable->Prologue.FileNames) {994// Verify directory index.995if (FileName.DirIdx > MaxDirIndex) {996++NumDebugLineErrors;997ErrorCategory.Report(998"Invalid index in .debug_line->prologue.file_names->dir_idx",999[&]() {1000error() << ".debug_line["1001<< format("0x%08" PRIx64,1002*toSectionOffset(Die.find(DW_AT_stmt_list)))1003<< "].prologue.file_names[" << FileIndex1004<< "].dir_idx contains an invalid index: "1005<< FileName.DirIdx << "\n";1006});1007}10081009// Check file paths for duplicates.1010std::string FullPath;1011const bool HasFullPath = LineTable->getFileNameByIndex(1012FileIndex, CU->getCompilationDir(),1013DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FullPath);1014assert(HasFullPath && "Invalid index?");1015(void)HasFullPath;1016auto It = FullPathMap.find(FullPath);1017if (It == FullPathMap.end())1018FullPathMap[FullPath] = FileIndex;1019else if (It->second != FileIndex && DumpOpts.Verbose) {1020warn() << ".debug_line["1021<< format("0x%08" PRIx64,1022*toSectionOffset(Die.find(DW_AT_stmt_list)))1023<< "].prologue.file_names[" << FileIndex1024<< "] is a duplicate of file_names[" << It->second << "]\n";1025}10261027FileIndex++;1028}10291030// Nothing to verify in a line table with a single row containing the end1031// sequence.1032if (LineTable->Rows.size() == 1 && LineTable->Rows.front().EndSequence)1033continue;10341035// Verify rows.1036uint64_t PrevAddress = 0;1037uint32_t RowIndex = 0;1038for (const auto &Row : LineTable->Rows) {1039// Verify row address.1040if (Row.Address.Address < PrevAddress) {1041++NumDebugLineErrors;1042ErrorCategory.Report(1043"decreasing address between debug_line rows", [&]() {1044error() << ".debug_line["1045<< format("0x%08" PRIx64,1046*toSectionOffset(Die.find(DW_AT_stmt_list)))1047<< "] row[" << RowIndex1048<< "] decreases in address from previous row:\n";10491050DWARFDebugLine::Row::dumpTableHeader(OS, 0);1051if (RowIndex > 0)1052LineTable->Rows[RowIndex - 1].dump(OS);1053Row.dump(OS);1054OS << '\n';1055});1056}10571058if (!LineTable->hasFileAtIndex(Row.File)) {1059++NumDebugLineErrors;1060ErrorCategory.Report("Invalid file index in debug_line", [&]() {1061error() << ".debug_line["1062<< format("0x%08" PRIx64,1063*toSectionOffset(Die.find(DW_AT_stmt_list)))1064<< "][" << RowIndex << "] has invalid file index " << Row.File1065<< " (valid values are [" << MinFileIndex << ','1066<< LineTable->Prologue.FileNames.size()1067<< (isDWARF5 ? ")" : "]") << "):\n";1068DWARFDebugLine::Row::dumpTableHeader(OS, 0);1069Row.dump(OS);1070OS << '\n';1071});1072}1073if (Row.EndSequence)1074PrevAddress = 0;1075else1076PrevAddress = Row.Address.Address;1077++RowIndex;1078}1079}1080}10811082DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D,1083DIDumpOptions DumpOpts)1084: OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false),1085IsMachOObject(false) {1086ErrorCategory.ShowDetail(this->DumpOpts.Verbose ||1087!this->DumpOpts.ShowAggregateErrors);1088if (const auto *F = DCtx.getDWARFObj().getFile()) {1089IsObjectFile = F->isRelocatableObject();1090IsMachOObject = F->isMachO();1091}1092}10931094bool DWARFVerifier::handleDebugLine() {1095NumDebugLineErrors = 0;1096OS << "Verifying .debug_line...\n";1097verifyDebugLineStmtOffsets();1098verifyDebugLineRows();1099return NumDebugLineErrors == 0;1100}11011102unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,1103DataExtractor *StrData,1104const char *SectionName) {1105unsigned NumErrors = 0;1106DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection,1107DCtx.isLittleEndian(), 0);1108AppleAcceleratorTable AccelTable(AccelSectionData, *StrData);11091110OS << "Verifying " << SectionName << "...\n";11111112// Verify that the fixed part of the header is not too short.1113if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) {1114ErrorCategory.Report("Section is too small to fit a section header", [&]() {1115error() << "Section is too small to fit a section header.\n";1116});1117return 1;1118}11191120// Verify that the section is not too short.1121if (Error E = AccelTable.extract()) {1122std::string Msg = toString(std::move(E));1123ErrorCategory.Report("Section is too small to fit a section header",1124[&]() { error() << Msg << '\n'; });1125return 1;1126}11271128// Verify that all buckets have a valid hash index or are empty.1129uint32_t NumBuckets = AccelTable.getNumBuckets();1130uint32_t NumHashes = AccelTable.getNumHashes();11311132uint64_t BucketsOffset =1133AccelTable.getSizeHdr() + AccelTable.getHeaderDataLength();1134uint64_t HashesBase = BucketsOffset + NumBuckets * 4;1135uint64_t OffsetsBase = HashesBase + NumHashes * 4;1136for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) {1137uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset);1138if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) {1139ErrorCategory.Report("Invalid hash index", [&]() {1140error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx,1141HashIdx);1142});1143++NumErrors;1144}1145}1146uint32_t NumAtoms = AccelTable.getAtomsDesc().size();1147if (NumAtoms == 0) {1148ErrorCategory.Report("No atoms", [&]() {1149error() << "No atoms: failed to read HashData.\n";1150});1151return 1;1152}1153if (!AccelTable.validateForms()) {1154ErrorCategory.Report("Unsupported form", [&]() {1155error() << "Unsupported form: failed to read HashData.\n";1156});1157return 1;1158}11591160for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) {1161uint64_t HashOffset = HashesBase + 4 * HashIdx;1162uint64_t DataOffset = OffsetsBase + 4 * HashIdx;1163uint32_t Hash = AccelSectionData.getU32(&HashOffset);1164uint64_t HashDataOffset = AccelSectionData.getU32(&DataOffset);1165if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset,1166sizeof(uint64_t))) {1167ErrorCategory.Report("Invalid HashData offset", [&]() {1168error() << format("Hash[%d] has invalid HashData offset: "1169"0x%08" PRIx64 ".\n",1170HashIdx, HashDataOffset);1171});1172++NumErrors;1173}11741175uint64_t StrpOffset;1176uint64_t StringOffset;1177uint32_t StringCount = 0;1178uint64_t Offset;1179unsigned Tag;1180while ((StrpOffset = AccelSectionData.getU32(&HashDataOffset)) != 0) {1181const uint32_t NumHashDataObjects =1182AccelSectionData.getU32(&HashDataOffset);1183for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects;1184++HashDataIdx) {1185std::tie(Offset, Tag) = AccelTable.readAtoms(&HashDataOffset);1186auto Die = DCtx.getDIEForOffset(Offset);1187if (!Die) {1188const uint32_t BucketIdx =1189NumBuckets ? (Hash % NumBuckets) : UINT32_MAX;1190StringOffset = StrpOffset;1191const char *Name = StrData->getCStr(&StringOffset);1192if (!Name)1193Name = "<NULL>";11941195ErrorCategory.Report("Invalid DIE offset", [&]() {1196error() << format(1197"%s Bucket[%d] Hash[%d] = 0x%08x "1198"Str[%u] = 0x%08" PRIx64 " DIE[%d] = 0x%08" PRIx64 " "1199"is not a valid DIE offset for \"%s\".\n",1200SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset,1201HashDataIdx, Offset, Name);1202});12031204++NumErrors;1205continue;1206}1207if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) {1208ErrorCategory.Report("Mismatched Tag in accellerator table", [&]() {1209error() << "Tag " << dwarf::TagString(Tag)1210<< " in accelerator table does not match Tag "1211<< dwarf::TagString(Die.getTag()) << " of DIE["1212<< HashDataIdx << "].\n";1213});1214++NumErrors;1215}1216}1217++StringCount;1218}1219}1220return NumErrors;1221}12221223unsigned1224DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {1225// A map from CU offset to the (first) Name Index offset which claims to index1226// this CU.1227DenseMap<uint64_t, uint64_t> CUMap;1228const uint64_t NotIndexed = std::numeric_limits<uint64_t>::max();12291230CUMap.reserve(DCtx.getNumCompileUnits());1231for (const auto &CU : DCtx.compile_units())1232CUMap[CU->getOffset()] = NotIndexed;12331234unsigned NumErrors = 0;1235for (const DWARFDebugNames::NameIndex &NI : AccelTable) {1236if (NI.getCUCount() == 0) {1237ErrorCategory.Report("Name Index doesn't index any CU", [&]() {1238error() << formatv("Name Index @ {0:x} does not index any CU\n",1239NI.getUnitOffset());1240});1241++NumErrors;1242continue;1243}1244for (uint32_t CU = 0, End = NI.getCUCount(); CU < End; ++CU) {1245uint64_t Offset = NI.getCUOffset(CU);1246auto Iter = CUMap.find(Offset);12471248if (Iter == CUMap.end()) {1249ErrorCategory.Report("Name Index references non-existing CU", [&]() {1250error() << formatv(1251"Name Index @ {0:x} references a non-existing CU @ {1:x}\n",1252NI.getUnitOffset(), Offset);1253});1254++NumErrors;1255continue;1256}12571258if (Iter->second != NotIndexed) {1259ErrorCategory.Report("Duplicate Name Index", [&]() {1260error() << formatv(1261"Name Index @ {0:x} references a CU @ {1:x}, but "1262"this CU is already indexed by Name Index @ {2:x}\n",1263NI.getUnitOffset(), Offset, Iter->second);1264});1265continue;1266}1267Iter->second = NI.getUnitOffset();1268}1269}12701271for (const auto &KV : CUMap) {1272if (KV.second == NotIndexed)1273warn() << formatv("CU @ {0:x} not covered by any Name Index\n", KV.first);1274}12751276return NumErrors;1277}12781279unsigned1280DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,1281const DataExtractor &StrData) {1282struct BucketInfo {1283uint32_t Bucket;1284uint32_t Index;12851286constexpr BucketInfo(uint32_t Bucket, uint32_t Index)1287: Bucket(Bucket), Index(Index) {}1288bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; }1289};12901291uint32_t NumErrors = 0;1292if (NI.getBucketCount() == 0) {1293warn() << formatv("Name Index @ {0:x} does not contain a hash table.\n",1294NI.getUnitOffset());1295return NumErrors;1296}12971298// Build up a list of (Bucket, Index) pairs. We use this later to verify that1299// each Name is reachable from the appropriate bucket.1300std::vector<BucketInfo> BucketStarts;1301BucketStarts.reserve(NI.getBucketCount() + 1);1302for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) {1303uint32_t Index = NI.getBucketArrayEntry(Bucket);1304if (Index > NI.getNameCount()) {1305ErrorCategory.Report("Name Index Bucket contains invalid value", [&]() {1306error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid "1307"value {2}. Valid range is [0, {3}].\n",1308Bucket, NI.getUnitOffset(), Index,1309NI.getNameCount());1310});1311++NumErrors;1312continue;1313}1314if (Index > 0)1315BucketStarts.emplace_back(Bucket, Index);1316}13171318// If there were any buckets with invalid values, skip further checks as they1319// will likely produce many errors which will only confuse the actual root1320// problem.1321if (NumErrors > 0)1322return NumErrors;13231324// Sort the list in the order of increasing "Index" entries.1325array_pod_sort(BucketStarts.begin(), BucketStarts.end());13261327// Insert a sentinel entry at the end, so we can check that the end of the1328// table is covered in the loop below.1329BucketStarts.emplace_back(NI.getBucketCount(), NI.getNameCount() + 1);13301331// Loop invariant: NextUncovered is the (1-based) index of the first Name1332// which is not reachable by any of the buckets we processed so far (and1333// hasn't been reported as uncovered).1334uint32_t NextUncovered = 1;1335for (const BucketInfo &B : BucketStarts) {1336// Under normal circumstances B.Index be equal to NextUncovered, but it can1337// be less if a bucket points to names which are already known to be in some1338// bucket we processed earlier. In that case, we won't trigger this error,1339// but report the mismatched hash value error instead. (We know the hash1340// will not match because we have already verified that the name's hash1341// puts it into the previous bucket.)1342if (B.Index > NextUncovered) {1343ErrorCategory.Report("Name table entries uncovered by hash table", [&]() {1344error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] "1345"are not covered by the hash table.\n",1346NI.getUnitOffset(), NextUncovered, B.Index - 1);1347});1348++NumErrors;1349}1350uint32_t Idx = B.Index;13511352// The rest of the checks apply only to non-sentinel entries.1353if (B.Bucket == NI.getBucketCount())1354break;13551356// This triggers if a non-empty bucket points to a name with a mismatched1357// hash. Clients are likely to interpret this as an empty bucket, because a1358// mismatched hash signals the end of a bucket, but if this is indeed an1359// empty bucket, the producer should have signalled this by marking the1360// bucket as empty.1361uint32_t FirstHash = NI.getHashArrayEntry(Idx);1362if (FirstHash % NI.getBucketCount() != B.Bucket) {1363ErrorCategory.Report("Name Index point to mismatched hash value", [&]() {1364error() << formatv(1365"Name Index @ {0:x}: Bucket {1} is not empty but points to a "1366"mismatched hash value {2:x} (belonging to bucket {3}).\n",1367NI.getUnitOffset(), B.Bucket, FirstHash,1368FirstHash % NI.getBucketCount());1369});1370++NumErrors;1371}13721373// This find the end of this bucket and also verifies that all the hashes in1374// this bucket are correct by comparing the stored hashes to the ones we1375// compute ourselves.1376while (Idx <= NI.getNameCount()) {1377uint32_t Hash = NI.getHashArrayEntry(Idx);1378if (Hash % NI.getBucketCount() != B.Bucket)1379break;13801381const char *Str = NI.getNameTableEntry(Idx).getString();1382if (caseFoldingDjbHash(Str) != Hash) {1383ErrorCategory.Report(1384"String hash doesn't match Name Index hash", [&]() {1385error() << formatv(1386"Name Index @ {0:x}: String ({1}) at index {2} "1387"hashes to {3:x}, but "1388"the Name Index hash is {4:x}\n",1389NI.getUnitOffset(), Str, Idx, caseFoldingDjbHash(Str), Hash);1390});1391++NumErrors;1392}13931394++Idx;1395}1396NextUncovered = std::max(NextUncovered, Idx);1397}1398return NumErrors;1399}14001401unsigned DWARFVerifier::verifyNameIndexAttribute(1402const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr,1403DWARFDebugNames::AttributeEncoding AttrEnc) {1404StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form);1405if (FormName.empty()) {1406ErrorCategory.Report("Unknown NameIndex Abbreviation", [&]() {1407error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an "1408"unknown form: {3}.\n",1409NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,1410AttrEnc.Form);1411});1412return 1;1413}14141415if (AttrEnc.Index == DW_IDX_type_hash) {1416if (AttrEnc.Form != dwarf::DW_FORM_data8) {1417ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() {1418error() << formatv(1419"NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash "1420"uses an unexpected form {2} (should be {3}).\n",1421NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8);1422});1423return 1;1424}1425return 0;1426}14271428if (AttrEnc.Index == dwarf::DW_IDX_parent) {1429constexpr static auto AllowedForms = {dwarf::Form::DW_FORM_flag_present,1430dwarf::Form::DW_FORM_ref4};1431if (!is_contained(AllowedForms, AttrEnc.Form)) {1432ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() {1433error() << formatv(1434"NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_parent "1435"uses an unexpected form {2} (should be "1436"DW_FORM_ref4 or DW_FORM_flag_present).\n",1437NI.getUnitOffset(), Abbr.Code, AttrEnc.Form);1438});1439return 1;1440}1441return 0;1442}14431444// A list of known index attributes and their expected form classes.1445// DW_IDX_type_hash is handled specially in the check above, as it has a1446// specific form (not just a form class) we should expect.1447struct FormClassTable {1448dwarf::Index Index;1449DWARFFormValue::FormClass Class;1450StringLiteral ClassName;1451};1452static constexpr FormClassTable Table[] = {1453{dwarf::DW_IDX_compile_unit, DWARFFormValue::FC_Constant, {"constant"}},1454{dwarf::DW_IDX_type_unit, DWARFFormValue::FC_Constant, {"constant"}},1455{dwarf::DW_IDX_die_offset, DWARFFormValue::FC_Reference, {"reference"}},1456};14571458ArrayRef<FormClassTable> TableRef(Table);1459auto Iter = find_if(TableRef, [AttrEnc](const FormClassTable &T) {1460return T.Index == AttrEnc.Index;1461});1462if (Iter == TableRef.end()) {1463warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an "1464"unknown index attribute: {2}.\n",1465NI.getUnitOffset(), Abbr.Code, AttrEnc.Index);1466return 0;1467}14681469if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) {1470ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() {1471error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an "1472"unexpected form {3} (expected form class {4}).\n",1473NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,1474AttrEnc.Form, Iter->ClassName);1475});1476return 1;1477}1478return 0;1479}14801481unsigned1482DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {1483if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) {1484warn() << formatv("Name Index @ {0:x}: Verifying indexes of type units is "1485"not currently supported.\n",1486NI.getUnitOffset());1487return 0;1488}14891490unsigned NumErrors = 0;1491for (const auto &Abbrev : NI.getAbbrevs()) {1492StringRef TagName = dwarf::TagString(Abbrev.Tag);1493if (TagName.empty()) {1494warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} references an "1495"unknown tag: {2}.\n",1496NI.getUnitOffset(), Abbrev.Code, Abbrev.Tag);1497}1498SmallSet<unsigned, 5> Attributes;1499for (const auto &AttrEnc : Abbrev.Attributes) {1500if (!Attributes.insert(AttrEnc.Index).second) {1501ErrorCategory.Report(1502"NameIndex Abbreviateion contains multiple attributes", [&]() {1503error() << formatv(1504"NameIndex @ {0:x}: Abbreviation {1:x} contains "1505"multiple {2} attributes.\n",1506NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index);1507});1508++NumErrors;1509continue;1510}1511NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc);1512}15131514if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) {1515ErrorCategory.Report("Abbreviation contains no attribute", [&]() {1516error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units "1517"and abbreviation {1:x} has no {2} attribute.\n",1518NI.getUnitOffset(), Abbrev.Code,1519dwarf::DW_IDX_compile_unit);1520});1521++NumErrors;1522}1523if (!Attributes.count(dwarf::DW_IDX_die_offset)) {1524ErrorCategory.Report("Abbreviate in NameIndex missing attribute", [&]() {1525error() << formatv(1526"NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n",1527NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset);1528});1529++NumErrors;1530}1531}1532return NumErrors;1533}15341535static SmallVector<std::string, 3> getNames(const DWARFDie &DIE,1536bool IncludeStrippedTemplateNames,1537bool IncludeObjCNames = true,1538bool IncludeLinkageName = true) {1539SmallVector<std::string, 3> Result;1540if (const char *Str = DIE.getShortName()) {1541StringRef Name(Str);1542Result.emplace_back(Name);1543if (IncludeStrippedTemplateNames) {1544if (std::optional<StringRef> StrippedName =1545StripTemplateParameters(Result.back()))1546// Convert to std::string and push; emplacing the StringRef may trigger1547// a vector resize which may destroy the StringRef memory.1548Result.push_back(StrippedName->str());1549}15501551if (IncludeObjCNames) {1552if (std::optional<ObjCSelectorNames> ObjCNames =1553getObjCNamesIfSelector(Name)) {1554Result.emplace_back(ObjCNames->ClassName);1555Result.emplace_back(ObjCNames->Selector);1556if (ObjCNames->ClassNameNoCategory)1557Result.emplace_back(*ObjCNames->ClassNameNoCategory);1558if (ObjCNames->MethodNameNoCategory)1559Result.push_back(std::move(*ObjCNames->MethodNameNoCategory));1560}1561}1562} else if (DIE.getTag() == dwarf::DW_TAG_namespace)1563Result.emplace_back("(anonymous namespace)");15641565if (IncludeLinkageName) {1566if (const char *Str = DIE.getLinkageName())1567Result.emplace_back(Str);1568}15691570return Result;1571}15721573unsigned DWARFVerifier::verifyNameIndexEntries(1574const DWARFDebugNames::NameIndex &NI,1575const DWARFDebugNames::NameTableEntry &NTE) {1576// Verifying type unit indexes not supported.1577if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0)1578return 0;15791580const char *CStr = NTE.getString();1581if (!CStr) {1582ErrorCategory.Report("Unable to get string associated with name", [&]() {1583error() << formatv("Name Index @ {0:x}: Unable to get string associated "1584"with name {1}.\n",1585NI.getUnitOffset(), NTE.getIndex());1586});1587return 1;1588}1589StringRef Str(CStr);15901591unsigned NumErrors = 0;1592unsigned NumEntries = 0;1593uint64_t EntryID = NTE.getEntryOffset();1594uint64_t NextEntryID = EntryID;1595Expected<DWARFDebugNames::Entry> EntryOr = NI.getEntry(&NextEntryID);1596for (; EntryOr; ++NumEntries, EntryID = NextEntryID,1597EntryOr = NI.getEntry(&NextEntryID)) {1598uint32_t CUIndex = *EntryOr->getCUIndex();1599if (CUIndex > NI.getCUCount()) {1600ErrorCategory.Report("Name Index entry contains invalid CU index", [&]() {1601error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an "1602"invalid CU index ({2}).\n",1603NI.getUnitOffset(), EntryID, CUIndex);1604});1605++NumErrors;1606continue;1607}1608uint64_t CUOffset = NI.getCUOffset(CUIndex);1609uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset();1610DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset);1611if (!DIE) {1612ErrorCategory.Report("NameIndex references nonexistent DIE", [&]() {1613error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a "1614"non-existing DIE @ {2:x}.\n",1615NI.getUnitOffset(), EntryID, DIEOffset);1616});1617++NumErrors;1618continue;1619}1620if (DIE.getDwarfUnit()->getOffset() != CUOffset) {1621ErrorCategory.Report("Name index contains mismatched CU of DIE", [&]() {1622error() << formatv(1623"Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of "1624"DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n",1625NI.getUnitOffset(), EntryID, DIEOffset, CUOffset,1626DIE.getDwarfUnit()->getOffset());1627});1628++NumErrors;1629}1630if (DIE.getTag() != EntryOr->tag()) {1631ErrorCategory.Report("Name Index contains mismatched Tag of DIE", [&]() {1632error() << formatv(1633"Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of "1634"DIE @ {2:x}: index - {3}; debug_info - {4}.\n",1635NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(),1636DIE.getTag());1637});1638++NumErrors;1639}16401641// We allow an extra name for functions: their name without any template1642// parameters.1643auto IncludeStrippedTemplateNames =1644DIE.getTag() == DW_TAG_subprogram ||1645DIE.getTag() == DW_TAG_inlined_subroutine;1646auto EntryNames = getNames(DIE, IncludeStrippedTemplateNames);1647if (!is_contained(EntryNames, Str)) {1648ErrorCategory.Report("Name Index contains mismatched name of DIE", [&]() {1649error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name "1650"of DIE @ {2:x}: index - {3}; debug_info - {4}.\n",1651NI.getUnitOffset(), EntryID, DIEOffset, Str,1652make_range(EntryNames.begin(), EntryNames.end()));1653});1654++NumErrors;1655}1656}1657handleAllErrors(1658EntryOr.takeError(),1659[&](const DWARFDebugNames::SentinelError &) {1660if (NumEntries > 0)1661return;1662ErrorCategory.Report(1663"NameIndex Name is not associated with any entries", [&]() {1664error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is "1665"not associated with any entries.\n",1666NI.getUnitOffset(), NTE.getIndex(), Str);1667});1668++NumErrors;1669},1670[&](const ErrorInfoBase &Info) {1671ErrorCategory.Report("Uncategorized NameIndex error", [&]() {1672error() << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n",1673NI.getUnitOffset(), NTE.getIndex(), Str,1674Info.message());1675});1676++NumErrors;1677});1678return NumErrors;1679}16801681static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) {1682Expected<std::vector<DWARFLocationExpression>> Loc =1683Die.getLocations(DW_AT_location);1684if (!Loc) {1685consumeError(Loc.takeError());1686return false;1687}1688DWARFUnit *U = Die.getDwarfUnit();1689for (const auto &Entry : *Loc) {1690DataExtractor Data(toStringRef(Entry.Expr), DCtx.isLittleEndian(),1691U->getAddressByteSize());1692DWARFExpression Expression(Data, U->getAddressByteSize(),1693U->getFormParams().Format);1694bool IsInteresting =1695any_of(Expression, [](const DWARFExpression::Operation &Op) {1696return !Op.isError() && (Op.getCode() == DW_OP_addr ||1697Op.getCode() == DW_OP_form_tls_address ||1698Op.getCode() == DW_OP_GNU_push_tls_address);1699});1700if (IsInteresting)1701return true;1702}1703return false;1704}17051706unsigned DWARFVerifier::verifyNameIndexCompleteness(1707const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) {17081709// First check, if the Die should be indexed. The code follows the DWARF v51710// wording as closely as possible.17111712// "All non-defining declarations (that is, debugging information entries1713// with a DW_AT_declaration attribute) are excluded."1714if (Die.find(DW_AT_declaration))1715return 0;17161717// "DW_TAG_namespace debugging information entries without a DW_AT_name1718// attribute are included with the name “(anonymous namespace)”.1719// All other debugging information entries without a DW_AT_name attribute1720// are excluded."1721// "If a subprogram or inlined subroutine is included, and has a1722// DW_AT_linkage_name attribute, there will be an additional index entry for1723// the linkage name."1724auto IncludeLinkageName = Die.getTag() == DW_TAG_subprogram ||1725Die.getTag() == DW_TAG_inlined_subroutine;1726// We *allow* stripped template names / ObjectiveC names as extra entries into1727// the table, but we don't *require* them to pass the completeness test.1728auto IncludeStrippedTemplateNames = false;1729auto IncludeObjCNames = false;1730auto EntryNames = getNames(Die, IncludeStrippedTemplateNames,1731IncludeObjCNames, IncludeLinkageName);1732if (EntryNames.empty())1733return 0;17341735// We deviate from the specification here, which says:1736// "The name index must contain an entry for each debugging information entry1737// that defines a named subprogram, label, variable, type, or namespace,1738// subject to ..."1739// Explicitly exclude all TAGs that we know shouldn't be indexed.1740switch (Die.getTag()) {1741// Compile units and modules have names but shouldn't be indexed.1742case DW_TAG_compile_unit:1743case DW_TAG_module:1744return 0;17451746// Function and template parameters are not globally visible, so we shouldn't1747// index them.1748case DW_TAG_formal_parameter:1749case DW_TAG_template_value_parameter:1750case DW_TAG_template_type_parameter:1751case DW_TAG_GNU_template_parameter_pack:1752case DW_TAG_GNU_template_template_param:1753return 0;17541755// Object members aren't globally visible.1756case DW_TAG_member:1757return 0;17581759// According to a strict reading of the specification, enumerators should not1760// be indexed (and LLVM currently does not do that). However, this causes1761// problems for the debuggers, so we may need to reconsider this.1762case DW_TAG_enumerator:1763return 0;17641765// Imported declarations should not be indexed according to the specification1766// and LLVM currently does not do that.1767case DW_TAG_imported_declaration:1768return 0;17691770// "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging1771// information entries without an address attribute (DW_AT_low_pc,1772// DW_AT_high_pc, DW_AT_ranges, or DW_AT_entry_pc) are excluded."1773case DW_TAG_subprogram:1774case DW_TAG_inlined_subroutine:1775case DW_TAG_label:1776if (Die.findRecursively(1777{DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc}))1778break;1779return 0;17801781// "DW_TAG_variable debugging information entries with a DW_AT_location1782// attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are1783// included; otherwise, they are excluded."1784//1785// LLVM extension: We also add DW_OP_GNU_push_tls_address to this list.1786case DW_TAG_variable:1787if (isVariableIndexable(Die, DCtx))1788break;1789return 0;17901791default:1792break;1793}17941795// Now we know that our Die should be present in the Index. Let's check if1796// that's the case.1797unsigned NumErrors = 0;1798uint64_t DieUnitOffset = Die.getOffset() - Die.getDwarfUnit()->getOffset();1799for (StringRef Name : EntryNames) {1800if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) {1801return E.getDIEUnitOffset() == DieUnitOffset;1802})) {1803ErrorCategory.Report("Name Index DIE entry missing name", [&]() {1804error() << formatv(1805"Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with "1806"name {3} missing.\n",1807NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name);1808});1809++NumErrors;1810}1811}1812return NumErrors;1813}18141815unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,1816const DataExtractor &StrData) {1817unsigned NumErrors = 0;1818DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), AccelSection,1819DCtx.isLittleEndian(), 0);1820DWARFDebugNames AccelTable(AccelSectionData, StrData);18211822OS << "Verifying .debug_names...\n";18231824// This verifies that we can read individual name indices and their1825// abbreviation tables.1826if (Error E = AccelTable.extract()) {1827std::string Msg = toString(std::move(E));1828ErrorCategory.Report("Accelerator Table Error",1829[&]() { error() << Msg << '\n'; });1830return 1;1831}18321833NumErrors += verifyDebugNamesCULists(AccelTable);1834for (const auto &NI : AccelTable)1835NumErrors += verifyNameIndexBuckets(NI, StrData);1836for (const auto &NI : AccelTable)1837NumErrors += verifyNameIndexAbbrevs(NI);18381839// Don't attempt Entry validation if any of the previous checks found errors1840if (NumErrors > 0)1841return NumErrors;1842for (const auto &NI : AccelTable)1843for (const DWARFDebugNames::NameTableEntry &NTE : NI)1844NumErrors += verifyNameIndexEntries(NI, NTE);18451846if (NumErrors > 0)1847return NumErrors;18481849for (const std::unique_ptr<DWARFUnit> &U : DCtx.compile_units()) {1850if (const DWARFDebugNames::NameIndex *NI =1851AccelTable.getCUNameIndex(U->getOffset())) {1852auto *CU = cast<DWARFCompileUnit>(U.get());1853for (const DWARFDebugInfoEntry &Die : CU->dies())1854NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &Die), *NI);1855}1856}1857return NumErrors;1858}18591860bool DWARFVerifier::handleAccelTables() {1861const DWARFObject &D = DCtx.getDWARFObj();1862DataExtractor StrData(D.getStrSection(), DCtx.isLittleEndian(), 0);1863unsigned NumErrors = 0;1864if (!D.getAppleNamesSection().Data.empty())1865NumErrors += verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData,1866".apple_names");1867if (!D.getAppleTypesSection().Data.empty())1868NumErrors += verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData,1869".apple_types");1870if (!D.getAppleNamespacesSection().Data.empty())1871NumErrors += verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData,1872".apple_namespaces");1873if (!D.getAppleObjCSection().Data.empty())1874NumErrors += verifyAppleAccelTable(&D.getAppleObjCSection(), &StrData,1875".apple_objc");18761877if (!D.getNamesSection().Data.empty())1878NumErrors += verifyDebugNames(D.getNamesSection(), StrData);1879return NumErrors == 0;1880}18811882bool DWARFVerifier::handleDebugStrOffsets() {1883OS << "Verifying .debug_str_offsets...\n";1884const DWARFObject &DObj = DCtx.getDWARFObj();1885bool Success = true;18861887// dwo sections may contain the legacy debug_str_offsets format (and they1888// can't be mixed with dwarf 5's format). This section format contains no1889// header.1890// As such, check the version from debug_info and, if we are in the legacy1891// mode (Dwarf <= 4), extract Dwarf32/Dwarf64.1892std::optional<DwarfFormat> DwoLegacyDwarf4Format;1893DObj.forEachInfoDWOSections([&](const DWARFSection &S) {1894if (DwoLegacyDwarf4Format)1895return;1896DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0);1897uint64_t Offset = 0;1898DwarfFormat InfoFormat = DebugInfoData.getInitialLength(&Offset).second;1899if (uint16_t InfoVersion = DebugInfoData.getU16(&Offset); InfoVersion <= 4)1900DwoLegacyDwarf4Format = InfoFormat;1901});19021903Success &= verifyDebugStrOffsets(1904DwoLegacyDwarf4Format, ".debug_str_offsets.dwo",1905DObj.getStrOffsetsDWOSection(), DObj.getStrDWOSection());1906Success &= verifyDebugStrOffsets(1907/*LegacyFormat=*/std::nullopt, ".debug_str_offsets",1908DObj.getStrOffsetsSection(), DObj.getStrSection());1909return Success;1910}19111912bool DWARFVerifier::verifyDebugStrOffsets(1913std::optional<DwarfFormat> LegacyFormat, StringRef SectionName,1914const DWARFSection &Section, StringRef StrData) {1915const DWARFObject &DObj = DCtx.getDWARFObj();19161917DWARFDataExtractor DA(DObj, Section, DCtx.isLittleEndian(), 0);1918DataExtractor::Cursor C(0);1919uint64_t NextUnit = 0;1920bool Success = true;1921while (C.seek(NextUnit), C.tell() < DA.getData().size()) {1922DwarfFormat Format;1923uint64_t Length;1924uint64_t StartOffset = C.tell();1925if (LegacyFormat) {1926Format = *LegacyFormat;1927Length = DA.getData().size();1928NextUnit = C.tell() + Length;1929} else {1930std::tie(Length, Format) = DA.getInitialLength(C);1931if (!C)1932break;1933if (C.tell() + Length > DA.getData().size()) {1934ErrorCategory.Report(1935"Section contribution length exceeds available space", [&]() {1936error() << formatv(1937"{0}: contribution {1:X}: length exceeds available space "1938"(contribution "1939"offset ({1:X}) + length field space ({2:X}) + length "1940"({3:X}) == "1941"{4:X} > section size {5:X})\n",1942SectionName, StartOffset, C.tell() - StartOffset, Length,1943C.tell() + Length, DA.getData().size());1944});1945Success = false;1946// Nothing more to do - no other contributions to try.1947break;1948}1949NextUnit = C.tell() + Length;1950uint8_t Version = DA.getU16(C);1951if (C && Version != 5) {1952ErrorCategory.Report("Invalid Section version", [&]() {1953error() << formatv("{0}: contribution {1:X}: invalid version {2}\n",1954SectionName, StartOffset, Version);1955});1956Success = false;1957// Can't parse the rest of this contribution, since we don't know the1958// version, but we can pick up with the next contribution.1959continue;1960}1961(void)DA.getU16(C); // padding1962}1963uint64_t OffsetByteSize = getDwarfOffsetByteSize(Format);1964DA.setAddressSize(OffsetByteSize);1965uint64_t Remainder = (Length - 4) % OffsetByteSize;1966if (Remainder != 0) {1967ErrorCategory.Report("Invalid section contribution length", [&]() {1968error() << formatv(1969"{0}: contribution {1:X}: invalid length ((length ({2:X}) "1970"- header (0x4)) % offset size {3:X} == {4:X} != 0)\n",1971SectionName, StartOffset, Length, OffsetByteSize, Remainder);1972});1973Success = false;1974}1975for (uint64_t Index = 0; C && C.tell() + OffsetByteSize <= NextUnit; ++Index) {1976uint64_t OffOff = C.tell();1977uint64_t StrOff = DA.getAddress(C);1978// check StrOff refers to the start of a string1979if (StrOff == 0)1980continue;1981if (StrData.size() <= StrOff) {1982ErrorCategory.Report(1983"String offset out of bounds of string section", [&]() {1984error() << formatv(1985"{0}: contribution {1:X}: index {2:X}: invalid string "1986"offset *{3:X} == {4:X}, is beyond the bounds of the string "1987"section of length {5:X}\n",1988SectionName, StartOffset, Index, OffOff, StrOff,1989StrData.size());1990});1991continue;1992}1993if (StrData[StrOff - 1] == '\0')1994continue;1995ErrorCategory.Report(1996"Section contribution contains invalid string offset", [&]() {1997error() << formatv(1998"{0}: contribution {1:X}: index {2:X}: invalid string "1999"offset *{3:X} == {4:X}, is neither zero nor "2000"immediately following a null character\n",2001SectionName, StartOffset, Index, OffOff, StrOff);2002});2003Success = false;2004}2005}20062007if (Error E = C.takeError()) {2008std::string Msg = toString(std::move(E));2009ErrorCategory.Report("String offset error", [&]() {2010error() << SectionName << ": " << Msg << '\n';2011return false;2012});2013}2014return Success;2015}20162017void OutputCategoryAggregator::Report(2018StringRef s, std::function<void(void)> detailCallback) {2019Aggregation[std::string(s)]++;2020if (IncludeDetail)2021detailCallback();2022}20232024void OutputCategoryAggregator::EnumerateResults(2025std::function<void(StringRef, unsigned)> handleCounts) {2026for (auto &&[name, count] : Aggregation) {2027handleCounts(name, count);2028}2029}20302031void DWARFVerifier::summarize() {2032if (DumpOpts.ShowAggregateErrors && ErrorCategory.GetNumCategories()) {2033error() << "Aggregated error counts:\n";2034ErrorCategory.EnumerateResults([&](StringRef s, unsigned count) {2035error() << s << " occurred " << count << " time(s).\n";2036});2037}2038if (!DumpOpts.JsonErrSummaryFile.empty()) {2039std::error_code EC;2040raw_fd_ostream JsonStream(DumpOpts.JsonErrSummaryFile, EC,2041sys::fs::OF_Text);2042if (EC) {2043error() << "unable to open json summary file '"2044<< DumpOpts.JsonErrSummaryFile2045<< "' for writing: " << EC.message() << '\n';2046return;2047}20482049llvm::json::Object Categories;2050uint64_t ErrorCount = 0;2051ErrorCategory.EnumerateResults([&](StringRef Category, unsigned Count) {2052llvm::json::Object Val;2053Val.try_emplace("count", Count);2054Categories.try_emplace(Category, std::move(Val));2055ErrorCount += Count;2056});2057llvm::json::Object RootNode;2058RootNode.try_emplace("error-categories", std::move(Categories));2059RootNode.try_emplace("error-count", ErrorCount);20602061JsonStream << llvm::json::Value(std::move(RootNode));2062}2063}20642065raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); }20662067raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); }20682069raw_ostream &DWARFVerifier::note() const { return WithColor::note(OS); }20702071raw_ostream &DWARFVerifier::dump(const DWARFDie &Die, unsigned indent) const {2072Die.dump(OS, indent, DumpOpts);2073return OS;2074}207520762077