Index: include/llvm/DebugInfo/DIContext.h =================================================================== --- include/llvm/DebugInfo/DIContext.h +++ include/llvm/DebugInfo/DIContext.h @@ -161,6 +161,9 @@ virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All, bool DumpEH = false, bool SummarizeTypes = false) = 0; + virtual bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) { + return true; // No verifier? Just say things went well. + } virtual DILineInfo getLineInfoForAddress(uint64_t Address, DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0; virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address, Index: include/llvm/DebugInfo/DWARF/DWARFContext.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFContext.h +++ include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -106,6 +106,8 @@ void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All, bool DumpEH = false, bool SummarizeTypes = false) override; + bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) override; + typedef DWARFUnitSection::iterator_range cu_iterator_range; typedef DWARFUnitSection::iterator_range tu_iterator_range; typedef iterator_range tu_section_iterator_range; @@ -170,6 +172,9 @@ return DWOCUs[index].get(); } + /// Get a DIE given an exact offset. + DWARFDie getDIEForOffset(uint32_t Offset); + const DWARFUnitIndex &getCUIndex(); DWARFGdbIndex &getGdbIndex(); const DWARFUnitIndex &getTUIndex(); Index: include/llvm/DebugInfo/DWARF/DWARFDebugAranges.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDebugAranges.h +++ include/llvm/DebugInfo/DWARF/DWARFDebugAranges.h @@ -21,7 +21,7 @@ class DWARFDebugAranges { public: - void generate(DWARFContext *CTX); + void generate(DWARFContext *CTX, bool SectionOnly = false); uint32_t findAddress(uint64_t Address) const; private: Index: include/llvm/DebugInfo/DWARF/DWARFDebugLine.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -103,7 +103,7 @@ void postAppend(); void reset(bool default_is_stmt); void dump(raw_ostream &OS) const; - + static void dumpTableHeader(raw_ostream &OS); static bool orderByAddress(const Row& LHS, const Row& RHS) { return LHS.Address < RHS.Address; } Index: lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFContext.cpp +++ lib/DebugInfo/DWARF/DWARFContext.cpp @@ -45,6 +45,8 @@ #include #include #include +#include +#include using namespace llvm; using namespace dwarf; @@ -284,6 +286,418 @@ getStringSection(), isLittleEndian()); } +DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) { + parseCompileUnits(); + if (auto *CU = CUs.getUnitForOffset(Offset)) + return CU->getDIEForOffset(Offset); + return DWARFDie(); +} + +namespace { + + // Returns true if the two ranges intersect +bool RangesIntersect(const std::pair &a, + const std::pair &b) { + if (a.first == a.second || b.first == b.second) + return false; // Empty ranges can't intersect. + return (a.first < b.second) && (a.second > b.first); +} + + struct RangeInfoType + { + DWARFDie Die; + DWARFAddressRangesVector Ranges; + RangeInfoType() : Die(), Ranges() {} + static bool CheckForRangeErrors(raw_ostream &OS, + DWARFAddressRangesVector &Ranges) { + bool HasErrors = false; + size_t I = 0; + while (I < Ranges.size()) { + if (Ranges[I].first >= Ranges[I].second) { + if (Ranges[I].first > Ranges[I].second) { + OS << "error: invalid range in DIE\n"; + HasErrors = true; + } + // Remove empty or invalid ranges. + Ranges.erase(Ranges.begin() + I); + continue; + } else { + ++I; // Only increment if we didn't remove the range. + } + } + if (Ranges.size() > 1) + std::sort(Ranges.begin(), Ranges.end()); + return HasErrors; + } + RangeInfoType(DWARFDie D, const DWARFAddressRangesVector &R) : + Die(D), Ranges(R) {} + void Dump(raw_ostream &OS) { + OS << format("0x%08" PRIx32, Die.getOffset()) << ":"; + for (const auto &R: Ranges) + OS << " [" << format("0x%" PRIx32, R.first) << "-" + << format("0x%" PRIx32, R.second) << ")"; + OS << "\n"; + } + /// Return true if this object contains all ranges within RHS. + bool Contains(const RangeInfoType &RHS) { + for (const auto &S: RHS.Ranges) + for (const auto &R: Ranges) + if (R.first <= S.first && S.first < R.second) + return R.first < S.second && S.second <= R.second; + return false; + } + bool DoesIntersect(const RangeInfoType &RHS) const { + for (const auto &R: RHS.Ranges) + for (const auto &L: Ranges) + if (RangesIntersect(R, L)) + return true; + return false; + } + bool operator<(const RangeInfoType &RHS) const { + if (Ranges.empty()) { + if (RHS.Ranges.empty()) + return Die.getOffset() < RHS.Die.getOffset(); + else + return true; + } else if (RHS.Ranges.empty()) + return false; + else { + size_t LeftSize = Ranges.size(); + size_t RightSize = RHS.Ranges.size(); + for (size_t i=0; i= RightSize) + return false; + if (Ranges[i].first != RHS.Ranges[i].first) + return Ranges[i].first < RHS.Ranges[i].first; + if (Ranges[i].second != RHS.Ranges[i].second) + return Ranges[i].second < RHS.Ranges[i].second; + } + return false; + } + } + }; + + +} +bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) { + bool success = true; + const bool VerifyInfo = DumpType == DIDT_All || DumpType == DIDT_Info; + const bool VerifyAranges = DumpType == DIDT_All || DumpType == DIDT_Aranges; + if (VerifyInfo || VerifyAranges) { + std::map> ReferenceToDIEOffsets; + std::set References; + // Keep a list of all DW_TAG_subprogram range infos across all compile + // units so we can look for overlapping ranges the the end. + std::set AllFunctionRangeInfos; + std::vector FuncRanges; + uint32_t FuncDieDepth = 0; + RangeInfoType CURangeInfo; + if (VerifyInfo) + OS << "Verifying .debug_info...\n"; + else + OS << "Verifying .debug_aranges...\n"; + for (const auto &CU : compile_units()) { + unsigned NumDies = CU->getNumDIEs(); + for (unsigned i=0; igetDIEAtIndex(i); + assert(Die); // Make sure DIE is valid + //const auto DieOffset = Die.getOffset(); + const auto Tag = Die.getTag(); + const uint32_t DieDepth = Die.getDebugInfoEntry()->getDepth(); + bool SkipAttributes = false; + switch (Tag) + { + case DW_TAG_null: + SkipAttributes = true; + break; + case DW_TAG_compile_unit: { + auto Ranges = Die.getAddressRanges(); + if (RangeInfoType::CheckForRangeErrors(OS, Ranges)) { + Die.dump(OS, 0); + OS << "\n"; + } + CURangeInfo = RangeInfoType(Die, Ranges); + } + break; + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + { + auto Ranges = Die.getAddressRanges(); + if (RangeInfoType::CheckForRangeErrors(OS, Ranges)) { + Die.dump(OS, 0); + OS << "\n"; + } + RangeInfoType RangeInfo(Die, Ranges); + if (!RangeInfo.Ranges.empty()) { + if (Tag == DW_TAG_subprogram) { + // We need to keep track of all function ranges for + // .debug_aranges and .debug_info verifiers + AllFunctionRangeInfos.insert(RangeInfo); + if (!VerifyInfo) + break; + // We keep a vector of ranges with the DW_TAG_subprogram + // ranges at index zero. Blocks and inlined funcitons will + // use this stack and place their ranges into the array and + // will always compare their ranges against the range above. + // We use the DIE depth to later calculate the FuncRanges + // index to use when we handle DW_TAG_lexical_block and + // DW_TAG_inlined_subroutine. + FuncRanges.clear(); + FuncRanges.push_back(RangeInfo); + FuncDieDepth = DieDepth; + + if (!CURangeInfo.Ranges.empty()) { + if (!CURangeInfo.Contains(RangeInfo)) { + success = false; + OS << "error: CU DIE has child with address ranges " + "that are not contained in its ranges:\n"; + CURangeInfo.Die.dump(OS, 0); + RangeInfo.Die.dump(OS, 0); + OS << "\n"; + } + } + } else if (VerifyInfo) { + // Use the function's DIE depth to calculate the correct + // index into the FuncRange array. + uint32_t D = DieDepth - FuncDieDepth; + if (D >= FuncRanges.size()) + FuncRanges.resize(D+1); + FuncRanges[D] = RangeInfo; + assert(D > 0); + assert(D < FuncRanges.size()); + if (!FuncRanges[D-1].Contains(FuncRanges[D])) { + success = false; + OS << "error: DIE has child with address ranges that " + "are not contained in its ranges:\n"; + FuncRanges[D-1].Die.dump(OS, 0); + FuncRanges[D].Die.dump(OS, 0); + } + } + } + } + break; + default: + break; + } + if (!VerifyInfo || SkipAttributes) + continue; + for (auto AttrValue : Die.attributes()) { + const auto Attr = AttrValue.Attr; + const auto Form = AttrValue.Value.getForm(); + switch (Attr) { + case DW_AT_ranges: + // Make sure the offset in the DW_AT_ranges attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= getRangeSection().Data.size()) { + success = false; + OS << "error: DW_AT_ranges offset is beyond .debug_ranges " + "bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + } else { + success = false; + OS << "error: DIE has invalid DW_AT_ranges encoding:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + case DW_AT_stmt_list: + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= getLineSection().Data.size()) { + success = false; + OS << "error: DW_AT_stmt_list offset is beyond .debug_line " + "bounds: " << format("0x%08" PRIx32, *SectionOffset) + << "\n"; + CU->getUnitDIE().dump(OS, 0); + OS << "\n"; + } + } + break; + + default: + break; + } + // Keep track of all references so we can make sure they + // point to valid + switch (Form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_ref_addr: { + Optional RefVal = AttrValue.Value.getAsReference(); + assert(RefVal); + if (RefVal) { + References.insert(*RefVal); + ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); + } + break; + } + case DW_FORM_strp: { + auto SectionOffset = AttrValue.Value.getAsSectionOffset(); + assert(SectionOffset); // DW_FORM_strp is a section offset. + if (*SectionOffset >= getStringSection().size()) { + success = false; + OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + } + default: + break; + } + } + } + } + if (VerifyInfo) { + // Now go through all function address ranges and check for any that + // overlap. All function range infos were added to a std::set that we can + // traverse in order and do the checking for overlaps. + OS << "Verifying .debug_info function address ranges for overlap...\n"; + bool First = true; + RangeInfoType PrevRI; + for (const auto &RI : AllFunctionRangeInfos) { + if (!First) { + if (PrevRI.DoesIntersect(RI)) { + success = false; + OS << "error: two function DIEs have overlapping address ranges:\n"; + PrevRI.Die.dump(OS, 0); + RI.Die.dump(OS, 0); + OS << "\n"; + } + } + First = false; + PrevRI = RI; + } + + // Take all references and make sure they point to an actual DIE by + // getting the DIE by offset and emitting an error + OS << "Verifying .debug_info references...\n"; + for (auto Reference: References) { + auto Die = getDIEForOffset(Reference); + if (!Die) { + success = false; + OS << "error: invalid reference " << format("0x%08" PRIx64, Reference) + << " found in DIEs:"; + for (auto Offset: ReferenceToDIEOffsets[Reference]) + OS << format(" 0x%08" PRIx32, Offset); + OS << "\n"; + } + } + } + + // Don't call DWARFContext::getDebugAranges() since it will manually + // parse all DIEs for addresses. Parse only the .debug_aranges so we + // can report if they are not doing their job. + if (VerifyAranges) { + if (VerifyInfo) + OS << "Verifying .debug_aranges...\n"; + if (getARangeSection().empty()) { + DWARFDebugAranges Aranges; + Aranges.generate(this, true /*SectionOnly*/); + for (const auto &RI : AllFunctionRangeInfos) { + const auto DieCUOffset = RI.Die.getDwarfUnit()->getOffset(); + for (const auto &Range: RI.Ranges) { + auto CUOffset = Aranges.findAddress(Range.first); + if (DieCUOffset != CUOffset) { + success = false; + OS << "error: .debug_aranges doesn't contain start address of [" + << format("0x%08" PRIx64, Range.first) << "-" + << format("0x%08" PRIx64, Range.second) << ") for DIE:\n"; + RI.Die.dump(OS, 0); + OS << "\n"; + } else if (Range.first != Range.second - 1) { + auto CUOffset = Aranges.findAddress(Range.second-1); + if (DieCUOffset != CUOffset) { + success = false; + OS << "error: .debug_aranges doesn't contain last byte of [" + << format("0x%08" PRIx64, Range.first) << "-" + << format("0x%08" PRIx64, Range.second) << ") for DIE:\n"; + RI.Die.dump(OS, 0); + OS << "\n"; + } + } + } + } + } + } + } + + if (DumpType == DIDT_All || DumpType == DIDT_Line) { + OS << "Verifying .debug_line...\n"; + for (const auto &CU : compile_units()) { + uint32_t LineTableOffset = 0; + if (auto StmtFormValue = CU->getUnitDIE().find(DW_AT_stmt_list)) { + if (auto DataOpt = toSectionOffset(StmtFormValue)) { + LineTableOffset = *DataOpt; + if (LineTableOffset >= getLineSection().Data.size()) { + // DWARF with invalid DW_AT_stmt_list values was returning an + // empty non-NULL line table before, so ensure this stays fixed. + // If we have a bad line table offset we should get NULL back. + assert(getLineTableForUnit(CU.get()) == nullptr); + success = false; + OS << "error: DW_AT_stmt_list offset is beyond .debug_line " + "bounds: " << format("0x%08" PRIx32, LineTableOffset) + << "\n"; + CU->getUnitDIE().dump(OS, 0); + OS << "\n"; + continue; + } + } + } else { + continue; // No line table for this compile unit. + } + auto LineTable = getLineTableForUnit(CU.get()); + if (!LineTable) { + success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + CU->getUnitDIE().dump(OS, 0); + OS << "\n"; + 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) { + success = false; + OS << "error: .debug_line[" + << format("0x%08" PRIx32, LineTableOffset) + << "][" << RowIndex + << "] decreases in address from previous row\n"; + + DWARFDebugLine::Row::dumpTableHeader(OS); + if (RowIndex > 0) + LineTable->Rows[RowIndex-1].dump(OS); + Row.dump(OS); + OS << "\n"; + } + + if (Row.File > MaxFileIndex) { + success = false; + OS << "error: .debug_line[" + << format("0x%08" PRIx32, LineTableOffset) + << "][" << RowIndex + << "] has invalid file index. Valid values are 1 - " + << MaxFileIndex << ".\n"; + DWARFDebugLine::Row::dumpTableHeader(OS); + Row.dump(OS); + OS << "\n"; + } + if (Row.EndSequence) { + PrevAddress = 0; + } + ++RowIndex; + } + } + } + return success; +} const DWARFUnitIndex &DWARFContext::getCUIndex() { if (CUIndex) return *CUIndex; @@ -427,6 +841,10 @@ if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) return lt; + // Make sure the offset is good before we try to parse. + if (stmtOffset >= U->getLineSection().size()) + return nullptr; + // We have to parse it first. DataExtractor lineData(U->getLineSection(), isLittleEndian(), U->getAddressByteSize()); Index: lib/DebugInfo/DWARF/DWARFDebugAranges.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -37,7 +37,7 @@ } } -void DWARFDebugAranges::generate(DWARFContext *CTX) { +void DWARFDebugAranges::generate(DWARFContext *CTX, bool SectionOnly) { clear(); if (!CTX) return; @@ -49,17 +49,18 @@ // Generate aranges from DIEs: even if .debug_aranges section is present, // it may describe only a small subset of compilation units, so we need to // manually build aranges for the rest of them. - for (const auto &CU : CTX->compile_units()) { - uint32_t CUOffset = CU->getOffset(); - if (ParsedCUOffsets.insert(CUOffset).second) { - DWARFAddressRangesVector CURanges; - CU->collectAddressRanges(CURanges); - for (const auto &R : CURanges) { - appendRange(CUOffset, R.first, R.second); + if (!SectionOnly) { + for (const auto &CU : CTX->compile_units()) { + uint32_t CUOffset = CU->getOffset(); + if (ParsedCUOffsets.insert(CUOffset).second) { + DWARFAddressRangesVector CURanges; + CU->collectAddressRanges(CURanges); + for (const auto &R : CURanges) { + appendRange(CUOffset, R.first, R.second); + } } } } - construct(); } @@ -113,12 +114,12 @@ uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { if (!Aranges.empty()) { - Range range(Address); RangeCollIterator begin = Aranges.begin(); RangeCollIterator end = Aranges.end(); - RangeCollIterator pos = - std::lower_bound(begin, end, range); - + RangeCollIterator pos = std::lower_bound(begin, end, Address, + [](const Range &R, uint64_t Address) { + return R.LowPC < Address; + }); if (pos != end && pos->containsAddress(Address)) { return pos->CUOffset; } else if (pos != begin) { Index: lib/DebugInfo/DWARF/DWARFDebugLine.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -161,6 +161,11 @@ EpilogueBegin = false; } +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { + OS << "Address Line Column File ISA Discriminator Flags\n" + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; +} void DWARFDebugLine::Row::dump(raw_ostream &OS) const { OS << format("0x%16.16" PRIx64 " %6u %6u", Address, Line, Column) << format(" %6u %3u %13u ", File, Isa, Discriminator) @@ -187,9 +192,7 @@ OS << '\n'; if (!Rows.empty()) { - OS << "Address Line Column File ISA Discriminator Flags\n" - << "------------------ ------ ------ ------ --- ------------- " - "-------------\n"; + Row::dumpTableHeader(OS); for (const Row &R : Rows) { R.dump(OS); } Index: lib/DebugInfo/DWARF/DWARFFormValue.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -261,12 +261,12 @@ case DW_FORM_GNU_str_index: DebugInfoData.getULEB128(OffsetPtr); return true; - + case DW_FORM_indirect: Indirect = true; Form = static_cast(DebugInfoData.getULEB128(OffsetPtr)); break; - + default: return false; } @@ -309,12 +309,13 @@ } // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset. // Don't check for DWARF version here, as some producers may still do this - // by mistake. - return (Form == DW_FORM_data4 || Form == DW_FORM_data8) && - FC == FC_SectionOffset; + // by mistake. Also accept DW_FORM_strp since this is .debug_str section + // offset. + return (Form == DW_FORM_data4 || Form == DW_FORM_data8 || + Form == DW_FORM_strp) && FC == FC_SectionOffset; } -bool DWARFFormValue::extractValue(const DataExtractor &data, +bool DWARFFormValue::extractValue(const DataExtractor &data, uint32_t *offset_ptr, const DWARFUnit *cu) { U = cu; Index: tools/llvm-dwarfdump/llvm-dwarfdump.cpp =================================================================== --- tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -78,6 +78,12 @@ SummarizeTypes("summarize-types", cl::desc("Abbreviate the description of type unit entries")); +static cl::opt + Verify("verify", cl::desc("Verify the DWARF debug info")); + +static cl::opt + Quiet("quiet", cl::desc("Use with -verify to not emit to STDOUT.")); + static void error(StringRef Filename, std::error_code EC) { if (!EC) return; @@ -88,10 +94,24 @@ static void DumpObjectFile(ObjectFile &Obj, Twine Filename) { std::unique_ptr DICtx(new DWARFContextInMemory(Obj)); - outs() << Filename.str() << ":\tfile format " << Obj.getFileFormatName() - << "\n\n"; - // Dump the complete DWARF structure. - DICtx->dump(outs(), DumpType, false, SummarizeTypes); + if (Verify) { + // Verify the DWARF and exit with non-zero exit status if verification + // fails. + raw_ostream &stream = Quiet ? nulls() : outs(); + stream << "Verifying " << Filename.str() << ":\tfile format " + << Obj.getFileFormatName() << "\n"; + if (DICtx->verify(stream, DumpType)) { + stream << "No errors.\n"; + } else { + stream << "Errors detected.\n"; + exit(1); + } + } else { + // Dump the complete DWARF structure. + outs() << Filename.str() << ":\tfile format " << Obj.getFileFormatName() + << "\n\n"; + DICtx->dump(outs(), DumpType, false, SummarizeTypes); + } } static void DumpInput(StringRef Filename) {