diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -229,7 +229,8 @@ virtual void dump(raw_ostream &OS, DIDumpOptions DumpOpts) = 0; - virtual bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) { + virtual bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}, + const object::ObjectFile *Obj = nullptr) { // No verifier? Just say things went well. return true; } diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h @@ -36,6 +36,16 @@ /// dead-stripped ranges. bool valid() const { return LowPC <= HighPC; } + /// Returns true if [LowPC, HighPC) fully contains the entire range + /// [RHS.LowPC, RHS.HighPC). + bool contains(const DWARFAddressRange &RHS) const { + if (!valid() || !RHS.valid() || SectionIndex != RHS.SectionIndex) + return false; + if (LowPC <= RHS.LowPC && RHS.LowPC < HighPC) + return RHS.HighPC <= HighPC; + return false; + } + /// Returns true if [LowPC, HighPC) intersects with [RHS.LowPC, RHS.HighPC). bool intersects(const DWARFAddressRange &RHS) const { assert(valid() && RHS.valid()); diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -143,7 +143,8 @@ dump(OS, DumpOpts, DumpOffsets); } - bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) override; + bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}, + const object::ObjectFile *Obj = nullptr) override; using unit_iterator_range = DWARFUnitVector::iterator_range; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -35,12 +35,42 @@ /// A class that verifies DWARF debug information given a DWARF Context. class DWARFVerifier { public: + /// A class that keeps address ranges sorted for quick lookups. + struct SortedRanges { + /// Sorted DWARFAddressRanges. + DWARFAddressRangesVector Ranges; + typedef DWARFAddressRangesVector::const_iterator address_range_iterator; + SortedRanges() = default; + SortedRanges(const DWARFAddressRangesVector R); + /// Inserts the address range. If the range overlaps with an existing + /// range, the range that it overlaps with will be returned and the two + /// address ranges will be unioned together in "Ranges". + /// + /// This is used for finding overlapping ranges in the DW_AT_ranges + /// attribute of a DIE. It is also used as a set of address ranges that + /// children address ranges must all be contained in. + Optional insert(const DWARFAddressRange &R); + + /// Return true if ranges in this object contains the range within RHS. + bool contains(const DWARFAddressRange &Range) const; + + /// Return true if ranges in this object contains all ranges within RHS. + bool contains(const SortedRanges &RHS) const; + + /// Return true if any range in this object intersects with any range in + /// RHS. + bool intersects(const SortedRanges &RHS) const; + + /// Returns true if there are no address ranges. + bool empty() const { return Ranges.empty(); } + }; + /// A class that keeps the address range information for a single DIE. struct DieRangeInfo { DWARFDie Die; /// Sorted DWARFAddressRanges. - std::vector Ranges; + SortedRanges Ranges; /// Sorted DWARFAddressRangeInfo. std::set Children; @@ -49,32 +79,10 @@ DieRangeInfo(DWARFDie Die) : Die(Die) {} /// Used for unit testing. - DieRangeInfo(std::vector Ranges) - : Ranges(std::move(Ranges)) {} + DieRangeInfo(DWARFAddressRangesVector R) : Ranges(R) {} - typedef std::vector::const_iterator - address_range_iterator; typedef std::set::const_iterator die_range_info_iterator; - /// Inserts the address range. If the range overlaps with an existing - /// range, the range that it overlaps with will be returned and the two - /// address ranges will be unioned together in "Ranges". - /// - /// This is used for finding overlapping ranges in the DW_AT_ranges - /// attribute of a DIE. It is also used as a set of address ranges that - /// children address ranges must all be contained in. - Optional insert(const DWARFAddressRange &R); - - /// Finds an address range in the sorted vector of ranges. - address_range_iterator findRange(const DWARFAddressRange &R) const { - auto Begin = Ranges.begin(); - auto End = Ranges.end(); - auto Iter = std::upper_bound(Begin, End, R); - if (Iter != Begin) - --Iter; - return Iter; - } - /// Inserts the address range info. If any of its ranges overlaps with a /// range in an existing range info, the range info is *not* added and an /// iterator to the overlapping range info. @@ -98,6 +106,7 @@ /// can verify each reference points to a valid DIE and not an offset that /// lies between to valid DIEs. std::map> ReferenceToDIEOffsets; + Optional ValidTextRanges; uint32_t NumDebugLineErrors = 0; // Used to relax some checks that do not currently work portably bool IsObjectFile; @@ -108,6 +117,28 @@ raw_ostream ¬e() const; raw_ostream &dump(const DWARFDie &Die, unsigned indent = 0) const; + /// Detects if a DWARF address range is stripped. + /// + /// This function can check any DWARF address range to make sure it should be + /// checked. If the object file has executable sections, those address ranges + /// can be used to determine if an address range is stripped. This helps + /// catch addresses left in the DWARF whose low PC has been set to zero, -1 + /// or -2. Typically those addresses won't be valid function addresses. + /// + /// \param R An address range from DWARF to check. + /// + /// \returns True if the text ranges have been set and if the address is not + /// contained in any of those ranges. + bool rangeIsStripped(const DWARFAddressRange R) const { + // If we have .text ranges, check to make sure the address range is not + // contained in any of them. + if (ValidTextRanges) + return !ValidTextRanges->contains(R); + // No .text ranges have been set we don't know if an address range + // should have been stripped, so we return false. + return false; + } + /// Verifies the abbreviations section. /// /// This function currently checks that: @@ -327,8 +358,15 @@ /// \returns true if the existing Apple-style accelerator tables verify /// successfully, false otherwise. bool handleAccelTables(); + + void setValidTextRanges(const SortedRanges &R) { ValidTextRanges = R; } }; +static inline bool operator<(const DWARFVerifier::SortedRanges &LHS, + const DWARFVerifier::SortedRanges &RHS) { + return LHS.Ranges < RHS.Ranges; +} + static inline bool operator<(const DWARFVerifier::DieRangeInfo &LHS, const DWARFVerifier::DieRangeInfo &RHS) { return std::tie(LHS.Ranges, LHS.Die) < std::tie(RHS.Ranges, RHS.Die); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" @@ -721,10 +722,30 @@ return DWARFDie(); } -bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { +bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts, + const object::ObjectFile *Obj) { bool Success = true; DWARFVerifier verifier(OS, *this, DumpOpts); + if (Obj) { + // We need to know where the valid sections are that contain instructions. + // See header documentation for DWARFTransformer::SetValidTextRanges() for + // defails. + DWARFAddressRangesVector TextRanges; + for (const object::SectionRef &Sect : Obj->sections()) { + if (!Sect.isText()) + continue; + const uint64_t Size = Sect.getSize(); + if (Size == 0) + continue; + const uint64_t StartAddr = Sect.getAddress(); + TextRanges.emplace_back(DWARFAddressRange( + StartAddr, StartAddr + Size, SectionedAddress::UndefSection)); + } + if (!TextRanges.empty()) + verifier.setValidTextRanges(TextRanges); + } + Success &= verifier.handleDebugAbbrev(); if (DumpOpts.DumpType & DIDT_DebugInfo) Success &= verifier.handleDebugInfo(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -26,8 +26,13 @@ using namespace dwarf; using namespace object; +DWARFVerifier::SortedRanges::SortedRanges(const DWARFAddressRangesVector R) { + for (const auto Range : R) + insert(Range); +} + Optional -DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { +DWARFVerifier::SortedRanges::insert(const DWARFAddressRange &R) { auto Begin = Ranges.begin(); auto End = Ranges.end(); auto Pos = std::lower_bound(Begin, End, R); @@ -48,20 +53,20 @@ return None; } -DWARFVerifier::DieRangeInfo::die_range_info_iterator -DWARFVerifier::DieRangeInfo::insert(const DieRangeInfo &RI) { - auto End = Children.end(); - auto Iter = Children.begin(); - while (Iter != End) { - if (Iter->intersects(RI)) - return Iter; - ++Iter; +bool DWARFVerifier::SortedRanges::contains(const DWARFAddressRange &R) const { + auto Begin = Ranges.begin(); + auto End = Ranges.end(); + auto Pos = std::lower_bound(Begin, End, R); + if (Pos != End && Pos->contains(R)) + return true; + if (Pos != Begin) { + auto Prev = Pos - 1; + return Prev->contains(R); } - Children.insert(RI); - return Children.end(); + return false; } -bool DWARFVerifier::DieRangeInfo::contains(const DieRangeInfo &RHS) const { +bool DWARFVerifier::SortedRanges::contains(const SortedRanges &RHS) const { auto I1 = Ranges.begin(), E1 = Ranges.end(); auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end(); if (I2 == E2) @@ -85,7 +90,7 @@ return false; } -bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const { +bool DWARFVerifier::SortedRanges::intersects(const SortedRanges &RHS) const { auto I1 = Ranges.begin(), E1 = Ranges.end(); auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end(); while (I1 != E1 && I2 != E2) { @@ -99,6 +104,27 @@ return false; } +DWARFVerifier::DieRangeInfo::die_range_info_iterator +DWARFVerifier::DieRangeInfo::insert(const DieRangeInfo &RI) { + auto End = Children.end(); + auto Iter = Children.begin(); + while (Iter != End) { + if (Iter->intersects(RI)) + return Iter; + ++Iter; + } + Children.insert(RI); + return Children.end(); +} + +bool DWARFVerifier::DieRangeInfo::contains(const DieRangeInfo &RHS) const { + return Ranges.contains(RHS.Ranges); +} + +bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const { + return Ranges.intersects(RHS.Ranges); +} + bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, uint64_t *Offset, unsigned UnitIndex, uint8_t &UnitType, bool &isUnitDWARF64) { @@ -401,6 +427,11 @@ if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) { bool DumpDieAfterError = false; for (auto Range : Ranges) { + if (rangeIsStripped(Range)) { + OS << "warning: ignoring stripped address range " << Range << "\n"; + DumpDieAfterError = true; + continue; + } if (!Range.valid()) { ++NumErrors; error() << "Invalid address range " << Range << "\n"; @@ -414,7 +445,7 @@ // to have. Compile units often have DW_AT_ranges that can contain one or // more dead stripped address ranges which tend to all be at the same // address: 0 or -1. - if (auto PrevRange = RI.insert(Range)) { + if (auto PrevRange = RI.Ranges.insert(Range)) { ++NumErrors; error() << "DIE has overlapping ranges in DW_AT_ranges attribute: " << *PrevRange << " and " << Range << '\n'; diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -485,7 +485,8 @@ raw_ostream &stream = Quiet ? nulls() : OS; stream << "Verifying " << Filename.str() << ":\tfile format " << Obj.getFileFormatName() << "\n"; - bool Result = DICtx.verify(stream, getDumpOpts(DICtx)); + + bool Result = DICtx.verify(stream, getDumpOpts(DICtx), &Obj); if (Result) stream << "No errors.\n"; else