diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -231,6 +231,7 @@ llvm::Optional BaseAddr; /// The compile unit debug information entry items. std::vector DieArray; + std::vector LastChildren; /// Map from range's start address to end address and corresponding DIE. /// IntervalMap does not support range removal, as a result, we use the diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -413,9 +414,48 @@ Context.getRecoverableErrorHandler()(std::move(e)); } +template +auto findLastChildren(const In &InRange, const GetDepthFunc &GetDepth, + const HasChildrenPred &HasChildren, + const IsEndTagPred &IsEndTag) { + using DepthType = decltype(GetDepth(*InRange.begin())); + using IndexType = typename In::size_type; + + std::vector LastChildren(InRange.size(), 0); + + std::stack> ParentIdxs; + for (IndexType I = 0, EndIdx = InRange.size(); I < EndIdx; ++I) { + if (!ParentIdxs.empty()) { + DepthType CurrentDepth = GetDepth(InRange[I]); + + DepthType StackSize = ParentIdxs.size(); + if (CurrentDepth <= StackSize) { + // a child of the current parent + while (CurrentDepth < StackSize--) { + ParentIdxs.pop(); + } + + assert(!LastChildren[ParentIdxs.top()]); + + if (IsEndTag(InRange[I])) { + // the last child of the current parent + LastChildren[ParentIdxs.top()] = I; + } + } + } + + if (HasChildren(InRange[I])) { + assert(GetDepth(InRange[I]) == ParentIdxs.size()); + ParentIdxs.push(I); + } + } + + return LastChildren; +} + Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { - if ((CUDieOnly && !DieArray.empty()) || - DieArray.size() > 1) + if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1) return Error::success(); // Already parsed. bool HasCUDie = !DieArray.empty(); @@ -512,6 +552,13 @@ isLittleEndian, getAddressByteSize())); } + LastChildren = + findLastChildren(DieArray, std::mem_fn(&DWARFDebugInfoEntry::getDepth), + std::mem_fn(&DWARFDebugInfoEntry::hasChildren), + [](const DWARFDebugInfoEntry &DIE) { + return DIE.getTag() == dwarf::DW_TAG_null; + }); + // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for // skeleton CU DIE, so that DWARF users not aware of it are not broken. return Error::success(); @@ -782,15 +829,16 @@ if (!Die->hasChildren()) return DWARFDie(); - uint32_t Depth = Die->getDepth(); - for (size_t I = getDIEIndex(Die) + 1, EndIdx = DieArray.size(); I < EndIdx; - ++I) { - if (DieArray[I].getDepth() == Depth + 1 && - DieArray[I].getTag() == dwarf::DW_TAG_null) - return DWARFDie(this, &DieArray[I]); - assert(DieArray[I].getDepth() > Depth && "Not processing children?"); + // We do not want access out of bounds when parsing corrupted debug data. + size_t I = getDIEIndex(Die); + if (I >= LastChildren.size()) + return DWARFDie(); + size_t LastChild = LastChildren[I]; + if (!LastChild) { + // TODO Shouldn't Die->hasChildren() be false in that case? + return DWARFDie(); } - return DWARFDie(); + return DWARFDie(this, &DieArray[LastChild]); } const DWARFAbbreviationDeclarationSet *DWARFUnit::getAbbreviations() const {