diff options
Diffstat (limited to 'contrib/llvm/lib/DebugInfo/DWARF')
24 files changed, 2870 insertions, 1464 deletions
diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp index f593953c62ff..adada672af00 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include <cstddef> #include <cstdint> @@ -96,8 +97,7 @@ DWARFAbbreviationDeclaration::extract(DataExtractor Data, default: // The form has a byte size that doesn't depend on Params. // If it's a fixed size, keep track of it. - if ((ByteSize = - DWARFFormValue::getFixedByteSize(F, DWARFFormParams()))) { + if ((ByteSize = dwarf::getFixedFormByteSize(F, dwarf::FormParams()))) { if (FixedAttributeSize) FixedAttributeSize->NumBytes += *ByteSize; break; @@ -127,26 +127,11 @@ DWARFAbbreviationDeclaration::extract(DataExtractor Data, } void DWARFAbbreviationDeclaration::dump(raw_ostream &OS) const { - auto tagString = TagString(getTag()); OS << '[' << getCode() << "] "; - if (!tagString.empty()) - OS << tagString; - else - OS << format("DW_TAG_Unknown_%x", getTag()); + OS << formatv("{0}", getTag()); OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no") << '\n'; for (const AttributeSpec &Spec : AttributeSpecs) { - OS << '\t'; - auto attrString = AttributeString(Spec.Attr); - if (!attrString.empty()) - OS << attrString; - else - OS << format("DW_AT_Unknown_%x", Spec.Attr); - OS << '\t'; - auto formString = FormEncodingString(Spec.Form); - if (!formString.empty()) - OS << formString; - else - OS << format("DW_FORM_Unknown_%x", Spec.Form); + OS << formatv("\t{0}\t{1}", Spec.Attr, Spec.Form); if (Spec.isImplicitConst()) OS << '\t' << Spec.getImplicitConstValue(); OS << '\n'; @@ -217,8 +202,7 @@ Optional<int64_t> DWARFAbbreviationDeclaration::AttributeSpec::getByteSize( if (ByteSize.HasByteSize) return ByteSize.ByteSize; Optional<int64_t> S; - auto FixedByteSize = - DWARFFormValue::getFixedByteSize(Form, U.getFormParams()); + auto FixedByteSize = dwarf::getFixedFormByteSize(Form, U.getFormParams()); if (FixedByteSize) S = *FixedByteSize; return S; diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index 6a6b7fc6fc20..4582e036f9fc 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -13,7 +13,10 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/DJB.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" #include <cstddef> #include <cstdint> @@ -21,7 +24,24 @@ using namespace llvm; -llvm::Error DWARFAcceleratorTable::extract() { +namespace { +struct Atom { + unsigned Value; +}; + +static raw_ostream &operator<<(raw_ostream &OS, const Atom &A) { + StringRef Str = dwarf::AtomTypeString(A.Value); + if (!Str.empty()) + return OS << Str; + return OS << "DW_ATOM_unknown_" << format("%x", A.Value); +} +} // namespace + +static Atom formatAtom(unsigned Atom) { return {Atom}; } + +DWARFAcceleratorTable::~DWARFAcceleratorTable() = default; + +llvm::Error AppleAcceleratorTable::extract() { uint32_t Offset = 0; // Check that we can at least read the header. @@ -32,8 +52,8 @@ llvm::Error DWARFAcceleratorTable::extract() { Hdr.Magic = AccelSection.getU32(&Offset); Hdr.Version = AccelSection.getU16(&Offset); Hdr.HashFunction = AccelSection.getU16(&Offset); - Hdr.NumBuckets = AccelSection.getU32(&Offset); - Hdr.NumHashes = AccelSection.getU32(&Offset); + Hdr.BucketCount = AccelSection.getU32(&Offset); + Hdr.HashCount = AccelSection.getU32(&Offset); Hdr.HeaderDataLength = AccelSection.getU32(&Offset); // Check that we can read all the hashes and offsets from the @@ -41,7 +61,7 @@ llvm::Error DWARFAcceleratorTable::extract() { // We need to substract one because we're checking for an *offset* which is // equal to the size for an empty table and hence pointer after the section. if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength + - Hdr.NumBuckets * 4 + Hdr.NumHashes * 8 - 1)) + Hdr.BucketCount * 4 + Hdr.HashCount * 8 - 1)) return make_error<StringError>( "Section too small: cannot read buckets and hashes.", inconvertibleErrorCode()); @@ -59,20 +79,20 @@ llvm::Error DWARFAcceleratorTable::extract() { return Error::success(); } -uint32_t DWARFAcceleratorTable::getNumBuckets() { return Hdr.NumBuckets; } -uint32_t DWARFAcceleratorTable::getNumHashes() { return Hdr.NumHashes; } -uint32_t DWARFAcceleratorTable::getSizeHdr() { return sizeof(Hdr); } -uint32_t DWARFAcceleratorTable::getHeaderDataLength() { +uint32_t AppleAcceleratorTable::getNumBuckets() { return Hdr.BucketCount; } +uint32_t AppleAcceleratorTable::getNumHashes() { return Hdr.HashCount; } +uint32_t AppleAcceleratorTable::getSizeHdr() { return sizeof(Hdr); } +uint32_t AppleAcceleratorTable::getHeaderDataLength() { return Hdr.HeaderDataLength; } -ArrayRef<std::pair<DWARFAcceleratorTable::HeaderData::AtomType, - DWARFAcceleratorTable::HeaderData::Form>> -DWARFAcceleratorTable::getAtomsDesc() { +ArrayRef<std::pair<AppleAcceleratorTable::HeaderData::AtomType, + AppleAcceleratorTable::HeaderData::Form>> +AppleAcceleratorTable::getAtomsDesc() { return HdrData.Atoms; } -bool DWARFAcceleratorTable::validateForms() { +bool AppleAcceleratorTable::validateForms() { for (auto Atom : getAtomsDesc()) { DWARFFormValue FormValue(Atom.second); switch (Atom.first) { @@ -92,10 +112,10 @@ bool DWARFAcceleratorTable::validateForms() { } std::pair<uint32_t, dwarf::Tag> -DWARFAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { +AppleAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { uint32_t DieOffset = dwarf::DW_INVALID_OFFSET; dwarf::Tag DieTag = dwarf::DW_TAG_null; - DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (auto Atom : getAtomsDesc()) { DWARFFormValue FormValue(Atom.second); @@ -114,144 +134,219 @@ DWARFAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { return {DieOffset, DieTag}; } -LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { +void AppleAcceleratorTable::Header::dump(ScopedPrinter &W) const { + DictScope HeaderScope(W, "Header"); + W.printHex("Magic", Magic); + W.printHex("Version", Version); + W.printHex("Hash function", HashFunction); + W.printNumber("Bucket count", BucketCount); + W.printNumber("Hashes count", HashCount); + W.printNumber("HeaderData length", HeaderDataLength); +} + +Optional<uint64_t> AppleAcceleratorTable::HeaderData::extractOffset( + Optional<DWARFFormValue> Value) const { + if (!Value) + return None; + + switch (Value->getForm()) { + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + case dwarf::DW_FORM_ref_udata: + return Value->getRawUValue() + DIEOffsetBase; + default: + return Value->getAsSectionOffset(); + } +} + +bool AppleAcceleratorTable::dumpName(ScopedPrinter &W, + SmallVectorImpl<DWARFFormValue> &AtomForms, + uint32_t *DataOffset) const { + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + uint32_t NameOffset = *DataOffset; + if (!AccelSection.isValidOffsetForDataOfSize(*DataOffset, 4)) { + W.printString("Incorrectly terminated list."); + return false; + } + unsigned StringOffset = AccelSection.getRelocatedValue(4, DataOffset); + if (!StringOffset) + return false; // End of list + + DictScope NameScope(W, ("Name@0x" + Twine::utohexstr(NameOffset)).str()); + W.startLine() << format("String: 0x%08x", StringOffset); + W.getOStream() << " \"" << StringSection.getCStr(&StringOffset) << "\"\n"; + + unsigned NumData = AccelSection.getU32(DataOffset); + for (unsigned Data = 0; Data < NumData; ++Data) { + ListScope DataScope(W, ("Data " + Twine(Data)).str()); + unsigned i = 0; + for (auto &Atom : AtomForms) { + W.startLine() << format("Atom[%d]: ", i); + if (Atom.extractValue(AccelSection, DataOffset, FormParams)) { + Atom.dump(W.getOStream()); + if (Optional<uint64_t> Val = Atom.getAsUnsignedConstant()) { + StringRef Str = dwarf::AtomValueString(HdrData.Atoms[i].first, *Val); + if (!Str.empty()) + W.getOStream() << " (" << Str << ")"; + } + } else + W.getOStream() << "Error extracting the value"; + W.getOStream() << "\n"; + i++; + } + } + return true; // more entries follow +} + +LLVM_DUMP_METHOD void AppleAcceleratorTable::dump(raw_ostream &OS) const { if (!IsValid) return; - // Dump the header. - OS << "Magic = " << format("0x%08x", Hdr.Magic) << '\n' - << "Version = " << format("0x%04x", Hdr.Version) << '\n' - << "Hash function = " << format("0x%08x", Hdr.HashFunction) << '\n' - << "Bucket count = " << Hdr.NumBuckets << '\n' - << "Hashes count = " << Hdr.NumHashes << '\n' - << "HeaderData length = " << Hdr.HeaderDataLength << '\n' - << "DIE offset base = " << HdrData.DIEOffsetBase << '\n' - << "Number of atoms = " << HdrData.Atoms.size() << '\n'; - - unsigned i = 0; + ScopedPrinter W(OS); + + Hdr.dump(W); + + W.printNumber("DIE offset base", HdrData.DIEOffsetBase); + W.printNumber("Number of atoms", uint64_t(HdrData.Atoms.size())); SmallVector<DWARFFormValue, 3> AtomForms; - for (const auto &Atom: HdrData.Atoms) { - OS << format("Atom[%d] Type: ", i++); - auto TypeString = dwarf::AtomTypeString(Atom.first); - if (!TypeString.empty()) - OS << TypeString; - else - OS << format("DW_ATOM_Unknown_0x%x", Atom.first); - OS << " Form: "; - auto FormString = dwarf::FormEncodingString(Atom.second); - if (!FormString.empty()) - OS << FormString; - else - OS << format("DW_FORM_Unknown_0x%x", Atom.second); - OS << '\n'; - AtomForms.push_back(DWARFFormValue(Atom.second)); + { + ListScope AtomsScope(W, "Atoms"); + unsigned i = 0; + for (const auto &Atom : HdrData.Atoms) { + DictScope AtomScope(W, ("Atom " + Twine(i++)).str()); + W.startLine() << "Type: " << formatAtom(Atom.first) << '\n'; + W.startLine() << "Form: " << formatv("{0}", Atom.second) << '\n'; + AtomForms.push_back(DWARFFormValue(Atom.second)); + } } // Now go through the actual tables and dump them. uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength; - unsigned HashesBase = Offset + Hdr.NumBuckets * 4; - unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4; - DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + unsigned HashesBase = Offset + Hdr.BucketCount * 4; + unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4; - for (unsigned Bucket = 0; Bucket < Hdr.NumBuckets; ++Bucket) { + for (unsigned Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) { unsigned Index = AccelSection.getU32(&Offset); - OS << format("Bucket[%d]\n", Bucket); + ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); if (Index == UINT32_MAX) { - OS << " EMPTY\n"; + W.printString("EMPTY"); continue; } - for (unsigned HashIdx = Index; HashIdx < Hdr.NumHashes; ++HashIdx) { + for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) { unsigned HashOffset = HashesBase + HashIdx*4; unsigned OffsetsOffset = OffsetsBase + HashIdx*4; uint32_t Hash = AccelSection.getU32(&HashOffset); - if (Hash % Hdr.NumBuckets != Bucket) + if (Hash % Hdr.BucketCount != Bucket) break; unsigned DataOffset = AccelSection.getU32(&OffsetsOffset); - OS << format(" Hash = 0x%08x Offset = 0x%08x\n", Hash, DataOffset); + ListScope HashScope(W, ("Hash 0x" + Twine::utohexstr(Hash)).str()); if (!AccelSection.isValidOffset(DataOffset)) { - OS << " Invalid section offset\n"; + W.printString("Invalid section offset"); continue; } - while (AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) { - unsigned StringOffset = AccelSection.getRelocatedValue(4, &DataOffset); - if (!StringOffset) - break; - OS << format(" Name: %08x \"%s\"\n", StringOffset, - StringSection.getCStr(&StringOffset)); - unsigned NumData = AccelSection.getU32(&DataOffset); - for (unsigned Data = 0; Data < NumData; ++Data) { - OS << format(" Data[%d] => ", Data); - unsigned i = 0; - for (auto &Atom : AtomForms) { - OS << format("{Atom[%d]: ", i++); - if (Atom.extractValue(AccelSection, &DataOffset, FormParams)) - Atom.dump(OS); - else - OS << "Error extracting the value"; - OS << "} "; - } - OS << '\n'; - } - } + while (dumpName(W, AtomForms, &DataOffset)) + /*empty*/; } } } -DWARFAcceleratorTable::ValueIterator::ValueIterator( - const DWARFAcceleratorTable &AccelTable, unsigned Offset) - : AccelTable(&AccelTable), DataOffset(Offset) { +AppleAcceleratorTable::Entry::Entry( + const AppleAcceleratorTable::HeaderData &HdrData) + : HdrData(&HdrData) { + Values.reserve(HdrData.Atoms.size()); + for (const auto &Atom : HdrData.Atoms) + Values.push_back(DWARFFormValue(Atom.second)); +} + +void AppleAcceleratorTable::Entry::extract( + const AppleAcceleratorTable &AccelTable, uint32_t *Offset) { + + dwarf::FormParams FormParams = {AccelTable.Hdr.Version, 0, + dwarf::DwarfFormat::DWARF32}; + for (auto &Atom : Values) + Atom.extractValue(AccelTable.AccelSection, Offset, FormParams); +} + +Optional<DWARFFormValue> +AppleAcceleratorTable::Entry::lookup(HeaderData::AtomType Atom) const { + assert(HdrData && "Dereferencing end iterator?"); + assert(HdrData->Atoms.size() == Values.size()); + for (const auto &Tuple : zip_first(HdrData->Atoms, Values)) { + if (std::get<0>(Tuple).first == Atom) + return std::get<1>(Tuple); + } + return None; +} + +Optional<uint64_t> AppleAcceleratorTable::Entry::getDIESectionOffset() const { + return HdrData->extractOffset(lookup(dwarf::DW_ATOM_die_offset)); +} + +Optional<uint64_t> AppleAcceleratorTable::Entry::getCUOffset() const { + return HdrData->extractOffset(lookup(dwarf::DW_ATOM_cu_offset)); +} + +Optional<dwarf::Tag> AppleAcceleratorTable::Entry::getTag() const { + Optional<DWARFFormValue> Tag = lookup(dwarf::DW_ATOM_die_tag); + if (!Tag) + return None; + if (Optional<uint64_t> Value = Tag->getAsUnsignedConstant()) + return dwarf::Tag(*Value); + return None; +} + +AppleAcceleratorTable::ValueIterator::ValueIterator( + const AppleAcceleratorTable &AccelTable, unsigned Offset) + : AccelTable(&AccelTable), Current(AccelTable.HdrData), DataOffset(Offset) { if (!AccelTable.AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) return; - for (const auto &Atom : AccelTable.HdrData.Atoms) - AtomForms.push_back(DWARFFormValue(Atom.second)); - // Read the first entry. NumData = AccelTable.AccelSection.getU32(&DataOffset); Next(); } -void DWARFAcceleratorTable::ValueIterator::Next() { +void AppleAcceleratorTable::ValueIterator::Next() { assert(NumData > 0 && "attempted to increment iterator past the end"); auto &AccelSection = AccelTable->AccelSection; if (Data >= NumData || !AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) { NumData = 0; + DataOffset = 0; return; } - DWARFFormParams FormParams = {AccelTable->Hdr.Version, 0, - dwarf::DwarfFormat::DWARF32}; - for (auto &Atom : AtomForms) - Atom.extractValue(AccelSection, &DataOffset, FormParams); + Current.extract(*AccelTable, &DataOffset); ++Data; } -iterator_range<DWARFAcceleratorTable::ValueIterator> -DWARFAcceleratorTable::equal_range(StringRef Key) const { +iterator_range<AppleAcceleratorTable::ValueIterator> +AppleAcceleratorTable::equal_range(StringRef Key) const { if (!IsValid) return make_range(ValueIterator(), ValueIterator()); // Find the bucket. - unsigned HashValue = dwarf::djbHash(Key); - unsigned Bucket = HashValue % Hdr.NumBuckets; + unsigned HashValue = djbHash(Key); + unsigned Bucket = HashValue % Hdr.BucketCount; unsigned BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength; - unsigned HashesBase = BucketBase + Hdr.NumBuckets * 4; - unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4; + unsigned HashesBase = BucketBase + Hdr.BucketCount * 4; + unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4; unsigned BucketOffset = BucketBase + Bucket * 4; unsigned Index = AccelSection.getU32(&BucketOffset); // Search through all hashes in the bucket. - for (unsigned HashIdx = Index; HashIdx < Hdr.NumHashes; ++HashIdx) { + for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) { unsigned HashOffset = HashesBase + HashIdx * 4; unsigned OffsetsOffset = OffsetsBase + HashIdx * 4; uint32_t Hash = AccelSection.getU32(&HashOffset); - if (Hash % Hdr.NumBuckets != Bucket) + if (Hash % Hdr.BucketCount != Bucket) // We are already in the next bucket. break; @@ -266,3 +361,529 @@ DWARFAcceleratorTable::equal_range(StringRef Key) const { } return make_range(ValueIterator(), ValueIterator()); } + +void DWARFDebugNames::Header::dump(ScopedPrinter &W) const { + DictScope HeaderScope(W, "Header"); + W.printHex("Length", UnitLength); + W.printNumber("Version", Version); + W.printHex("Padding", Padding); + W.printNumber("CU count", CompUnitCount); + W.printNumber("Local TU count", LocalTypeUnitCount); + W.printNumber("Foreign TU count", ForeignTypeUnitCount); + W.printNumber("Bucket count", BucketCount); + W.printNumber("Name count", NameCount); + W.printHex("Abbreviations table size", AbbrevTableSize); + W.startLine() << "Augmentation: '" << AugmentationString << "'\n"; +} + +llvm::Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS, + uint32_t *Offset) { + // Check that we can read the fixed-size part. + if (!AS.isValidOffset(*Offset + sizeof(HeaderPOD) - 1)) + return make_error<StringError>("Section too small: cannot read header.", + inconvertibleErrorCode()); + + UnitLength = AS.getU32(Offset); + Version = AS.getU16(Offset); + Padding = AS.getU16(Offset); + CompUnitCount = AS.getU32(Offset); + LocalTypeUnitCount = AS.getU32(Offset); + ForeignTypeUnitCount = AS.getU32(Offset); + BucketCount = AS.getU32(Offset); + NameCount = AS.getU32(Offset); + AbbrevTableSize = AS.getU32(Offset); + AugmentationStringSize = alignTo(AS.getU32(Offset), 4); + + if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize)) + return make_error<StringError>( + "Section too small: cannot read header augmentation.", + inconvertibleErrorCode()); + AugmentationString.resize(AugmentationStringSize); + AS.getU8(Offset, reinterpret_cast<uint8_t *>(AugmentationString.data()), + AugmentationStringSize); + return Error::success(); +} + +void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const { + DictScope AbbrevScope(W, ("Abbreviation 0x" + Twine::utohexstr(Code)).str()); + W.startLine() << formatv("Tag: {0}\n", Tag); + + for (const auto &Attr : Attributes) + W.startLine() << formatv("{0}: {1}\n", Attr.Index, Attr.Form); +} + +static constexpr DWARFDebugNames::AttributeEncoding sentinelAttrEnc() { + return {dwarf::Index(0), dwarf::Form(0)}; +} + +static bool isSentinel(const DWARFDebugNames::AttributeEncoding &AE) { + return AE == sentinelAttrEnc(); +} + +static DWARFDebugNames::Abbrev sentinelAbbrev() { + return DWARFDebugNames::Abbrev(0, dwarf::Tag(0), {}); +} + +static bool isSentinel(const DWARFDebugNames::Abbrev &Abbr) { + return Abbr.Code == 0; +} + +DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getEmptyKey() { + return sentinelAbbrev(); +} + +DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getTombstoneKey() { + return DWARFDebugNames::Abbrev(~0, dwarf::Tag(0), {}); +} + +Expected<DWARFDebugNames::AttributeEncoding> +DWARFDebugNames::NameIndex::extractAttributeEncoding(uint32_t *Offset) { + if (*Offset >= EntriesBase) { + return make_error<StringError>("Incorrectly terminated abbreviation table.", + inconvertibleErrorCode()); + } + + uint32_t Index = Section.AccelSection.getULEB128(Offset); + uint32_t Form = Section.AccelSection.getULEB128(Offset); + return AttributeEncoding(dwarf::Index(Index), dwarf::Form(Form)); +} + +Expected<std::vector<DWARFDebugNames::AttributeEncoding>> +DWARFDebugNames::NameIndex::extractAttributeEncodings(uint32_t *Offset) { + std::vector<AttributeEncoding> Result; + for (;;) { + auto AttrEncOr = extractAttributeEncoding(Offset); + if (!AttrEncOr) + return AttrEncOr.takeError(); + if (isSentinel(*AttrEncOr)) + return std::move(Result); + + Result.emplace_back(*AttrEncOr); + } +} + +Expected<DWARFDebugNames::Abbrev> +DWARFDebugNames::NameIndex::extractAbbrev(uint32_t *Offset) { + if (*Offset >= EntriesBase) { + return make_error<StringError>("Incorrectly terminated abbreviation table.", + inconvertibleErrorCode()); + } + + uint32_t Code = Section.AccelSection.getULEB128(Offset); + if (Code == 0) + return sentinelAbbrev(); + + uint32_t Tag = Section.AccelSection.getULEB128(Offset); + auto AttrEncOr = extractAttributeEncodings(Offset); + if (!AttrEncOr) + return AttrEncOr.takeError(); + return Abbrev(Code, dwarf::Tag(Tag), std::move(*AttrEncOr)); +} + +Error DWARFDebugNames::NameIndex::extract() { + const DWARFDataExtractor &AS = Section.AccelSection; + uint32_t Offset = Base; + if (Error E = Hdr.extract(AS, &Offset)) + return E; + + CUsBase = Offset; + Offset += Hdr.CompUnitCount * 4; + Offset += Hdr.LocalTypeUnitCount * 4; + Offset += Hdr.ForeignTypeUnitCount * 8; + BucketsBase = Offset; + Offset += Hdr.BucketCount * 4; + HashesBase = Offset; + if (Hdr.BucketCount > 0) + Offset += Hdr.NameCount * 4; + StringOffsetsBase = Offset; + Offset += Hdr.NameCount * 4; + EntryOffsetsBase = Offset; + Offset += Hdr.NameCount * 4; + + if (!AS.isValidOffsetForDataOfSize(Offset, Hdr.AbbrevTableSize)) + return make_error<StringError>( + "Section too small: cannot read abbreviations.", + inconvertibleErrorCode()); + + EntriesBase = Offset + Hdr.AbbrevTableSize; + + for (;;) { + auto AbbrevOr = extractAbbrev(&Offset); + if (!AbbrevOr) + return AbbrevOr.takeError(); + if (isSentinel(*AbbrevOr)) + return Error::success(); + + if (!Abbrevs.insert(std::move(*AbbrevOr)).second) { + return make_error<StringError>("Duplicate abbreviation code.", + inconvertibleErrorCode()); + } + } +} +DWARFDebugNames::Entry::Entry(const NameIndex &NameIdx, const Abbrev &Abbr) + : NameIdx(&NameIdx), Abbr(&Abbr) { + // This merely creates form values. It is up to the caller + // (NameIndex::getEntry) to populate them. + Values.reserve(Abbr.Attributes.size()); + for (const auto &Attr : Abbr.Attributes) + Values.emplace_back(Attr.Form); +} + +Optional<DWARFFormValue> +DWARFDebugNames::Entry::lookup(dwarf::Index Index) const { + assert(Abbr->Attributes.size() == Values.size()); + for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { + if (std::get<0>(Tuple).Index == Index) + return std::get<1>(Tuple); + } + return None; +} + +Optional<uint64_t> DWARFDebugNames::Entry::getDIEUnitOffset() const { + if (Optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_die_offset)) + return Off->getAsReferenceUVal(); + return None; +} + +Optional<uint64_t> DWARFDebugNames::Entry::getCUIndex() const { + if (Optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_compile_unit)) + return Off->getAsUnsignedConstant(); + // In a per-CU index, the entries without a DW_IDX_compile_unit attribute + // implicitly refer to the single CU. + if (NameIdx->getCUCount() == 1) + return 0; + return None; +} + +Optional<uint64_t> DWARFDebugNames::Entry::getCUOffset() const { + Optional<uint64_t> Index = getCUIndex(); + if (!Index || *Index >= NameIdx->getCUCount()) + return None; + return NameIdx->getCUOffset(*Index); +} + +void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const { + W.printHex("Abbrev", Abbr->Code); + W.startLine() << formatv("Tag: {0}\n", Abbr->Tag); + assert(Abbr->Attributes.size() == Values.size()); + for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { + W.startLine() << formatv("{0}: ", std::get<0>(Tuple).Index); + std::get<1>(Tuple).dump(W.getOStream()); + W.getOStream() << '\n'; + } +} + +char DWARFDebugNames::SentinelError::ID; +std::error_code DWARFDebugNames::SentinelError::convertToErrorCode() const { + return inconvertibleErrorCode(); +} + +uint32_t DWARFDebugNames::NameIndex::getCUOffset(uint32_t CU) const { + assert(CU < Hdr.CompUnitCount); + uint32_t Offset = CUsBase + 4 * CU; + return Section.AccelSection.getRelocatedValue(4, &Offset); +} + +uint32_t DWARFDebugNames::NameIndex::getLocalTUOffset(uint32_t TU) const { + assert(TU < Hdr.LocalTypeUnitCount); + uint32_t Offset = CUsBase + Hdr.CompUnitCount * 4; + return Section.AccelSection.getRelocatedValue(4, &Offset); +} + +uint64_t DWARFDebugNames::NameIndex::getForeignTUSignature(uint32_t TU) const { + assert(TU < Hdr.ForeignTypeUnitCount); + uint32_t Offset = CUsBase + (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) * 4; + return Section.AccelSection.getU64(&Offset); +} + +Expected<DWARFDebugNames::Entry> +DWARFDebugNames::NameIndex::getEntry(uint32_t *Offset) const { + const DWARFDataExtractor &AS = Section.AccelSection; + if (!AS.isValidOffset(*Offset)) + return make_error<StringError>("Incorrectly terminated entry list.", + inconvertibleErrorCode()); + + uint32_t AbbrevCode = AS.getULEB128(Offset); + if (AbbrevCode == 0) + return make_error<SentinelError>(); + + const auto AbbrevIt = Abbrevs.find_as(AbbrevCode); + if (AbbrevIt == Abbrevs.end()) + return make_error<StringError>("Invalid abbreviation.", + inconvertibleErrorCode()); + + Entry E(*this, *AbbrevIt); + + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + for (auto &Value : E.Values) { + if (!Value.extractValue(AS, Offset, FormParams)) + return make_error<StringError>("Error extracting index attribute values.", + inconvertibleErrorCode()); + } + return std::move(E); +} + +DWARFDebugNames::NameTableEntry +DWARFDebugNames::NameIndex::getNameTableEntry(uint32_t Index) const { + assert(0 < Index && Index <= Hdr.NameCount); + uint32_t StringOffsetOffset = StringOffsetsBase + 4 * (Index - 1); + uint32_t EntryOffsetOffset = EntryOffsetsBase + 4 * (Index - 1); + const DWARFDataExtractor &AS = Section.AccelSection; + + uint32_t StringOffset = AS.getRelocatedValue(4, &StringOffsetOffset); + uint32_t EntryOffset = AS.getU32(&EntryOffsetOffset); + EntryOffset += EntriesBase; + return {Section.StringSection, Index, StringOffset, EntryOffset}; +} + +uint32_t +DWARFDebugNames::NameIndex::getBucketArrayEntry(uint32_t Bucket) const { + assert(Bucket < Hdr.BucketCount); + uint32_t BucketOffset = BucketsBase + 4 * Bucket; + return Section.AccelSection.getU32(&BucketOffset); +} + +uint32_t DWARFDebugNames::NameIndex::getHashArrayEntry(uint32_t Index) const { + assert(0 < Index && Index <= Hdr.NameCount); + uint32_t HashOffset = HashesBase + 4 * (Index - 1); + return Section.AccelSection.getU32(&HashOffset); +} + +// Returns true if we should continue scanning for entries, false if this is the +// last (sentinel) entry). In case of a parsing error we also return false, as +// it's not possible to recover this entry list (but the other lists may still +// parse OK). +bool DWARFDebugNames::NameIndex::dumpEntry(ScopedPrinter &W, + uint32_t *Offset) const { + uint32_t EntryId = *Offset; + auto EntryOr = getEntry(Offset); + if (!EntryOr) { + handleAllErrors(EntryOr.takeError(), [](const SentinelError &) {}, + [&W](const ErrorInfoBase &EI) { EI.log(W.startLine()); }); + return false; + } + + DictScope EntryScope(W, ("Entry @ 0x" + Twine::utohexstr(EntryId)).str()); + EntryOr->dump(W); + return true; +} + +void DWARFDebugNames::NameIndex::dumpName(ScopedPrinter &W, + const NameTableEntry &NTE, + Optional<uint32_t> Hash) const { + DictScope NameScope(W, ("Name " + Twine(NTE.getIndex())).str()); + if (Hash) + W.printHex("Hash", *Hash); + + W.startLine() << format("String: 0x%08x", NTE.getStringOffset()); + W.getOStream() << " \"" << NTE.getString() << "\"\n"; + + uint32_t EntryOffset = NTE.getEntryOffset(); + while (dumpEntry(W, &EntryOffset)) + /*empty*/; +} + +void DWARFDebugNames::NameIndex::dumpCUs(ScopedPrinter &W) const { + ListScope CUScope(W, "Compilation Unit offsets"); + for (uint32_t CU = 0; CU < Hdr.CompUnitCount; ++CU) + W.startLine() << format("CU[%u]: 0x%08x\n", CU, getCUOffset(CU)); +} + +void DWARFDebugNames::NameIndex::dumpLocalTUs(ScopedPrinter &W) const { + if (Hdr.LocalTypeUnitCount == 0) + return; + + ListScope TUScope(W, "Local Type Unit offsets"); + for (uint32_t TU = 0; TU < Hdr.LocalTypeUnitCount; ++TU) + W.startLine() << format("LocalTU[%u]: 0x%08x\n", TU, getLocalTUOffset(TU)); +} + +void DWARFDebugNames::NameIndex::dumpForeignTUs(ScopedPrinter &W) const { + if (Hdr.ForeignTypeUnitCount == 0) + return; + + ListScope TUScope(W, "Foreign Type Unit signatures"); + for (uint32_t TU = 0; TU < Hdr.ForeignTypeUnitCount; ++TU) { + W.startLine() << format("ForeignTU[%u]: 0x%016" PRIx64 "\n", TU, + getForeignTUSignature(TU)); + } +} + +void DWARFDebugNames::NameIndex::dumpAbbreviations(ScopedPrinter &W) const { + ListScope AbbrevsScope(W, "Abbreviations"); + for (const auto &Abbr : Abbrevs) + Abbr.dump(W); +} + +void DWARFDebugNames::NameIndex::dumpBucket(ScopedPrinter &W, + uint32_t Bucket) const { + ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); + uint32_t Index = getBucketArrayEntry(Bucket); + if (Index == 0) { + W.printString("EMPTY"); + return; + } + if (Index > Hdr.NameCount) { + W.printString("Name index is invalid"); + return; + } + + for (; Index <= Hdr.NameCount; ++Index) { + uint32_t Hash = getHashArrayEntry(Index); + if (Hash % Hdr.BucketCount != Bucket) + break; + + dumpName(W, getNameTableEntry(Index), Hash); + } +} + +LLVM_DUMP_METHOD void DWARFDebugNames::NameIndex::dump(ScopedPrinter &W) const { + DictScope UnitScope(W, ("Name Index @ 0x" + Twine::utohexstr(Base)).str()); + Hdr.dump(W); + dumpCUs(W); + dumpLocalTUs(W); + dumpForeignTUs(W); + dumpAbbreviations(W); + + if (Hdr.BucketCount > 0) { + for (uint32_t Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) + dumpBucket(W, Bucket); + return; + } + + W.startLine() << "Hash table not present\n"; + for (NameTableEntry NTE : *this) + dumpName(W, NTE, None); +} + +llvm::Error DWARFDebugNames::extract() { + uint32_t Offset = 0; + while (AccelSection.isValidOffset(Offset)) { + NameIndex Next(*this, Offset); + if (llvm::Error E = Next.extract()) + return E; + Offset = Next.getNextUnitOffset(); + NameIndices.push_back(std::move(Next)); + } + return Error::success(); +} + +iterator_range<DWARFDebugNames::ValueIterator> +DWARFDebugNames::NameIndex::equal_range(StringRef Key) const { + return make_range(ValueIterator(*this, Key), ValueIterator()); +} + +LLVM_DUMP_METHOD void DWARFDebugNames::dump(raw_ostream &OS) const { + ScopedPrinter W(OS); + for (const NameIndex &NI : NameIndices) + NI.dump(W); +} + +Optional<uint32_t> +DWARFDebugNames::ValueIterator::findEntryOffsetInCurrentIndex() { + const Header &Hdr = CurrentIndex->Hdr; + if (Hdr.BucketCount == 0) { + // No Hash Table, We need to search through all names in the Name Index. + for (NameTableEntry NTE : *CurrentIndex) { + if (NTE.getString() == Key) + return NTE.getEntryOffset(); + } + return None; + } + + // The Name Index has a Hash Table, so use that to speed up the search. + // Compute the Key Hash, if it has not been done already. + if (!Hash) + Hash = caseFoldingDjbHash(Key); + uint32_t Bucket = *Hash % Hdr.BucketCount; + uint32_t Index = CurrentIndex->getBucketArrayEntry(Bucket); + if (Index == 0) + return None; // Empty bucket + + for (; Index <= Hdr.NameCount; ++Index) { + uint32_t Hash = CurrentIndex->getHashArrayEntry(Index); + if (Hash % Hdr.BucketCount != Bucket) + return None; // End of bucket + + NameTableEntry NTE = CurrentIndex->getNameTableEntry(Index); + if (NTE.getString() == Key) + return NTE.getEntryOffset(); + } + return None; +} + +bool DWARFDebugNames::ValueIterator::getEntryAtCurrentOffset() { + auto EntryOr = CurrentIndex->getEntry(&DataOffset); + if (!EntryOr) { + consumeError(EntryOr.takeError()); + return false; + } + CurrentEntry = std::move(*EntryOr); + return true; +} + +bool DWARFDebugNames::ValueIterator::findInCurrentIndex() { + Optional<uint32_t> Offset = findEntryOffsetInCurrentIndex(); + if (!Offset) + return false; + DataOffset = *Offset; + return getEntryAtCurrentOffset(); +} + +void DWARFDebugNames::ValueIterator::searchFromStartOfCurrentIndex() { + for (const NameIndex *End = CurrentIndex->Section.NameIndices.end(); + CurrentIndex != End; ++CurrentIndex) { + if (findInCurrentIndex()) + return; + } + setEnd(); +} + +void DWARFDebugNames::ValueIterator::next() { + assert(CurrentIndex && "Incrementing an end() iterator?"); + + // First try the next entry in the current Index. + if (getEntryAtCurrentOffset()) + return; + + // If we're a local iterator or we have reached the last Index, we're done. + if (IsLocal || CurrentIndex == &CurrentIndex->Section.NameIndices.back()) { + setEnd(); + return; + } + + // Otherwise, try the next index. + ++CurrentIndex; + searchFromStartOfCurrentIndex(); +} + +DWARFDebugNames::ValueIterator::ValueIterator(const DWARFDebugNames &AccelTable, + StringRef Key) + : CurrentIndex(AccelTable.NameIndices.begin()), IsLocal(false), Key(Key) { + searchFromStartOfCurrentIndex(); +} + +DWARFDebugNames::ValueIterator::ValueIterator( + const DWARFDebugNames::NameIndex &NI, StringRef Key) + : CurrentIndex(&NI), IsLocal(true), Key(Key) { + if (!findInCurrentIndex()) + setEnd(); +} + +iterator_range<DWARFDebugNames::ValueIterator> +DWARFDebugNames::equal_range(StringRef Key) const { + if (NameIndices.empty()) + return make_range(ValueIterator(), ValueIterator()); + return make_range(ValueIterator(*this, Key), ValueIterator()); +} + +const DWARFDebugNames::NameIndex * +DWARFDebugNames::getCUNameIndex(uint32_t CUOffset) { + if (CUToNameIndex.size() == 0 && NameIndices.size() > 0) { + for (const auto &NI : *this) { + for (uint32_t CU = 0; CU < NI.getCUCount(); ++CU) + CUToNameIndex.try_emplace(NI.getCUOffset(CU), &NI); + } + } + return CUToNameIndex.lookup(CUOffset); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp new file mode 100644 index 000000000000..86c8d19c02f4 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp @@ -0,0 +1,29 @@ +//===- DWARFDebugAranges.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void DWARFAddressRange::dump(raw_ostream &OS, uint32_t AddressSize, + DIDumpOptions DumpOpts) const { + + OS << (DumpOpts.DisplayRawContents ? " " : "["); + OS << format("0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, LowPC) + << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, HighPC); + OS << (DumpOpts.DisplayRawContents ? "" : ")"); +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, const DWARFAddressRange &R) { + R.dump(OS, /* AddressSize */ 8); + return OS; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp index 43b235621d18..00a23b3898fa 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp @@ -22,9 +22,10 @@ void DWARFCompileUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { if (getVersion() >= 5) OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); OS << " abbr_offset = " << format("0x%04x", getAbbreviations()->getOffset()) - << " addr_size = " << format("0x%02x", getAddressByteSize()) - << " (next unit at " << format("0x%08x", getNextUnitOffset()) - << ")\n"; + << " addr_size = " << format("0x%02x", getAddressByteSize()); + if (getVersion() >= 5 && getUnitType() != dwarf::DW_UT_compile) + OS << " DWO_id = " << format("0x%016" PRIx64, *getDWOId()); + OS << " (next unit at " << format("0x%08x", getNextUnitOffset()) << ")\n"; if (DWARFDie CUDie = getUnitDIE(false)) CUDie.dump(OS, 0, DumpOpts); diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index eb23ca8229a3..da13c5047f77 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -25,6 +25,7 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" @@ -43,9 +44,11 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstdint> +#include <deque> #include <map> #include <string> #include <utility> @@ -82,7 +85,8 @@ static void dumpUUID(raw_ostream &OS, const ObjectFile &Obj) { OS << "UUID: "; memcpy(&UUID, LC.Ptr+sizeof(LC.C), sizeof(UUID)); OS.write_uuid(UUID); - OS << ' ' << MachO->getFileFormatName(); + Triple T = MachO->getArchTriple(); + OS << " (" << T.getArchName() << ')'; OS << ' ' << MachO->getFileName() << '\n'; } } @@ -106,12 +110,12 @@ collectContributionData(DWARFContext::cu_iterator_range CUs, // Sort the contributions so that any invalid ones are placed at // the start of the contributions vector. This way they are reported // first. - std::sort(Contributions.begin(), Contributions.end(), - [](const Optional<StrOffsetsContributionDescriptor> &L, - const Optional<StrOffsetsContributionDescriptor> &R) { - if (L && R) return L->Base < R->Base; - return R.hasValue(); - }); + llvm::sort(Contributions.begin(), Contributions.end(), + [](const Optional<StrOffsetsContributionDescriptor> &L, + const Optional<StrOffsetsContributionDescriptor> &R) { + if (L && R) return L->Base < R->Base; + return R.hasValue(); + }); // Uniquify contributions, as it is possible that units (specifically // type units in dwo or dwp files) share contributions. We don't want @@ -169,7 +173,11 @@ static void dumpDWARFv5StringOffsetsSection( OS << (ContributionHeader - Offset) << "\n"; } OS << format("0x%8.8x: ", (uint32_t)ContributionHeader); - OS << "Contribution size = " << Contribution->Size + // In DWARF v5 the contribution size in the descriptor does not equal + // the originally encoded length (it does not contain the length of the + // version field and the padding, a total of 4 bytes). Add them back in + // for reporting. + OS << "Contribution size = " << (Contribution->Size + (Version < 5 ? 0 : 4)) << ", Format = " << (Format == DWARF32 ? "DWARF32" : "DWARF64") << ", Version = " << Version << "\n"; @@ -241,26 +249,26 @@ static void dumpStringOffsetsSection( } } -// We want to supply the Unit associated with a .debug_line[.dwo] table when -// we dump it, if possible, but still dump the table even if there isn't a Unit. -// Therefore, collect up handles on all the Units that point into the -// line-table section. -typedef std::map<uint64_t, DWARFUnit *> LineToUnitMap; - -static LineToUnitMap -buildLineToUnitMap(DWARFContext::cu_iterator_range CUs, - DWARFContext::tu_section_iterator_range TUSections) { - LineToUnitMap LineToUnit; - for (const auto &CU : CUs) - if (auto CUDIE = CU->getUnitDIE()) - if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) - LineToUnit.insert(std::make_pair(*StmtOffset, &*CU)); - for (const auto &TUS : TUSections) - for (const auto &TU : TUS) - if (auto TUDIE = TU->getUnitDIE()) - if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list))) - LineToUnit.insert(std::make_pair(*StmtOffset, &*TU)); - return LineToUnit; +// Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). +static void dumpRnglistsSection(raw_ostream &OS, + DWARFDataExtractor &rnglistData, + DIDumpOptions DumpOpts) { + uint32_t Offset = 0; + while (rnglistData.isValidOffset(Offset)) { + llvm::DWARFDebugRnglistTable Rnglists; + uint32_t TableOffset = Offset; + if (Error Err = Rnglists.extract(rnglistData, &Offset)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + uint64_t Length = Rnglists.length(); + // Keep going after an error, if we can, assuming that the length field + // could be read. If it couldn't, stop reading the section. + if (Length == 0) + break; + Offset = TableOffset + Length; + } else { + Rnglists.dump(OS, DumpOpts); + } + } } void DWARFContext::dump( @@ -347,11 +355,11 @@ void DWARFContext::dump( if (shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, DObj->getDebugFrameSection())) - getDebugFrame()->dump(OS, DumpOffset); + getDebugFrame()->dump(OS, getRegisterInfo(), DumpOffset); if (shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, DObj->getEHFrameSection())) - getEHFrame()->dump(OS, DumpOffset); + getEHFrame()->dump(OS, getRegisterInfo(), DumpOffset); if (DumpType & DIDT_DebugMacro) { if (Explicit || !getDebugMacro()->empty()) { @@ -369,63 +377,39 @@ void DWARFContext::dump( set.dump(OS); } - if (shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, - DObj->getLineSection().Data)) { - LineToUnitMap LineToUnit = - buildLineToUnitMap(compile_units(), type_unit_sections()); - unsigned Offset = 0; - DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(), - 0); - while (Offset < LineData.getData().size()) { - DWARFUnit *U = nullptr; - auto It = LineToUnit.find(Offset); - if (It != LineToUnit.end()) - U = It->second; - LineData.setAddressSize(U ? U->getAddressByteSize() : 0); - DWARFDebugLine::LineTable LineTable; - if (DumpOffset && Offset != *DumpOffset) { - // Find the size of this part of the line table section and skip it. - unsigned OldOffset = Offset; - LineTable.Prologue.parse(LineData, &Offset, U); - Offset = OldOffset + LineTable.Prologue.TotalLength + - LineTable.Prologue.sizeofTotalLength(); + auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser, + DIDumpOptions DumpOpts) { + while (!Parser.done()) { + if (DumpOffset && Parser.getOffset() != *DumpOffset) { + Parser.skip(); continue; } - // Verbose dumping is done during parsing and not on the intermediate - // representation. - OS << "debug_line[" << format("0x%8.8x", Offset) << "]\n"; - unsigned OldOffset = Offset; + OS << "debug_line[" << format("0x%8.8x", Parser.getOffset()) << "]\n"; if (DumpOpts.Verbose) { - LineTable.parse(LineData, &Offset, U, &OS); + Parser.parseNext(DWARFDebugLine::warn, DWARFDebugLine::warn, &OS); } else { - LineTable.parse(LineData, &Offset, U); - LineTable.dump(OS); + DWARFDebugLine::LineTable LineTable = Parser.parseNext(); + LineTable.dump(OS, DumpOpts); } - // Check for unparseable prologue, to avoid infinite loops. - if (OldOffset == Offset) - break; } + }; + + if (shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, + DObj->getLineSection().Data)) { + DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(), + 0); + DWARFDebugLine::SectionParser Parser(LineData, *this, compile_units(), + type_unit_sections()); + DumpLineSection(Parser, DumpOpts); } if (shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, DObj->getLineDWOSection().Data)) { - LineToUnitMap LineToUnit = - buildLineToUnitMap(dwo_compile_units(), dwo_type_unit_sections()); - unsigned Offset = 0; DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(), isLittleEndian(), 0); - while (Offset < LineData.getData().size()) { - DWARFUnit *U = nullptr; - auto It = LineToUnit.find(Offset); - if (It != LineToUnit.end()) - U = It->second; - DWARFDebugLine::LineTable LineTable; - unsigned OldOffset = Offset; - if (!LineTable.Prologue.parse(LineData, &Offset, U)) - break; - if (!DumpOffset || OldOffset == *DumpOffset) - LineTable.dump(OS); - } + DWARFDebugLine::SectionParser Parser(LineData, *this, dwo_compile_units(), + dwo_type_unit_sections()); + DumpLineSection(Parser, DumpOpts); } if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, @@ -458,6 +442,18 @@ void DWARFContext::dump( strDWOOffset = offset; } } + if (shouldDump(Explicit, ".debug_line_str", DIDT_ID_DebugLineStr, + DObj->getLineStringSection())) { + DataExtractor strData(DObj->getLineStringSection(), isLittleEndian(), 0); + uint32_t offset = 0; + uint32_t strOffset = 0; + while (const char *s = strData.getCStr(&offset)) { + OS << format("0x%8.8x: \"", strOffset); + OS.write_escaped(s); + OS << "\"\n"; + strOffset = offset; + } + } if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, DObj->getRangeSection().Data)) { @@ -475,8 +471,27 @@ void DWARFContext::dump( isLittleEndian(), savedAddressByteSize); uint32_t offset = 0; DWARFDebugRangeList rangeList; - while (rangeList.extract(rangesData, &offset)) + while (rangesData.isValidOffset(offset)) { + if (Error E = rangeList.extract(rangesData, &offset)) { + WithColor::error() << toString(std::move(E)) << '\n'; + break; + } rangeList.dump(OS); + } + } + + if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, + DObj->getRnglistsSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, DumpOpts); + } + + if (shouldDump(ExplicitDWO, ".debug_rnglists.dwo", DIDT_ID_DebugRnglists, + DObj->getRnglistsDWOSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsDWOSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, DumpOpts); } if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, @@ -534,6 +549,9 @@ void DWARFContext::dump( if (shouldDump(Explicit, ".apple_objc", DIDT_ID_AppleObjC, DObj->getAppleObjCSection().Data)) getAppleObjC().dump(OS); + if (shouldDump(Explicit, ".debug_names", DIDT_ID_DebugNames, + DObj->getDebugNamesSection().Data)) + getDebugNames().dump(OS); } DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { @@ -549,9 +567,19 @@ DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { // probably only one unless this is something like LTO - though an in-process // built/cached lookup table could be used in that case to improve repeated // lookups of different CUs in the DWO. - for (const auto &DWOCU : dwo_compile_units()) + for (const auto &DWOCU : dwo_compile_units()) { + // Might not have parsed DWO ID yet. + if (!DWOCU->getDWOId()) { + if (Optional<uint64_t> DWOId = + toUnsigned(DWOCU->getUnitDIE().find(DW_AT_GNU_dwo_id))) + DWOCU->setDWOId(*DWOId); + else + // No DWO ID? + continue; + } if (DWOCU->getDWOId() == Hash) return DWOCU.get(); + } return nullptr; } @@ -633,7 +661,7 @@ const DWARFDebugLoc *DWARFContext::getDebugLoc() { return Loc.get(); Loc.reset(new DWARFDebugLoc); - // assume all compile units have the same address byte size + // Assume all compile units have the same address byte size. if (getNumCompileUnits()) { DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), getCompileUnitAtIndex(0)->getAddressByteSize()); @@ -646,9 +674,13 @@ const DWARFDebugLocDWO *DWARFContext::getDebugLocDWO() { if (LocDWO) return LocDWO.get(); - DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), 0); LocDWO.reset(new DWARFDebugLocDWO()); - LocDWO->parse(LocData); + // Assume all compile units have the same address byte size. + if (getNumCompileUnits()) { + DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), + getCompileUnitAtIndex(0)->getAddressByteSize()); + LocDWO->parse(LocData); + } return LocDWO.get(); } @@ -674,8 +706,8 @@ const DWARFDebugFrame *DWARFContext::getDebugFrame() { // provides this information). This problem is fixed in DWARFv4 // See this dwarf-discuss discussion for more details: // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html - DataExtractor debugFrameData(DObj->getDebugFrameSection(), isLittleEndian(), - DObj->getAddressSize()); + DWARFDataExtractor debugFrameData(DObj->getDebugFrameSection(), + isLittleEndian(), DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(false /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); @@ -685,8 +717,8 @@ const DWARFDebugFrame *DWARFContext::getEHFrame() { if (EHFrame) return EHFrame.get(); - DataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(), - DObj->getAddressSize()); + DWARFDataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(), + DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(true /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); @@ -702,43 +734,59 @@ const DWARFDebugMacro *DWARFContext::getDebugMacro() { return Macro.get(); } -static DWARFAcceleratorTable & -getAccelTable(std::unique_ptr<DWARFAcceleratorTable> &Cache, - const DWARFObject &Obj, const DWARFSection &Section, - StringRef StringSection, bool IsLittleEndian) { +template <typename T> +static T &getAccelTable(std::unique_ptr<T> &Cache, const DWARFObject &Obj, + const DWARFSection &Section, StringRef StringSection, + bool IsLittleEndian) { if (Cache) return *Cache; DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); DataExtractor StrData(StringSection, IsLittleEndian, 0); - Cache.reset(new DWARFAcceleratorTable(AccelSection, StrData)); + Cache.reset(new T(AccelSection, StrData)); if (Error E = Cache->extract()) llvm::consumeError(std::move(E)); return *Cache; } -const DWARFAcceleratorTable &DWARFContext::getAppleNames() { +const DWARFDebugNames &DWARFContext::getDebugNames() { + return getAccelTable(Names, *DObj, DObj->getDebugNamesSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleNames() { return getAccelTable(AppleNames, *DObj, DObj->getAppleNamesSection(), DObj->getStringSection(), isLittleEndian()); } -const DWARFAcceleratorTable &DWARFContext::getAppleTypes() { +const AppleAcceleratorTable &DWARFContext::getAppleTypes() { return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), DObj->getStringSection(), isLittleEndian()); } -const DWARFAcceleratorTable &DWARFContext::getAppleNamespaces() { +const AppleAcceleratorTable &DWARFContext::getAppleNamespaces() { return getAccelTable(AppleNamespaces, *DObj, DObj->getAppleNamespacesSection(), DObj->getStringSection(), isLittleEndian()); } -const DWARFAcceleratorTable &DWARFContext::getAppleObjC() { +const AppleAcceleratorTable &DWARFContext::getAppleObjC() { return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), DObj->getStringSection(), isLittleEndian()); } -const DWARFLineTable * +const DWARFDebugLine::LineTable * DWARFContext::getLineTableForUnit(DWARFUnit *U) { + Expected<const DWARFDebugLine::LineTable *> ExpectedLineTable = + getLineTableForUnit(U, DWARFDebugLine::warn); + if (!ExpectedLineTable) { + DWARFDebugLine::warn(ExpectedLineTable.takeError()); + return nullptr; + } + return *ExpectedLineTable; +} + +Expected<const DWARFDebugLine::LineTable *> DWARFContext::getLineTableForUnit( + DWARFUnit *U, std::function<void(Error)> RecoverableErrorCallback) { if (!Line) Line.reset(new DWARFDebugLine); @@ -762,7 +810,8 @@ DWARFContext::getLineTableForUnit(DWARFUnit *U) { // We have to parse it first. DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), U->getAddressByteSize()); - return Line->getOrParseLineTable(lineData, stmtOffset, U); + return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, + RecoverableErrorCallback); } void DWARFContext::parseCompileUnits() { @@ -1119,7 +1168,7 @@ static bool isRelocScattered(const object::ObjectFile &Obj, } ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { - errs() << "error: " + toString(std::move(E)) << '\n'; + WithColor::error() << toString(std::move(E)) << '\n'; return ErrorPolicy::Continue; } @@ -1145,17 +1194,20 @@ class DWARFObjInMemory final : public DWARFObject { DWARFSectionMap LocSection; DWARFSectionMap LineSection; DWARFSectionMap RangeSection; + DWARFSectionMap RnglistsSection; DWARFSectionMap StringOffsetSection; DWARFSectionMap InfoDWOSection; DWARFSectionMap LineDWOSection; DWARFSectionMap LocDWOSection; DWARFSectionMap StringOffsetDWOSection; DWARFSectionMap RangeDWOSection; + DWARFSectionMap RnglistsDWOSection; DWARFSectionMap AddrSection; DWARFSectionMap AppleNamesSection; DWARFSectionMap AppleTypesSection; DWARFSectionMap AppleNamespacesSection; DWARFSectionMap AppleObjCSection; + DWARFSectionMap DebugNamesSection; DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { return StringSwitch<DWARFSectionMap *>(Name) @@ -1164,9 +1216,12 @@ class DWARFObjInMemory final : public DWARFObject { .Case("debug_line", &LineSection) .Case("debug_str_offsets", &StringOffsetSection) .Case("debug_ranges", &RangeSection) + .Case("debug_rnglists", &RnglistsSection) .Case("debug_info.dwo", &InfoDWOSection) .Case("debug_loc.dwo", &LocDWOSection) .Case("debug_line.dwo", &LineDWOSection) + .Case("debug_names", &DebugNamesSection) + .Case("debug_rnglists.dwo", &RnglistsDWOSection) .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) .Case("debug_addr", &AddrSection) .Case("apple_names", &AppleNamesSection) @@ -1192,8 +1247,11 @@ class DWARFObjInMemory final : public DWARFObject { StringRef CUIndexSection; StringRef GdbIndexSection; StringRef TUIndexSection; + StringRef LineStringSection; - SmallVector<SmallString<32>, 4> UncompressedSections; + // A deque holding section data whose iterators are not invalidated when + // new decompressed sections are inserted at the end. + std::deque<SmallString<0>> UncompressedSections; StringRef *mapSectionToMember(StringRef Name) { if (DWARFSection *Sec = mapNameToDWARFSection(Name)) @@ -1214,6 +1272,7 @@ class DWARFObjInMemory final : public DWARFObject { .Case("debug_cu_index", &CUIndexSection) .Case("debug_tu_index", &TUIndexSection) .Case("gdb_index", &GdbIndexSection) + .Case("debug_line_str", &LineStringSection) // Any more debug info sections go here. .Default(nullptr); } @@ -1230,11 +1289,11 @@ class DWARFObjInMemory final : public DWARFObject { if (!Decompressor) return Decompressor.takeError(); - SmallString<32> Out; + SmallString<0> Out; if (auto Err = Decompressor->resizeAndDecompress(Out)) return Err; - UncompressedSections.emplace_back(std::move(Out)); + UncompressedSections.push_back(std::move(Out)); Data = UncompressedSections.back(); return Error::success(); @@ -1423,6 +1482,9 @@ public: const DWARFSection &getRangeDWOSection() const override { return RangeDWOSection; } + const DWARFSection &getRnglistsDWOSection() const override { + return RnglistsDWOSection; + } const DWARFSection &getAddrSection() const override { return AddrSection; } StringRef getCUIndexSection() const override { return CUIndexSection; } StringRef getGdbIndexSection() const override { return GdbIndexSection; } @@ -1432,6 +1494,7 @@ public: const DWARFSection &getStringOffsetSection() const override { return StringOffsetSection; } + StringRef getLineStringSection() const override { return LineStringSection; } // Sections for DWARF5 split dwarf proposal. const DWARFSection &getInfoDWOSection() const override { @@ -1451,6 +1514,9 @@ public: const DWARFSection &getLineSection() const override { return LineSection; } StringRef getStringSection() const override { return StringSection; } const DWARFSection &getRangeSection() const override { return RangeSection; } + const DWARFSection &getRnglistsSection() const override { + return RnglistsSection; + } StringRef getMacinfoSection() const override { return MacinfoSection; } StringRef getPubNamesSection() const override { return PubNamesSection; } StringRef getPubTypesSection() const override { return PubTypesSection; } @@ -1472,6 +1538,9 @@ public: const DWARFSection &getAppleObjCSection() const override { return AppleObjCSection; } + const DWARFSection &getDebugNamesSection() const override { + return DebugNamesSection; + } StringRef getFileName() const override { return FileName; } uint8_t getAddressSize() const override { return AddressSize; } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp index 861dd313fb09..03e317461396 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" using namespace llvm; @@ -25,3 +26,71 @@ uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint32_t *Off, *SecNdx = Rel->SectionIndex; return getUnsigned(Off, Size) + Rel->Value; } + +Optional<uint64_t> +DWARFDataExtractor::getEncodedPointer(uint32_t *Offset, uint8_t Encoding, + uint64_t PCRelOffset) const { + if (Encoding == dwarf::DW_EH_PE_omit) + return None; + + uint64_t Result = 0; + uint32_t OldOffset = *Offset; + // First get value + switch (Encoding & 0x0F) { + case dwarf::DW_EH_PE_absptr: + switch (getAddressSize()) { + case 2: + case 4: + case 8: + Result = getUnsigned(Offset, getAddressSize()); + break; + default: + return None; + } + break; + case dwarf::DW_EH_PE_uleb128: + Result = getULEB128(Offset); + break; + case dwarf::DW_EH_PE_sleb128: + Result = getSLEB128(Offset); + break; + case dwarf::DW_EH_PE_udata2: + Result = getUnsigned(Offset, 2); + break; + case dwarf::DW_EH_PE_udata4: + Result = getUnsigned(Offset, 4); + break; + case dwarf::DW_EH_PE_udata8: + Result = getUnsigned(Offset, 8); + break; + case dwarf::DW_EH_PE_sdata2: + Result = getSigned(Offset, 2); + break; + case dwarf::DW_EH_PE_sdata4: + Result = getSigned(Offset, 4); + break; + case dwarf::DW_EH_PE_sdata8: + Result = getSigned(Offset, 8); + break; + default: + return None; + } + // Then add relative offset, if required + switch (Encoding & 0x70) { + case dwarf::DW_EH_PE_absptr: + // do nothing + break; + case dwarf::DW_EH_PE_pcrel: + Result += PCRelOffset; + break; + case dwarf::DW_EH_PE_datarel: + case dwarf::DW_EH_PE_textrel: + case dwarf::DW_EH_PE_funcrel: + case dwarf::DW_EH_PE_aligned: + default: + *Offset = OldOffset; + return None; + } + + return Result; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp index ed5d726ae4e2..b9ef6905912a 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp @@ -17,6 +17,13 @@ using namespace llvm; +void DWARFDebugArangeSet::Descriptor::dump(raw_ostream &OS, + uint32_t AddressSize) const { + OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, Address) + << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, + getEndAddress()); +} + void DWARFDebugArangeSet::clear() { Offset = -1U; std::memset(&HeaderData, 0, sizeof(Header)); @@ -98,10 +105,8 @@ void DWARFDebugArangeSet::dump(raw_ostream &OS) const { << format("cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", HeaderData.CuOffset, HeaderData.AddrSize, HeaderData.SegSize); - const uint32_t hex_width = HeaderData.AddrSize * 2; for (const auto &Desc : ArangeDescriptors) { - OS << format("[0x%*.*" PRIx64 " -", hex_width, hex_width, Desc.Address) - << format(" 0x%*.*" PRIx64 ")\n", - hex_width, hex_width, Desc.getEndAddress()); + Desc.dump(OS, HeaderData.AddrSize); + OS << '\n'; } } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp index a3ecb15e3661..19bfcaed2021 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -80,7 +80,7 @@ void DWARFDebugAranges::appendRange(uint32_t CUOffset, uint64_t LowPC, void DWARFDebugAranges::construct() { std::multiset<uint32_t> ValidCUs; // Maintain the set of CUs describing // a current address range. - std::sort(Endpoints.begin(), Endpoints.end()); + llvm::sort(Endpoints.begin(), Endpoints.end()); uint64_t PrevAddress = -1ULL; for (const auto &E : Endpoints) { if (PrevAddress < E.Address && !ValidCUs.empty()) { diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 3312da67804b..73333395f4c1 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -8,10 +8,8 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" @@ -31,87 +29,13 @@ using namespace llvm; using namespace dwarf; -/// \brief Abstract frame entry defining the common interface concrete -/// entries implement. -class llvm::FrameEntry { -public: - enum FrameKind {FK_CIE, FK_FDE}; - - FrameEntry(FrameKind K, uint64_t Offset, uint64_t Length) - : Kind(K), Offset(Offset), Length(Length) {} - - virtual ~FrameEntry() = default; - - FrameKind getKind() const { return Kind; } - virtual uint64_t getOffset() const { return Offset; } - - /// Parse and store a sequence of CFI instructions from Data, - /// starting at *Offset and ending at EndOffset. If everything - /// goes well, *Offset should be equal to EndOffset when this method - /// returns. Otherwise, an error occurred. - virtual void parseInstructions(DataExtractor Data, uint32_t *Offset, - uint32_t EndOffset); - - /// Dump the entry header to the given output stream. - virtual void dumpHeader(raw_ostream &OS) const = 0; - - /// Dump the entry's instructions to the given output stream. - virtual void dumpInstructions(raw_ostream &OS) const; - - /// Dump the entire entry to the given output stream. - void dump(raw_ostream &OS) const { - dumpHeader(OS); - dumpInstructions(OS); - OS << "\n"; - } - -protected: - const FrameKind Kind; - - /// \brief Offset of this entry in the section. - uint64_t Offset; - - /// \brief Entry length as specified in DWARF. - uint64_t Length; - - /// An entry may contain CFI instructions. An instruction consists of an - /// opcode and an optional sequence of operands. - using Operands = std::vector<uint64_t>; - struct Instruction { - Instruction(uint8_t Opcode) - : Opcode(Opcode) - {} - - uint8_t Opcode; - Operands Ops; - }; - - std::vector<Instruction> Instructions; - - /// Convenience methods to add a new instruction with the given opcode and - /// operands to the Instructions vector. - void addInstruction(uint8_t Opcode) { - Instructions.push_back(Instruction(Opcode)); - } - - void addInstruction(uint8_t Opcode, uint64_t Operand1) { - Instructions.push_back(Instruction(Opcode)); - Instructions.back().Ops.push_back(Operand1); - } - - void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) { - Instructions.push_back(Instruction(Opcode)); - Instructions.back().Ops.push_back(Operand1); - Instructions.back().Ops.push_back(Operand2); - } -}; // See DWARF standard v3, section 7.23 const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0; const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; -void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, - uint32_t EndOffset) { +Error CFIProgram::parse(DataExtractor Data, uint32_t *Offset, + uint32_t EndOffset) { while (*Offset < EndOffset) { uint8_t Opcode = Data.getU8(Offset); // Some instructions have a primary opcode encoded in the top bits. @@ -122,67 +46,73 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, // bits of the opcode itself. uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; switch (Primary) { - default: llvm_unreachable("Impossible primary CFI opcode"); - case DW_CFA_advance_loc: - case DW_CFA_restore: - addInstruction(Primary, Op1); - break; - case DW_CFA_offset: - addInstruction(Primary, Op1, Data.getULEB128(Offset)); - break; + default: + return make_error<StringError>( + "Invalid primary CFI opcode", + std::make_error_code(std::errc::illegal_byte_sequence)); + case DW_CFA_advance_loc: + case DW_CFA_restore: + addInstruction(Primary, Op1); + break; + case DW_CFA_offset: + addInstruction(Primary, Op1, Data.getULEB128(Offset)); + break; } } else { // Extended opcode - its value is Opcode itself. switch (Opcode) { - default: llvm_unreachable("Invalid extended CFI opcode"); - case DW_CFA_nop: - case DW_CFA_remember_state: - case DW_CFA_restore_state: - case DW_CFA_GNU_window_save: - // No operands - addInstruction(Opcode); - break; - case DW_CFA_set_loc: - // Operands: Address - addInstruction(Opcode, Data.getAddress(Offset)); - break; - case DW_CFA_advance_loc1: - // Operands: 1-byte delta - addInstruction(Opcode, Data.getU8(Offset)); - break; - case DW_CFA_advance_loc2: - // Operands: 2-byte delta - addInstruction(Opcode, Data.getU16(Offset)); - break; - case DW_CFA_advance_loc4: - // Operands: 4-byte delta - addInstruction(Opcode, Data.getU32(Offset)); - break; - case DW_CFA_restore_extended: - case DW_CFA_undefined: - case DW_CFA_same_value: - case DW_CFA_def_cfa_register: - case DW_CFA_def_cfa_offset: - case DW_CFA_GNU_args_size: - // Operands: ULEB128 - addInstruction(Opcode, Data.getULEB128(Offset)); - break; - case DW_CFA_def_cfa_offset_sf: - // Operands: SLEB128 - addInstruction(Opcode, Data.getSLEB128(Offset)); - break; - case DW_CFA_offset_extended: - case DW_CFA_register: - case DW_CFA_def_cfa: - case DW_CFA_val_offset: { - // Operands: ULEB128, ULEB128 - // Note: We can not embed getULEB128 directly into function - // argument list. getULEB128 changes Offset and order of evaluation - // for arguments is unspecified. - auto op1 = Data.getULEB128(Offset); - auto op2 = Data.getULEB128(Offset); - addInstruction(Opcode, op1, op2); - break; + default: + return make_error<StringError>( + "Invalid extended CFI opcode", + std::make_error_code(std::errc::illegal_byte_sequence)); + case DW_CFA_nop: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_GNU_window_save: + // No operands + addInstruction(Opcode); + break; + case DW_CFA_set_loc: + // Operands: Address + addInstruction(Opcode, Data.getAddress(Offset)); + break; + case DW_CFA_advance_loc1: + // Operands: 1-byte delta + addInstruction(Opcode, Data.getU8(Offset)); + break; + case DW_CFA_advance_loc2: + // Operands: 2-byte delta + addInstruction(Opcode, Data.getU16(Offset)); + break; + case DW_CFA_advance_loc4: + // Operands: 4-byte delta + addInstruction(Opcode, Data.getU32(Offset)); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_GNU_args_size: + // Operands: ULEB128 + addInstruction(Opcode, Data.getULEB128(Offset)); + break; + case DW_CFA_def_cfa_offset_sf: + // Operands: SLEB128 + addInstruction(Opcode, Data.getSLEB128(Offset)); + break; + case DW_CFA_offset_extended: + case DW_CFA_register: + case DW_CFA_def_cfa: + case DW_CFA_val_offset: { + // Operands: ULEB128, ULEB128 + // Note: We can not embed getULEB128 directly into function + // argument list. getULEB128 changes Offset and order of evaluation + // for arguments is unspecified. + auto op1 = Data.getULEB128(Offset); + auto op2 = Data.getULEB128(Offset); + addInstruction(Opcode, op1, op2); + break; } case DW_CFA_offset_extended_sf: case DW_CFA_def_cfa_sf: @@ -194,162 +124,49 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, addInstruction(Opcode, op1, op2); break; } - case DW_CFA_def_cfa_expression: - // FIXME: Parse the actual instruction. - *Offset += Data.getULEB128(Offset); + case DW_CFA_def_cfa_expression: { + uint32_t ExprLength = Data.getULEB128(Offset); + addInstruction(Opcode, 0); + DataExtractor Extractor( + Data.getData().slice(*Offset, *Offset + ExprLength), + Data.isLittleEndian(), Data.getAddressSize()); + Instructions.back().Expression = DWARFExpression( + Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); + *Offset += ExprLength; break; + } case DW_CFA_expression: case DW_CFA_val_expression: { - // FIXME: Parse the actual instruction. - Data.getULEB128(Offset); - *Offset += Data.getULEB128(Offset); + auto RegNum = Data.getULEB128(Offset); + auto BlockLength = Data.getULEB128(Offset); + addInstruction(Opcode, RegNum, 0); + DataExtractor Extractor( + Data.getData().slice(*Offset, *Offset + BlockLength), + Data.isLittleEndian(), Data.getAddressSize()); + Instructions.back().Expression = DWARFExpression( + Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); + *Offset += BlockLength; break; } } } } + + return Error::success(); } namespace { -/// \brief DWARF Common Information Entry (CIE) -class CIE : public FrameEntry { -public: - // CIEs (and FDEs) are simply container classes, so the only sensible way to - // create them is by providing the full parsed contents in the constructor. - CIE(uint64_t Offset, uint64_t Length, uint8_t Version, - SmallString<8> Augmentation, uint8_t AddressSize, - uint8_t SegmentDescriptorSize, uint64_t CodeAlignmentFactor, - int64_t DataAlignmentFactor, uint64_t ReturnAddressRegister, - SmallString<8> AugmentationData, uint32_t FDEPointerEncoding, - uint32_t LSDAPointerEncoding) - : FrameEntry(FK_CIE, Offset, Length), Version(Version), - Augmentation(std::move(Augmentation)), AddressSize(AddressSize), - SegmentDescriptorSize(SegmentDescriptorSize), - CodeAlignmentFactor(CodeAlignmentFactor), - DataAlignmentFactor(DataAlignmentFactor), - ReturnAddressRegister(ReturnAddressRegister), - AugmentationData(std::move(AugmentationData)), - FDEPointerEncoding(FDEPointerEncoding), - LSDAPointerEncoding(LSDAPointerEncoding) {} - - ~CIE() override = default; - - StringRef getAugmentationString() const { return Augmentation; } - uint64_t getCodeAlignmentFactor() const { return CodeAlignmentFactor; } - int64_t getDataAlignmentFactor() const { return DataAlignmentFactor; } - - uint32_t getFDEPointerEncoding() const { - return FDEPointerEncoding; - } - - uint32_t getLSDAPointerEncoding() const { - return LSDAPointerEncoding; - } - - void dumpHeader(raw_ostream &OS) const override { - OS << format("%08x %08x %08x CIE", - (uint32_t)Offset, (uint32_t)Length, DW_CIE_ID) - << "\n"; - OS << format(" Version: %d\n", Version); - OS << " Augmentation: \"" << Augmentation << "\"\n"; - if (Version >= 4) { - OS << format(" Address size: %u\n", - (uint32_t)AddressSize); - OS << format(" Segment desc size: %u\n", - (uint32_t)SegmentDescriptorSize); - } - OS << format(" Code alignment factor: %u\n", - (uint32_t)CodeAlignmentFactor); - OS << format(" Data alignment factor: %d\n", - (int32_t)DataAlignmentFactor); - OS << format(" Return address column: %d\n", - (int32_t)ReturnAddressRegister); - if (!AugmentationData.empty()) { - OS << " Augmentation data: "; - for (uint8_t Byte : AugmentationData) - OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf); - OS << "\n"; - } - OS << "\n"; - } - - static bool classof(const FrameEntry *FE) { - return FE->getKind() == FK_CIE; - } - -private: - /// The following fields are defined in section 6.4.1 of the DWARF standard v4 - uint8_t Version; - SmallString<8> Augmentation; - uint8_t AddressSize; - uint8_t SegmentDescriptorSize; - uint64_t CodeAlignmentFactor; - int64_t DataAlignmentFactor; - uint64_t ReturnAddressRegister; - - // The following are used when the CIE represents an EH frame entry. - SmallString<8> AugmentationData; - uint32_t FDEPointerEncoding; - uint32_t LSDAPointerEncoding; -}; - -/// \brief DWARF Frame Description Entry (FDE) -class FDE : public FrameEntry { -public: - // Each FDE has a CIE it's "linked to". Our FDE contains is constructed with - // an offset to the CIE (provided by parsing the FDE header). The CIE itself - // is obtained lazily once it's actually required. - FDE(uint64_t Offset, uint64_t Length, int64_t LinkedCIEOffset, - uint64_t InitialLocation, uint64_t AddressRange, - CIE *Cie) - : FrameEntry(FK_FDE, Offset, Length), LinkedCIEOffset(LinkedCIEOffset), - InitialLocation(InitialLocation), AddressRange(AddressRange), - LinkedCIE(Cie) {} - - ~FDE() override = default; - - CIE *getLinkedCIE() const { return LinkedCIE; } - - void dumpHeader(raw_ostream &OS) const override { - OS << format("%08x %08x %08x FDE ", - (uint32_t)Offset, (uint32_t)Length, (int32_t)LinkedCIEOffset); - OS << format("cie=%08x pc=%08x...%08x\n", - (int32_t)LinkedCIEOffset, - (uint32_t)InitialLocation, - (uint32_t)InitialLocation + (uint32_t)AddressRange); - } - - static bool classof(const FrameEntry *FE) { - return FE->getKind() == FK_FDE; - } - -private: - /// The following fields are defined in section 6.4.1 of the DWARF standard v3 - uint64_t LinkedCIEOffset; - uint64_t InitialLocation; - uint64_t AddressRange; - CIE *LinkedCIE; -}; - -/// \brief Types of operands to CF instructions. -enum OperandType { - OT_Unset, - OT_None, - OT_Address, - OT_Offset, - OT_FactoredCodeOffset, - OT_SignedFactDataOffset, - OT_UnsignedFactDataOffset, - OT_Register, - OT_Expression -}; } // end anonymous namespace -/// \brief Initialize the array describing the types of operands. -static ArrayRef<OperandType[2]> getOperandTypes() { +ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() { static OperandType OpTypes[DW_CFA_restore+1][2]; + static bool Initialized = false; + if (Initialized) { + return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1); + } + Initialized = true; #define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \ do { \ @@ -396,15 +213,13 @@ static ArrayRef<OperandType[2]> getOperandTypes() { return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1); } -static ArrayRef<OperandType[2]> OpTypes = getOperandTypes(); - -/// \brief Print \p Opcode's operand number \p OperandIdx which has -/// value \p Operand. -static void printOperand(raw_ostream &OS, uint8_t Opcode, unsigned OperandIdx, - uint64_t Operand, uint64_t CodeAlignmentFactor, - int64_t DataAlignmentFactor) { +/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. +void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, + bool IsEH, const Instruction &Instr, + unsigned OperandIdx, uint64_t Operand) const { assert(OperandIdx < 2); - OperandType Type = OpTypes[Opcode][OperandIdx]; + uint8_t Opcode = Instr.Opcode; + OperandType Type = getOperandTypes()[Opcode][OperandIdx]; switch (Type) { case OT_Unset: { @@ -449,36 +264,68 @@ static void printOperand(raw_ostream &OS, uint8_t Opcode, unsigned OperandIdx, OS << format(" reg%" PRId64, Operand); break; case OT_Expression: - OS << " expression"; + assert(Instr.Expression && "missing DWARFExpression object"); + OS << " "; + Instr.Expression->print(OS, MRI, IsEH); break; } } -void FrameEntry::dumpInstructions(raw_ostream &OS) const { - uint64_t CodeAlignmentFactor = 0; - int64_t DataAlignmentFactor = 0; - const CIE *Cie = dyn_cast<CIE>(this); - - if (!Cie) - Cie = cast<FDE>(this)->getLinkedCIE(); - if (Cie) { - CodeAlignmentFactor = Cie->getCodeAlignmentFactor(); - DataAlignmentFactor = Cie->getDataAlignmentFactor(); - } - +void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, + unsigned IndentLevel) const { for (const auto &Instr : Instructions) { uint8_t Opcode = Instr.Opcode; if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK; - OS << " " << CallFrameString(Opcode) << ":"; + OS.indent(2 * IndentLevel); + OS << CallFrameString(Opcode) << ":"; for (unsigned i = 0; i < Instr.Ops.size(); ++i) - printOperand(OS, Opcode, i, Instr.Ops[i], CodeAlignmentFactor, - DataAlignmentFactor); + printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]); OS << '\n'; } } -DWARFDebugFrame::DWARFDebugFrame(bool IsEH) : IsEH(IsEH) {} +void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { + OS << format("%08x %08x %08x CIE", (uint32_t)Offset, (uint32_t)Length, + DW_CIE_ID) + << "\n"; + OS << format(" Version: %d\n", Version); + OS << " Augmentation: \"" << Augmentation << "\"\n"; + if (Version >= 4) { + OS << format(" Address size: %u\n", (uint32_t)AddressSize); + OS << format(" Segment desc size: %u\n", + (uint32_t)SegmentDescriptorSize); + } + OS << format(" Code alignment factor: %u\n", (uint32_t)CodeAlignmentFactor); + OS << format(" Data alignment factor: %d\n", (int32_t)DataAlignmentFactor); + OS << format(" Return address column: %d\n", (int32_t)ReturnAddressRegister); + if (Personality) + OS << format(" Personality Address: %08x\n", *Personality); + if (!AugmentationData.empty()) { + OS << " Augmentation data: "; + for (uint8_t Byte : AugmentationData) + OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf); + OS << "\n"; + } + OS << "\n"; + CFIs.dump(OS, MRI, IsEH); + OS << "\n"; +} + +void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { + OS << format("%08x %08x %08x FDE ", (uint32_t)Offset, (uint32_t)Length, + (int32_t)LinkedCIEOffset); + OS << format("cie=%08x pc=%08x...%08x\n", (int32_t)LinkedCIEOffset, + (uint32_t)InitialLocation, + (uint32_t)InitialLocation + (uint32_t)AddressRange); + if (LSDAAddress) + OS << format(" LSDA Address: %08x\n", *LSDAAddress); + CFIs.dump(OS, MRI, IsEH); + OS << "\n"; +} + +DWARFDebugFrame::DWARFDebugFrame(bool IsEH, uint64_t EHFrameAddress) + : IsEH(IsEH), EHFrameAddress(EHFrameAddress) {} DWARFDebugFrame::~DWARFDebugFrame() = default; @@ -492,40 +339,6 @@ static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, errs() << "\n"; } -static unsigned getSizeForEncoding(const DataExtractor &Data, - unsigned symbolEncoding) { - unsigned format = symbolEncoding & 0x0f; - switch (format) { - default: llvm_unreachable("Unknown Encoding"); - case DW_EH_PE_absptr: - case DW_EH_PE_signed: - return Data.getAddressSize(); - case DW_EH_PE_udata2: - case DW_EH_PE_sdata2: - return 2; - case DW_EH_PE_udata4: - case DW_EH_PE_sdata4: - return 4; - case DW_EH_PE_udata8: - case DW_EH_PE_sdata8: - return 8; - } -} - -static uint64_t readPointer(const DataExtractor &Data, uint32_t &Offset, - unsigned Encoding) { - switch (getSizeForEncoding(Data, Encoding)) { - case 2: - return Data.getU16(&Offset); - case 4: - return Data.getU32(&Offset); - case 8: - return Data.getU64(&Offset); - default: - llvm_unreachable("Illegal data size"); - } -} - // This is a workaround for old compilers which do not allow // noreturn attribute usage in lambdas. Once the support for those // compilers are phased out, we can remove this and return back to @@ -539,7 +352,7 @@ static void LLVM_ATTRIBUTE_NORETURN ReportError(uint32_t StartOffset, report_fatal_error(Str); } -void DWARFDebugFrame::parse(DataExtractor Data) { +void DWARFDebugFrame::parse(DWARFDataExtractor Data) { uint32_t Offset = 0; DenseMap<uint32_t, CIE *> CIEs; @@ -569,9 +382,8 @@ void DWARFDebugFrame::parse(DataExtractor Data) { // The Id field's size depends on the DWARF format Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4); - bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) || - Id == DW_CIE_ID || - (IsEH && !Id)); + bool IsCIE = + ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id)); if (IsCIE) { uint8_t Version = Data.getU8(&Offset); @@ -587,12 +399,11 @@ void DWARFDebugFrame::parse(DataExtractor Data) { // Parse the augmentation data for EH CIEs StringRef AugmentationData(""); - uint32_t FDEPointerEncoding = DW_EH_PE_omit; + uint32_t FDEPointerEncoding = DW_EH_PE_absptr; uint32_t LSDAPointerEncoding = DW_EH_PE_omit; + Optional<uint64_t> Personality; + Optional<uint32_t> PersonalityEncoding; if (IsEH) { - Optional<uint32_t> PersonalityEncoding; - Optional<uint64_t> Personality; - Optional<uint64_t> AugmentationLength; uint32_t StartAugmentationOffset; uint32_t EndAugmentationOffset; @@ -611,12 +422,17 @@ void DWARFDebugFrame::parse(DataExtractor Data) { ReportError(StartOffset, "Duplicate personality in entry at %lx"); PersonalityEncoding = Data.getU8(&Offset); - Personality = readPointer(Data, Offset, *PersonalityEncoding); + Personality = Data.getEncodedPointer( + &Offset, *PersonalityEncoding, + EHFrameAddress ? EHFrameAddress + Offset : 0); break; } case 'R': FDEPointerEncoding = Data.getU8(&Offset); break; + case 'S': + // Current frame is a signal trampoline. + break; case 'z': if (i) ReportError(StartOffset, @@ -639,14 +455,11 @@ void DWARFDebugFrame::parse(DataExtractor Data) { } } - auto Cie = llvm::make_unique<CIE>(StartOffset, Length, Version, - AugmentationString, AddressSize, - SegmentDescriptorSize, - CodeAlignmentFactor, - DataAlignmentFactor, - ReturnAddressRegister, - AugmentationData, FDEPointerEncoding, - LSDAPointerEncoding); + auto Cie = llvm::make_unique<CIE>( + StartOffset, Length, Version, AugmentationString, AddressSize, + SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor, + ReturnAddressRegister, AugmentationData, FDEPointerEncoding, + LSDAPointerEncoding, Personality, PersonalityEncoding); CIEs[StartOffset] = Cie.get(); Entries.emplace_back(std::move(Cie)); } else { @@ -654,6 +467,7 @@ void DWARFDebugFrame::parse(DataExtractor Data) { uint64_t CIEPointer = Id; uint64_t InitialLocation = 0; uint64_t AddressRange = 0; + Optional<uint64_t> LSDAAddress; CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer]; if (IsEH) { @@ -662,10 +476,15 @@ void DWARFDebugFrame::parse(DataExtractor Data) { ReportError(StartOffset, "Parsing FDE data at %lx failed due to missing CIE"); - InitialLocation = readPointer(Data, Offset, - Cie->getFDEPointerEncoding()); - AddressRange = readPointer(Data, Offset, - Cie->getFDEPointerEncoding()); + if (auto Val = Data.getEncodedPointer( + &Offset, Cie->getFDEPointerEncoding(), + EHFrameAddress ? EHFrameAddress + Offset : 0)) { + InitialLocation = *Val; + } + if (auto Val = Data.getEncodedPointer( + &Offset, Cie->getFDEPointerEncoding(), 0)) { + AddressRange = *Val; + } StringRef AugmentationString = Cie->getAugmentationString(); if (!AugmentationString.empty()) { @@ -676,8 +495,11 @@ void DWARFDebugFrame::parse(DataExtractor Data) { Offset + static_cast<uint32_t>(AugmentationLength); // Decode the LSDA if the CIE augmentation string said we should. - if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) - readPointer(Data, Offset, Cie->getLSDAPointerEncoding()); + if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) { + LSDAAddress = Data.getEncodedPointer( + &Offset, Cie->getLSDAPointerEncoding(), + EHFrameAddress ? Offset + EHFrameAddress : 0); + } if (Offset != EndAugmentationOffset) ReportError(StartOffset, "Parsing augmentation data at %lx failed"); @@ -689,10 +511,13 @@ void DWARFDebugFrame::parse(DataExtractor Data) { Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, InitialLocation, AddressRange, - Cie)); + Cie, LSDAAddress)); } - Entries.back()->parseInstructions(Data, &Offset, EndStructureOffset); + if (Error E = + Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) { + report_fatal_error(toString(std::move(E))); + } if (Offset != EndStructureOffset) ReportError(StartOffset, "Parsing entry instructions at %lx failed"); @@ -709,14 +534,15 @@ FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { return nullptr; } -void DWARFDebugFrame::dump(raw_ostream &OS, Optional<uint64_t> Offset) const { +void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, + Optional<uint64_t> Offset) const { if (Offset) { if (auto *Entry = getEntryAtOffset(*Offset)) - Entry->dump(OS); + Entry->dump(OS, MRI, IsEH); return; } OS << "\n"; for (const auto &Entry : Entries) - Entry->dump(OS); + Entry->dump(OS, MRI, IsEH); } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index e5ef4eaceebe..53a8e193ef56 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -16,6 +17,7 @@ #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> @@ -40,6 +42,28 @@ using ContentDescriptors = SmallVector<ContentDescriptor, 4>; } // end anonmyous namespace +void DWARFDebugLine::ContentTypeTracker::trackContentType( + dwarf::LineNumberEntryFormat ContentType) { + switch (ContentType) { + case dwarf::DW_LNCT_timestamp: + HasModTime = true; + break; + case dwarf::DW_LNCT_size: + HasLength = true; + break; + case dwarf::DW_LNCT_MD5: + HasMD5 = true; + break; + case dwarf::DW_LNCT_LLVM_source: + HasSource = true; + break; + default: + // We only care about values we consider optional, and new values may be + // added in the vendor extension range, so we do not match exhaustively. + break; + } +} + DWARFDebugLine::Prologue::Prologue() { clear(); } void DWARFDebugLine::Prologue::clear() { @@ -47,14 +71,15 @@ void DWARFDebugLine::Prologue::clear() { SegSelectorSize = 0; MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0; OpcodeBase = 0; - FormParams = DWARFFormParams({0, 0, DWARF32}); - HasMD5 = false; + FormParams = dwarf::FormParams({0, 0, DWARF32}); + ContentTypes = ContentTypeTracker(); StandardOpcodeLengths.clear(); IncludeDirectories.clear(); FileNames.clear(); } -void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { +void DWARFDebugLine::Prologue::dump(raw_ostream &OS, + DIDumpOptions DumpOptions) const { OS << "Line table prologue:\n" << format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength) << format(" version: %u\n", getVersion()); @@ -73,29 +98,37 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { OS << format("standard_opcode_lengths[%s] = %u\n", LNStandardString(I + 1).data(), StandardOpcodeLengths[I]); - if (!IncludeDirectories.empty()) - for (uint32_t I = 0; I != IncludeDirectories.size(); ++I) - OS << format("include_directories[%3u] = '", I + 1) - << IncludeDirectories[I] << "'\n"; + if (!IncludeDirectories.empty()) { + // DWARF v5 starts directory indexes at 0. + uint32_t DirBase = getVersion() >= 5 ? 0 : 1; + for (uint32_t I = 0; I != IncludeDirectories.size(); ++I) { + OS << format("include_directories[%3u] = ", I + DirBase); + IncludeDirectories[I].dump(OS, DumpOptions); + OS << '\n'; + } + } if (!FileNames.empty()) { - if (HasMD5) - OS << " Dir MD5 Checksum File Name\n" - << " ---- -------------------------------- -----------" - "---------------\n"; - else - OS << " Dir Mod Time File Len File Name\n" - << " ---- ---------- ---------- -----------" - "----------------\n"; + // DWARF v5 starts file indexes at 0. + uint32_t FileBase = getVersion() >= 5 ? 0 : 1; for (uint32_t I = 0; I != FileNames.size(); ++I) { const FileNameEntry &FileEntry = FileNames[I]; - OS << format("file_names[%3u] %4" PRIu64 " ", I + 1, FileEntry.DirIdx); - if (HasMD5) - OS << FileEntry.Checksum.digest(); - else - OS << format("0x%8.8" PRIx64 " 0x%8.8" PRIx64, FileEntry.ModTime, - FileEntry.Length); - OS << ' ' << FileEntry.Name << '\n'; + OS << format("file_names[%3u]:\n", I + FileBase); + OS << " name: "; + FileEntry.Name.dump(OS, DumpOptions); + OS << '\n' + << format(" dir_index: %" PRIu64 "\n", FileEntry.DirIdx); + if (ContentTypes.HasMD5) + OS << " md5_checksum: " << FileEntry.Checksum.digest() << '\n'; + if (ContentTypes.HasModTime) + OS << format(" mod_time: 0x%8.8" PRIx64 "\n", FileEntry.ModTime); + if (ContentTypes.HasLength) + OS << format(" length: 0x%8.8" PRIx64 "\n", FileEntry.Length); + if (ContentTypes.HasSource) { + OS << " source: "; + FileEntry.Source.dump(OS, DumpOptions); + OS << '\n'; + } } } } @@ -104,13 +137,16 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { static void parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, uint64_t EndPrologueOffset, - std::vector<StringRef> &IncludeDirectories, + DWARFDebugLine::ContentTypeTracker &ContentTypes, + std::vector<DWARFFormValue> &IncludeDirectories, std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { while (*OffsetPtr < EndPrologueOffset) { StringRef S = DebugLineData.getCStrRef(OffsetPtr); if (S.empty()) break; - IncludeDirectories.push_back(S); + DWARFFormValue Dir(dwarf::DW_FORM_string); + Dir.setPValue(S.data()); + IncludeDirectories.push_back(Dir); } while (*OffsetPtr < EndPrologueOffset) { @@ -118,20 +154,25 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, if (Name.empty()) break; DWARFDebugLine::FileNameEntry FileEntry; - FileEntry.Name = Name; + FileEntry.Name.setForm(dwarf::DW_FORM_string); + FileEntry.Name.setPValue(Name.data()); FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); FileNames.push_back(FileEntry); } + + ContentTypes.HasModTime = true; + ContentTypes.HasLength = true; } // Parse v5 directory/file entry content descriptions. // Returns the descriptors, or an empty vector if we did not find a path or // ran off the end of the prologue. static ContentDescriptors -parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, - uint64_t EndPrologueOffset, bool *HasMD5) { +parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t + *OffsetPtr, uint64_t EndPrologueOffset, DWARFDebugLine::ContentTypeTracker + *ContentTypes) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; @@ -144,8 +185,8 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); if (Descriptor.Type == dwarf::DW_LNCT_path) HasPath = true; - else if (Descriptor.Type == dwarf::DW_LNCT_MD5 && HasMD5) - *HasMD5 = true; + if (ContentTypes) + ContentTypes->trackContentType(Descriptor.Type); Descriptors.push_back(Descriptor); } return HasPath ? Descriptors : ContentDescriptors(); @@ -154,8 +195,10 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, static bool parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, uint64_t EndPrologueOffset, - const DWARFFormParams &FormParams, const DWARFUnit *U, - bool &HasMD5, std::vector<StringRef> &IncludeDirectories, + const dwarf::FormParams &FormParams, + const DWARFContext &Ctx, const DWARFUnit *U, + DWARFDebugLine::ContentTypeTracker &ContentTypes, + std::vector<DWARFFormValue> &IncludeDirectories, std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { // Get the directory entry description. ContentDescriptors DirDescriptors = @@ -172,9 +215,9 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { case DW_LNCT_path: - if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, U)) + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return false; - IncludeDirectories.push_back(Value.getAsCString().getValue()); + IncludeDirectories.push_back(Value); break; default: if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) @@ -185,7 +228,8 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, // Get the file entry description. ContentDescriptors FileDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, &HasMD5); + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, + &ContentTypes); if (FileDescriptors.empty()) return false; @@ -197,11 +241,14 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : FileDescriptors) { DWARFFormValue Value(Descriptor.Form); - if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, U)) + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return false; switch (Descriptor.Type) { case DW_LNCT_path: - FileEntry.Name = Value.getAsCString().getValue(); + FileEntry.Name = Value; + break; + case DW_LNCT_LLVM_source: + FileEntry.Source = Value; break; case DW_LNCT_directory_index: FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue(); @@ -226,8 +273,28 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, return true; } -bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, - uint32_t *OffsetPtr, const DWARFUnit *U) { +template <typename... Ts> +static std::string formatErrorString(char const *Fmt, const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return Stream.str(); +} + +template <typename... Ts> +static Error createError(char const *Fmt, const Ts &... Vals) { + return make_error<StringError>(formatErrorString(Fmt, Vals...), + inconvertibleErrorCode()); +} + +static Error createError(char const *Msg) { + return make_error<StringError>(Msg, inconvertibleErrorCode()); +} + +Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, + uint32_t *OffsetPtr, + const DWARFContext &Ctx, + const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; clear(); @@ -236,11 +303,16 @@ bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, FormParams.Format = dwarf::DWARF64; TotalLength = DebugLineData.getU64(OffsetPtr); } else if (TotalLength >= 0xffffff00) { - return false; + return createError( + "parsing line table prologue at offset 0x%8.8" PRIx64 + " unsupported reserved unit length found of value 0x%8.8" PRIx64, + PrologueOffset, TotalLength); } FormParams.Version = DebugLineData.getU16(OffsetPtr); if (getVersion() < 2) - return false; + return createError("parsing line table prologue at offset 0x%8.8" PRIx64 + " found unsupported version 0x%2.2" PRIx16, + PrologueOffset, getVersion()); if (getVersion() >= 5) { FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); @@ -268,27 +340,24 @@ bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, if (getVersion() >= 5) { if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - getFormParams(), U, HasMD5, IncludeDirectories, - FileNames)) { - fprintf(stderr, - "warning: parsing line table prologue at 0x%8.8" PRIx64 - " found an invalid directory or file table description at" - " 0x%8.8" PRIx64 "\n", PrologueOffset, (uint64_t)*OffsetPtr); - return false; + FormParams, Ctx, U, ContentTypes, + IncludeDirectories, FileNames)) { + return createError( + "parsing line table prologue at 0x%8.8" PRIx64 + " found an invalid directory or file table description at" + " 0x%8.8" PRIx64, + PrologueOffset, (uint64_t)*OffsetPtr); } } else parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - IncludeDirectories, FileNames); - - if (*OffsetPtr != EndPrologueOffset) { - fprintf(stderr, - "warning: parsing line table prologue at 0x%8.8" PRIx64 - " should have ended at 0x%8.8" PRIx64 - " but it ended at 0x%8.8" PRIx64 "\n", - PrologueOffset, EndPrologueOffset, (uint64_t)*OffsetPtr); - return false; - } - return true; + ContentTypes, IncludeDirectories, FileNames); + + if (*OffsetPtr != EndPrologueOffset) + return createError("parsing line table prologue at 0x%8.8" PRIx64 + " should have ended at 0x%8.8" PRIx64 + " but it ended at 0x%8.8" PRIx64, + PrologueOffset, EndPrologueOffset, (uint64_t)*OffsetPtr); + return Error::success(); } DWARFDebugLine::Row::Row(bool DefaultIsStmt) { reset(DefaultIsStmt); } @@ -340,8 +409,9 @@ void DWARFDebugLine::Sequence::reset() { DWARFDebugLine::LineTable::LineTable() { clear(); } -void DWARFDebugLine::LineTable::dump(raw_ostream &OS) const { - Prologue.dump(OS); +void DWARFDebugLine::LineTable::dump(raw_ostream &OS, + DIDumpOptions DumpOptions) const { + Prologue.dump(OS, DumpOptions); OS << '\n'; if (!Rows.empty()) { @@ -396,34 +466,45 @@ DWARFDebugLine::getLineTable(uint32_t Offset) const { return nullptr; } -const DWARFDebugLine::LineTable * -DWARFDebugLine::getOrParseLineTable(DWARFDataExtractor &DebugLineData, - uint32_t Offset, const DWARFUnit *U) { +Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( + DWARFDataExtractor &DebugLineData, uint32_t Offset, const DWARFContext &Ctx, + const DWARFUnit *U, std::function<void(Error)> RecoverableErrorCallback) { + if (!DebugLineData.isValidOffset(Offset)) + return createError("offset 0x%8.8" PRIx32 + " is not a valid debug line section offset", + Offset); + std::pair<LineTableIter, bool> Pos = LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable())); LineTable *LT = &Pos.first->second; if (Pos.second) { - if (!LT->parse(DebugLineData, &Offset, U)) - return nullptr; + if (Error Err = + LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorCallback)) + return std::move(Err); + return LT; } return LT; } -bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, - uint32_t *OffsetPtr, const DWARFUnit *U, - raw_ostream *OS) { +Error DWARFDebugLine::LineTable::parse( + DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, + const DWARFContext &Ctx, const DWARFUnit *U, + std::function<void(Error)> RecoverableErrorCallback, raw_ostream *OS) { const uint32_t DebugLineOffset = *OffsetPtr; clear(); - if (!Prologue.parse(DebugLineData, OffsetPtr, U)) { - // Restore our offset and return false to indicate failure! - *OffsetPtr = DebugLineOffset; - return false; + Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U); + + if (OS) { + // The presence of OS signals verbose dumping. + DIDumpOptions DumpOptions; + DumpOptions.Verbose = true; + Prologue.dump(*OS, DumpOptions); } - if (OS) - Prologue.dump(*OS); + if (PrologueErr) + return PrologueErr; const uint32_t EndOffset = DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength(); @@ -493,8 +574,12 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, // from the size of the operand. if (DebugLineData.getAddressSize() == 0) DebugLineData.setAddressSize(Len - 1); - else - assert(DebugLineData.getAddressSize() == Len - 1); + else if (DebugLineData.getAddressSize() != Len - 1) { + return createError("mismatching address size at offset 0x%8.8" PRIx32 + " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, + ExtOffset, DebugLineData.getAddressSize(), + Len - 1); + } State.Row.Address = DebugLineData.getRelocatedAddress(OffsetPtr); if (OS) *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address); @@ -523,14 +608,15 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, // the file register of the state machine. { FileNameEntry FileEntry; - FileEntry.Name = DebugLineData.getCStr(OffsetPtr); + const char *Name = DebugLineData.getCStr(OffsetPtr); + FileEntry.Name.setForm(dwarf::DW_FORM_string); + FileEntry.Name.setPValue(Name); FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); Prologue.FileNames.push_back(FileEntry); if (OS) - *OS << " (" << FileEntry.Name.str() - << ", dir=" << FileEntry.DirIdx << ", mod_time=" + *OS << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" << format("(0x%16.16" PRIx64 ")", FileEntry.ModTime) << ", length=" << FileEntry.Length << ")"; } @@ -553,14 +639,10 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, } // Make sure the stated and parsed lengths are the same. // Otherwise we have an unparseable line-number program. - if (*OffsetPtr - ExtOffset != Len) { - fprintf(stderr, "Unexpected line op length at offset 0x%8.8" PRIx32 - " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx32 "\n", - ExtOffset, Len, *OffsetPtr - ExtOffset); - // Skip the rest of the line-number program. - *OffsetPtr = EndOffset; - return false; - } + if (*OffsetPtr - ExtOffset != Len) + return createError("unexpected line op length at offset 0x%8.8" PRIx32 + " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx32, + ExtOffset, Len, *OffsetPtr - ExtOffset); } else if (Opcode < Prologue.OpcodeBase) { if (OS) *OS << LNStandardString(Opcode); @@ -763,14 +845,13 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, *OS << "\n"; } - if (!State.Sequence.Empty) { - fprintf(stderr, "warning: last sequence in debug line table is not" - "terminated!\n"); - } + if (!State.Sequence.Empty) + RecoverableErrorCallback( + createError("last sequence in debug line table is not terminated!")); // Sort all sequences so that address lookup will work faster. if (!Sequences.empty()) { - std::sort(Sequences.begin(), Sequences.end(), Sequence::orderByLowPC); + llvm::sort(Sequences.begin(), Sequences.end(), Sequence::orderByLowPC); // Note: actually, instruction address ranges of sequences should not // overlap (in shared objects and executables). If they do, the address // lookup would still work, though, but result would be ambiguous. @@ -779,7 +860,7 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, // rudimentary sequences for address ranges [0x0, 0xsomething). } - return EndOffset; + return Error::success(); } uint32_t @@ -887,6 +968,24 @@ bool DWARFDebugLine::LineTable::hasFileAtIndex(uint64_t FileIndex) const { return FileIndex != 0 && FileIndex <= Prologue.FileNames.size(); } +Optional<StringRef> DWARFDebugLine::LineTable::getSourceByIndex(uint64_t FileIndex, + FileLineInfoKind Kind) const { + if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) + return None; + const FileNameEntry &Entry = Prologue.FileNames[FileIndex - 1]; + if (Optional<const char *> source = Entry.Source.getAsCString()) + return StringRef(*source); + return None; +} + +static bool isPathAbsoluteOnWindowsOrPosix(const Twine &Path) { + // Debug info can contain paths from any OS, not necessarily + // an OS we're currently running on. Moreover different compilation units can + // be compiled on different operating systems and linked together later. + return sys::path::is_absolute(Path, sys::path::Style::posix) || + sys::path::is_absolute(Path, sys::path::Style::windows); +} + bool DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, const char *CompDir, FileLineInfoKind Kind, @@ -894,9 +993,9 @@ bool DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) return false; const FileNameEntry &Entry = Prologue.FileNames[FileIndex - 1]; - StringRef FileName = Entry.Name; + StringRef FileName = Entry.Name.getAsCString().getValue(); if (Kind != FileLineInfoKind::AbsoluteFilePath || - sys::path::is_absolute(FileName)) { + isPathAbsoluteOnWindowsOrPosix(FileName)) { Result = FileName; return true; } @@ -907,13 +1006,15 @@ bool DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, // Be defensive about the contents of Entry. if (IncludeDirIndex > 0 && IncludeDirIndex <= Prologue.IncludeDirectories.size()) - IncludeDir = Prologue.IncludeDirectories[IncludeDirIndex - 1]; + IncludeDir = Prologue.IncludeDirectories[IncludeDirIndex - 1] + .getAsCString() + .getValue(); // We may still need to append compilation directory of compile unit. // We know that FileName is not absolute, the only way to have an // absolute path at this point would be if IncludeDir is absolute. if (CompDir && Kind == FileLineInfoKind::AbsoluteFilePath && - sys::path::is_relative(IncludeDir)) + !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) sys::path::append(FilePath, CompDir); // sys::path::append skips empty strings. @@ -936,5 +1037,97 @@ bool DWARFDebugLine::LineTable::getFileLineInfoForAddress( Result.Line = Row.Line; Result.Column = Row.Column; Result.Discriminator = Row.Discriminator; + Result.Source = getSourceByIndex(Row.File, Kind); return true; } + +// We want to supply the Unit associated with a .debug_line[.dwo] table when +// we dump it, if possible, but still dump the table even if there isn't a Unit. +// Therefore, collect up handles on all the Units that point into the +// line-table section. +static DWARFDebugLine::SectionParser::LineToUnitMap +buildLineToUnitMap(DWARFDebugLine::SectionParser::cu_range CUs, + DWARFDebugLine::SectionParser::tu_range TUSections) { + DWARFDebugLine::SectionParser::LineToUnitMap LineToUnit; + for (const auto &CU : CUs) + if (auto CUDIE = CU->getUnitDIE()) + if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) + LineToUnit.insert(std::make_pair(*StmtOffset, &*CU)); + for (const auto &TUS : TUSections) + for (const auto &TU : TUS) + if (auto TUDIE = TU->getUnitDIE()) + if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list))) + LineToUnit.insert(std::make_pair(*StmtOffset, &*TU)); + return LineToUnit; +} + +DWARFDebugLine::SectionParser::SectionParser(DWARFDataExtractor &Data, + const DWARFContext &C, + cu_range CUs, tu_range TUs) + : DebugLineData(Data), Context(C) { + LineToUnit = buildLineToUnitMap(CUs, TUs); + if (!DebugLineData.isValidOffset(Offset)) + Done = true; +} + +bool DWARFDebugLine::Prologue::totalLengthIsValid() const { + return TotalLength == 0xffffffff || TotalLength < 0xffffff00; +} + +DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext( + function_ref<void(Error)> RecoverableErrorCallback, + function_ref<void(Error)> UnrecoverableErrorCallback, raw_ostream *OS) { + assert(DebugLineData.isValidOffset(Offset) && + "parsing should have terminated"); + DWARFUnit *U = prepareToParse(Offset); + uint32_t OldOffset = Offset; + LineTable LT; + if (Error Err = LT.parse(DebugLineData, &Offset, Context, U, + RecoverableErrorCallback, OS)) + UnrecoverableErrorCallback(std::move(Err)); + moveToNextTable(OldOffset, LT.Prologue); + return LT; +} + +void DWARFDebugLine::SectionParser::skip( + function_ref<void(Error)> ErrorCallback) { + assert(DebugLineData.isValidOffset(Offset) && + "parsing should have terminated"); + DWARFUnit *U = prepareToParse(Offset); + uint32_t OldOffset = Offset; + LineTable LT; + if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U)) + ErrorCallback(std::move(Err)); + moveToNextTable(OldOffset, LT.Prologue); +} + +DWARFUnit *DWARFDebugLine::SectionParser::prepareToParse(uint32_t Offset) { + DWARFUnit *U = nullptr; + auto It = LineToUnit.find(Offset); + if (It != LineToUnit.end()) + U = It->second; + DebugLineData.setAddressSize(U ? U->getAddressByteSize() : 0); + return U; +} + +void DWARFDebugLine::SectionParser::moveToNextTable(uint32_t OldOffset, + const Prologue &P) { + // If the length field is not valid, we don't know where the next table is, so + // cannot continue to parse. Mark the parser as done, and leave the Offset + // value as it currently is. This will be the end of the bad length field. + if (!P.totalLengthIsValid()) { + Done = true; + return; + } + + Offset = OldOffset + P.TotalLength + P.sizeofTotalLength(); + if (!DebugLineData.isValidOffset(Offset)) { + Done = true; + } +} + +void DWARFDebugLine::warn(Error Err) { + handleAllErrors(std::move(Err), [](ErrorInfoBase &Info) { + WithColor::warning() << Info.message() << '\n'; + }); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp index 58f88536f317..617b914ecce9 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cinttypes> @@ -33,18 +34,22 @@ static void dumpExpression(raw_ostream &OS, ArrayRef<char> Data, const MCRegisterInfo *MRI) { DWARFDataExtractor Extractor(StringRef(Data.data(), Data.size()), IsLittleEndian, AddressSize); - DWARFExpression(Extractor, AddressSize, dwarf::DWARF_VERSION).print(OS, MRI); + DWARFExpression(Extractor, dwarf::DWARF_VERSION, AddressSize).print(OS, MRI); } void DWARFDebugLoc::LocationList::dump(raw_ostream &OS, bool IsLittleEndian, unsigned AddressSize, const MCRegisterInfo *MRI, + uint64_t BaseAddress, unsigned Indent) const { for (const Entry &E : Entries) { OS << '\n'; OS.indent(Indent); - OS << format("0x%016" PRIx64, E.Begin) << " - " - << format("0x%016" PRIx64, E.End) << ": "; + OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, + BaseAddress + E.Begin); + OS << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, + BaseAddress + E.End); + OS << ": "; dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI); } @@ -64,7 +69,7 @@ void DWARFDebugLoc::dump(raw_ostream &OS, const MCRegisterInfo *MRI, Optional<uint64_t> Offset) const { auto DumpLocationList = [&](const LocationList &L) { OS << format("0x%8.8x: ", L.Offset); - L.dump(OS, IsLittleEndian, AddressSize, MRI, 12); + L.dump(OS, IsLittleEndian, AddressSize, MRI, 0, 12); OS << "\n\n"; }; @@ -89,7 +94,7 @@ DWARFDebugLoc::parseOneLocationList(DWARFDataExtractor Data, unsigned *Offset) { while (true) { Entry E; if (!Data.isValidOffsetForDataOfSize(*Offset, 2 * Data.getAddressSize())) { - llvm::errs() << "Location list overflows the debug_loc section.\n"; + WithColor::error() << "location list overflows the debug_loc section.\n"; return None; } @@ -106,13 +111,13 @@ DWARFDebugLoc::parseOneLocationList(DWARFDataExtractor Data, unsigned *Offset) { return LL; if (!Data.isValidOffsetForDataOfSize(*Offset, 2)) { - llvm::errs() << "Location list overflows the debug_loc section.\n"; + WithColor::error() << "location list overflows the debug_loc section.\n"; return None; } unsigned Bytes = Data.getU16(Offset); if (!Data.isValidOffsetForDataOfSize(*Offset, Bytes)) { - llvm::errs() << "Location list overflows the debug_loc section.\n"; + WithColor::error() << "location list overflows the debug_loc section.\n"; return None; } // A single location description describing the location of the object... @@ -136,7 +141,7 @@ void DWARFDebugLoc::parse(const DWARFDataExtractor &data) { break; } if (data.isValidOffset(Offset)) - errs() << "error: failed to consume entire .debug_loc section\n"; + WithColor::error() << "failed to consume entire .debug_loc section\n"; } Optional<DWARFDebugLocDWO::LocationList> @@ -148,8 +153,8 @@ DWARFDebugLocDWO::parseOneLocationList(DataExtractor Data, unsigned *Offset) { while (auto Kind = static_cast<dwarf::LocationListEntry>(Data.getU8(Offset))) { if (Kind != dwarf::DW_LLE_startx_length) { - llvm::errs() << "error: dumping support for LLE of kind " << (int)Kind - << " not implemented\n"; + WithColor::error() << "dumping support for LLE of kind " << (int)Kind + << " not implemented\n"; return None; } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp index 1b77be6192dd..6d789c3027a5 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -8,14 +8,13 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" -#include "SyntaxHighlighting.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cstdint> using namespace llvm; using namespace dwarf; -using namespace syntax; void DWARFDebugMacro::dump(raw_ostream &OS) const { unsigned IndLevel = 0; @@ -29,7 +28,7 @@ void DWARFDebugMacro::dump(raw_ostream &OS) const { OS << " "; IndLevel += (E.Type == DW_MACINFO_start_file); - WithColor(OS, syntax::Macro).get() << MacinfoString(E.Type); + WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); switch (E.Type) { default: // Got a corrupted ".debug_macinfo" section (invalid macinfo type). diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp index 956a91e9c4d6..de8b6e543fab 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -37,7 +37,7 @@ DWARFDebugPubTable::DWARFDebugPubTable(StringRef Data, bool LittleEndian, if (DieRef == 0) break; uint8_t IndexEntryValue = GnuStyle ? PubNames.getU8(&Offset) : 0; - const char *Name = PubNames.getCStr(&Offset); + StringRef Name = PubNames.getCStrRef(&Offset); SetData.Entries.push_back( {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp index f0b7ec2751de..a565718debd0 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp @@ -13,13 +13,16 @@ #include "llvm/Support/raw_ostream.h" #include <cinttypes> #include <cstdint> -#include <utility> using namespace llvm; -raw_ostream &llvm::operator<<(raw_ostream &OS, const DWARFAddressRange &R) { - return OS << format("[0x%16.16" PRIx64 ", 0x%16.16" PRIx64 ")", R.LowPC, - R.HighPC); +// FIXME: There are several versions of this. Consolidate them. +template <typename... Ts> +static Error createError(char const *Fmt, const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error<StringError>(Stream.str(), inconvertibleErrorCode()); } void DWARFDebugRangeList::clear() { @@ -28,14 +31,15 @@ void DWARFDebugRangeList::clear() { Entries.clear(); } -bool DWARFDebugRangeList::extract(const DWARFDataExtractor &data, - uint32_t *offset_ptr) { +Error DWARFDebugRangeList::extract(const DWARFDataExtractor &data, + uint32_t *offset_ptr) { clear(); if (!data.isValidOffset(*offset_ptr)) - return false; + return createError("invalid range list offset 0x%" PRIx32, *offset_ptr); + AddressSize = data.getAddressSize(); if (AddressSize != 4 && AddressSize != 8) - return false; + return createError("invalid address size: %d", AddressSize); Offset = *offset_ptr; while (true) { RangeListEntry Entry; @@ -49,13 +53,14 @@ bool DWARFDebugRangeList::extract(const DWARFDataExtractor &data, // Check that both values were extracted correctly. if (*offset_ptr != prev_offset + 2 * AddressSize) { clear(); - return false; + return createError("invalid range list entry at offset 0x%" PRIx32, + prev_offset); } if (Entry.isEndOfListEntry()) break; Entries.push_back(Entry); } - return true; + return Error::success(); } void DWARFDebugRangeList::dump(raw_ostream &OS) const { diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp new file mode 100644 index 000000000000..b19c808a8fb3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp @@ -0,0 +1,205 @@ +//===- DWARFDebugRnglists.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +template <typename... Ts> +static Error createError(char const *Fmt, const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error<StringError>(Stream.str(), inconvertibleErrorCode()); +} + +namespace llvm { // workaround for gcc bug +template <> +Error DWARFListType<RangeListEntry>::createError(const char *Fmt, const char *s, + uint32_t Val) { + return ::createError(Fmt, s, Val); +} +} + +Error RangeListEntry::extract(DWARFDataExtractor Data, uint32_t End, + uint32_t *OffsetPtr) { + Offset = *OffsetPtr; + SectionIndex = -1ULL; + // The caller should guarantee that we have at least 1 byte available, so + // we just assert instead of revalidate. + assert(*OffsetPtr < End && + "not enough space to extract a rangelist encoding"); + uint8_t Encoding = Data.getU8(OffsetPtr); + + switch (Encoding) { + case dwarf::DW_RLE_end_of_list: + Value0 = Value1 = 0; + break; + // TODO: Support other encodings. + case dwarf::DW_RLE_base_addressx: + return createError("unsupported rnglists encoding DW_RLE_base_addressx " + "at offset 0x%" PRIx32, + *OffsetPtr - 1); + case dwarf::DW_RLE_startx_endx: + return createError("unsupported rnglists encoding DW_RLE_startx_endx at " + "offset 0x%" PRIx32, + *OffsetPtr - 1); + case dwarf::DW_RLE_startx_length: + return createError("unsupported rnglists encoding DW_RLE_startx_length " + "at offset 0x%" PRIx32, + *OffsetPtr - 1); + case dwarf::DW_RLE_offset_pair: { + uint32_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createError("read past end of table when reading " + "DW_RLE_offset_pair encoding at offset 0x%" PRIx32, + PreviousOffset); + break; + } + case dwarf::DW_RLE_base_address: { + if ((End - *OffsetPtr) < Data.getAddressSize()) + return createError("insufficient space remaining in table for " + "DW_RLE_base_address encoding at offset 0x%" PRIx32, + *OffsetPtr - 1); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + break; + } + case dwarf::DW_RLE_start_end: { + if ((End - *OffsetPtr) < unsigned(Data.getAddressSize() * 2)) + return createError("insufficient space remaining in table for " + "DW_RLE_start_end encoding " + "at offset 0x%" PRIx32, + *OffsetPtr - 1); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + Value1 = Data.getRelocatedAddress(OffsetPtr); + break; + } + case dwarf::DW_RLE_start_length: { + uint32_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createError("read past end of table when reading " + "DW_RLE_start_length encoding at offset 0x%" PRIx32, + PreviousOffset); + break; + } + default: + return createError("unknown rnglists encoding 0x%" PRIx32 + " at offset 0x%" PRIx32, + uint32_t(Encoding), *OffsetPtr - 1); + } + + EntryKind = Encoding; + return Error::success(); +} + +DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( + llvm::Optional<BaseAddress> BaseAddr) const { + DWARFAddressRangesVector Res; + for (const RangeListEntry &RLE : Entries) { + if (RLE.EntryKind == dwarf::DW_RLE_end_of_list) + break; + if (RLE.EntryKind == dwarf::DW_RLE_base_address) { + BaseAddr = {RLE.Value0, RLE.SectionIndex}; + continue; + } + + DWARFAddressRange E; + E.SectionIndex = RLE.SectionIndex; + if (BaseAddr && E.SectionIndex == -1ULL) + E.SectionIndex = BaseAddr->SectionIndex; + + switch (RLE.EntryKind) { + case dwarf::DW_RLE_offset_pair: + E.LowPC = RLE.Value0; + E.HighPC = RLE.Value1; + if (BaseAddr) { + E.LowPC += BaseAddr->Address; + E.HighPC += BaseAddr->Address; + } + break; + case dwarf::DW_RLE_start_end: + E.LowPC = RLE.Value0; + E.HighPC = RLE.Value1; + break; + case dwarf::DW_RLE_start_length: + E.LowPC = RLE.Value0; + E.HighPC = E.LowPC + RLE.Value1; + break; + default: + // Unsupported encodings should have been reported during extraction, + // so we should not run into any here. + llvm_unreachable("Unsupported range list encoding"); + } + Res.push_back(E); + } + return Res; +} + +void RangeListEntry::dump(raw_ostream &OS, uint8_t AddrSize, + uint8_t MaxEncodingStringLength, + uint64_t &CurrentBase, DIDumpOptions DumpOpts) const { + auto PrintRawEntry = [](raw_ostream &OS, const RangeListEntry &Entry, + uint8_t AddrSize, DIDumpOptions DumpOpts) { + if (DumpOpts.Verbose) { + DumpOpts.DisplayRawContents = true; + DWARFAddressRange(Entry.Value0, Entry.Value1) + .dump(OS, AddrSize, DumpOpts); + OS << " => "; + } + }; + + if (DumpOpts.Verbose) { + // Print the section offset in verbose mode. + OS << format("0x%8.8" PRIx32 ":", Offset); + auto EncodingString = dwarf::RangeListEncodingString(EntryKind); + // Unsupported encodings should have been reported during parsing. + assert(!EncodingString.empty() && "Unknown range entry encoding"); + OS << format(" [%s%*c", EncodingString.data(), + MaxEncodingStringLength - EncodingString.size() + 1, ']'); + if (EntryKind != dwarf::DW_RLE_end_of_list) + OS << ": "; + } + + switch (EntryKind) { + case dwarf::DW_RLE_end_of_list: + OS << (DumpOpts.Verbose ? "" : "<End of list>"); + break; + case dwarf::DW_RLE_base_address: + // In non-verbose mode we do not print anything for this entry. + CurrentBase = Value0; + if (!DumpOpts.Verbose) + return; + OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Value0); + break; + case dwarf::DW_RLE_start_length: + PrintRawEntry(OS, *this, AddrSize, DumpOpts); + DWARFAddressRange(Value0, Value0 + Value1).dump(OS, AddrSize, DumpOpts); + break; + case dwarf::DW_RLE_offset_pair: + PrintRawEntry(OS, *this, AddrSize, DumpOpts); + DWARFAddressRange(Value0 + CurrentBase, Value1 + CurrentBase) + .dump(OS, AddrSize, DumpOpts); + break; + case dwarf::DW_RLE_start_end: + DWARFAddressRange(Value0, Value1).dump(OS, AddrSize, DumpOpts); + break; + default: + llvm_unreachable("Unsupported range list encoding"); + } + OS << "\n"; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index 91f0f8501f0c..904ceab7b286 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -8,9 +8,9 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDie.h" -#include "SyntaxHighlighting.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" @@ -22,7 +22,9 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> @@ -34,7 +36,6 @@ using namespace llvm; using namespace dwarf; using namespace object; -using namespace syntax; static void dumpApplePropertyAttribute(raw_ostream &OS, uint64_t Val) { OS << " ("; @@ -62,13 +63,11 @@ static void dumpRanges(const DWARFObject &Obj, raw_ostream &OS, if (DumpOpts.Verbose) SectionNames = Obj.getSectionNames(); - for (size_t I = 0; I < Ranges.size(); ++I) { - const DWARFAddressRange &R = Ranges[I]; + for (const DWARFAddressRange &R : Ranges) { OS << '\n'; OS.indent(Indent); - OS << format("[0x%0*" PRIx64 " - 0x%0*" PRIx64 ")", AddressSize * 2, - R.LowPC, AddressSize * 2, R.HighPC); + R.dump(OS, AddressSize); if (SectionNames.empty() || R.SectionIndex == -1ULL) continue; @@ -103,15 +102,18 @@ static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, const DWARFSection &LocSection = Obj.getLocSection(); const DWARFSection &LocDWOSection = Obj.getLocDWOSection(); uint32_t Offset = *FormValue.getAsSectionOffset(); - if (!LocSection.Data.empty()) { DWARFDebugLoc DebugLoc; DWARFDataExtractor Data(Obj, LocSection, Ctx.isLittleEndian(), Obj.getAddressSize()); auto LL = DebugLoc.parseOneLocationList(Data, &Offset); - if (LL) - LL->dump(OS, Ctx.isLittleEndian(), Obj.getAddressSize(), MRI, Indent); - else + if (LL) { + uint64_t BaseAddr = 0; + if (Optional<BaseAddress> BA = U->getBaseAddress()) + BaseAddr = BA->Address; + LL->dump(OS, Ctx.isLittleEndian(), Obj.getAddressSize(), MRI, BaseAddr, + Indent); + } else OS << "error extracting location list."; } else if (!LocDWOSection.Data.empty()) { DataExtractor Data(LocDWOSection.Data, Ctx.isLittleEndian(), 0); @@ -191,19 +193,10 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, const char BaseIndent[] = " "; OS << BaseIndent; OS.indent(Indent + 2); - auto attrString = AttributeString(Attr); - if (!attrString.empty()) - WithColor(OS, syntax::Attribute) << attrString; - else - WithColor(OS, syntax::Attribute).get() << format("DW_AT_Unknown_%x", Attr); + WithColor(OS, HighlightColor::Attribute) << formatv("{0}", Attr); - if (DumpOpts.Verbose || DumpOpts.ShowForm) { - auto formString = FormEncodingString(Form); - if (!formString.empty()) - OS << " [" << formString << ']'; - else - OS << format(" [DW_FORM_Unknown_%x]", Form); - } + if (DumpOpts.Verbose || DumpOpts.ShowForm) + OS << formatv(" [{0}]", Form); DWARFUnit *U = Die.getDwarfUnit(); DWARFFormValue formValue(Form); @@ -216,9 +209,9 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, StringRef Name; std::string File; - auto Color = syntax::Enumerator; + auto Color = HighlightColor::Enumerator; if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { - Color = syntax::String; + Color = HighlightColor::String; if (const auto *LT = U->getContext().getLineTableForUnit(U)) if (LT->getFileNameByIndex( formValue.getAsUnsignedConstant().getValue(), @@ -267,8 +260,22 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, dumpApplePropertyAttribute(OS, *OptVal); } else if (Attr == DW_AT_ranges) { const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); - dumpRanges(Obj, OS, Die.getAddressRanges(), U->getAddressByteSize(), - sizeof(BaseIndent) + Indent + 4, DumpOpts); + // For DW_FORM_rnglistx we need to dump the offset separately, since + // we have only dumped the index so far. + Optional<DWARFFormValue> Value = Die.find(DW_AT_ranges); + if (Value && Value->getForm() == DW_FORM_rnglistx) + if (auto RangeListOffset = + U->getRnglistOffset(*Value->getAsSectionOffset())) { + DWARFFormValue FV(dwarf::DW_FORM_sec_offset); + FV.setUValue(*RangeListOffset); + FV.dump(OS, DumpOpts); + } + if (auto RangesOrError = Die.getAddressRanges()) + dumpRanges(Obj, OS, RangesOrError.get(), U->getAddressByteSize(), + sizeof(BaseIndent) + Indent + 4, DumpOpts); + else + WithColor::error() << "decoding address ranges: " + << toString(RangesOrError.takeError()) << '\n'; } OS << ")\n"; @@ -306,18 +313,37 @@ DWARFDie::find(ArrayRef<dwarf::Attribute> Attrs) const { Optional<DWARFFormValue> DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const { - if (!isValid()) - return None; - if (auto Value = find(Attrs)) - return Value; - if (auto Die = getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) { - if (auto Value = Die.findRecursively(Attrs)) - return Value; - } - if (auto Die = getAttributeValueAsReferencedDie(DW_AT_specification)) { - if (auto Value = Die.findRecursively(Attrs)) + std::vector<DWARFDie> Worklist; + Worklist.push_back(*this); + + // Keep track if DIEs already seen to prevent infinite recursion. + // Empirically we rarely see a depth of more than 3 when dealing with valid + // DWARF. This corresponds to following the DW_AT_abstract_origin and + // DW_AT_specification just once. + SmallSet<DWARFDie, 3> Seen; + + while (!Worklist.empty()) { + DWARFDie Die = Worklist.back(); + Worklist.pop_back(); + + if (!Die.isValid()) + continue; + + if (Seen.count(Die)) + continue; + + Seen.insert(Die); + + if (auto Value = Die.find(Attrs)) return Value; + + if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) + Worklist.push_back(D); + + if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_specification)) + Worklist.push_back(D); } + return None; } @@ -363,20 +389,19 @@ bool DWARFDie::getLowAndHighPC(uint64_t &LowPC, uint64_t &HighPC, return false; } -DWARFAddressRangesVector DWARFDie::getAddressRanges() const { +Expected<DWARFAddressRangesVector> DWARFDie::getAddressRanges() const { if (isNULL()) return DWARFAddressRangesVector(); // Single range specified by low/high PC. uint64_t LowPC, HighPC, Index; if (getLowAndHighPC(LowPC, HighPC, Index)) - return {{LowPC, HighPC, Index}}; - - // Multiple ranges from .debug_ranges section. - auto RangesOffset = toSectionOffset(find(DW_AT_ranges)); - if (RangesOffset) { - DWARFDebugRangeList RangeList; - if (U->extractRangeList(*RangesOffset, RangeList)) - return RangeList.getAbsoluteRanges(U->getBaseAddress()); + return DWARFAddressRangesVector{{LowPC, HighPC, Index}}; + + Optional<DWARFFormValue> Value = find(DW_AT_ranges); + if (Value) { + if (Value->getForm() == DW_FORM_rnglistx) + return U->findRnglistFromIndex(*Value->getAsSectionOffset()); + return U->findRnglistFromOffset(*Value->getAsSectionOffset()); } return DWARFAddressRangesVector(); } @@ -386,8 +411,11 @@ void DWARFDie::collectChildrenAddressRanges( if (isNULL()) return; if (isSubprogramDIE()) { - const auto &DIERanges = getAddressRanges(); - Ranges.insert(Ranges.end(), DIERanges.begin(), DIERanges.end()); + if (auto DIERangesOrError = getAddressRanges()) + Ranges.insert(Ranges.end(), DIERangesOrError.get().begin(), + DIERangesOrError.get().end()); + else + llvm::consumeError(DIERangesOrError.takeError()); } for (auto Child : children()) @@ -395,10 +423,15 @@ void DWARFDie::collectChildrenAddressRanges( } bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { - for (const auto &R : getAddressRanges()) { + auto RangesOrError = getAddressRanges(); + if (!RangesOrError) { + llvm::consumeError(RangesOrError.takeError()); + return false; + } + + for (const auto &R : RangesOrError.get()) if (R.LowPC <= Address && Address < R.HighPC) return true; - } return false; } @@ -454,25 +487,23 @@ void DWARFDie::dump(raw_ostream &OS, unsigned Indent, const uint32_t Offset = getOffset(); uint32_t offset = Offset; if (DumpOpts.ShowParents) { - DumpOpts.ShowParents = false; - Indent = dumpParentChain(getParent(), OS, Indent, DumpOpts); + DIDumpOptions ParentDumpOpts = DumpOpts; + ParentDumpOpts.ShowParents = false; + ParentDumpOpts.ShowChildren = false; + Indent = dumpParentChain(getParent(), OS, Indent, ParentDumpOpts); } if (debug_info_data.isValidOffset(offset)) { uint32_t abbrCode = debug_info_data.getULEB128(&offset); if (DumpOpts.ShowAddresses) - WithColor(OS, syntax::Address).get() << format("\n0x%8.8x: ", Offset); + WithColor(OS, HighlightColor::Address).get() + << format("\n0x%8.8x: ", Offset); if (abbrCode) { auto AbbrevDecl = getAbbreviationDeclarationPtr(); if (AbbrevDecl) { - auto tagString = TagString(getTag()); - if (!tagString.empty()) - WithColor(OS, syntax::Tag).get().indent(Indent) << tagString; - else - WithColor(OS, syntax::Tag).get().indent(Indent) - << format("DW_TAG_Unknown_%x", getTag()); - + WithColor(OS, HighlightColor::Tag).get().indent(Indent) + << formatv("{0}", getTag()); if (DumpOpts.Verbose) OS << format(" [%u] %c", abbrCode, AbbrevDecl->hasChildren() ? '*' : ' '); @@ -493,8 +524,10 @@ void DWARFDie::dump(raw_ostream &OS, unsigned Indent, DWARFDie child = getFirstChild(); if (DumpOpts.ShowChildren && DumpOpts.RecurseDepth > 0 && child) { DumpOpts.RecurseDepth--; + DIDumpOptions ChildDumpOpts = DumpOpts; + ChildDumpOpts.ShowParents = false; while (child) { - child.dump(OS, Indent + 2, DumpOpts); + child.dump(OS, Indent + 2, ChildDumpOpts); child = child.getSibling(); } } @@ -522,12 +555,24 @@ DWARFDie DWARFDie::getSibling() const { return DWARFDie(); } +DWARFDie DWARFDie::getPreviousSibling() const { + if (isValid()) + return U->getPreviousSibling(Die); + return DWARFDie(); +} + DWARFDie DWARFDie::getFirstChild() const { if (isValid()) return U->getFirstChild(Die); return DWARFDie(); } +DWARFDie DWARFDie::getLastChild() const { + if (isValid()) + return U->getLastChild(Die); + return DWARFDie(); +} + iterator_range<DWARFDie::attribute_iterator> DWARFDie::attributes() const { return make_range(attribute_iterator(*this, false), attribute_iterator(*this, true)); diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index c704c2901aef..a9ea26c476ca 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -258,9 +258,10 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, return true; } -void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo) { +void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo, + bool IsEH) const { for (auto &Op : *this) { - if (!Op.print(OS, this, RegInfo, /* isEH */ false)) { + if (!Op.print(OS, this, RegInfo, IsEH)) { uint32_t FailOffset = Op.getEndOffset(); while (FailOffset < Data.getData().size()) OS << format(" %02x", Data.getU8(&FailOffset)); diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp index 44886de2e3d5..1aa43c6b6517 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "SyntaxHighlighting.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" @@ -19,6 +18,7 @@ #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cinttypes> #include <cstdint> @@ -26,9 +26,8 @@ using namespace llvm; using namespace dwarf; -using namespace syntax; -static const DWARFFormValue::FormClass DWARF4FormClasses[] = { +static const DWARFFormValue::FormClass DWARF5FormClasses[] = { DWARFFormValue::FC_Unknown, // 0x0 DWARFFormValue::FC_Address, // 0x01 DW_FORM_addr DWARFFormValue::FC_Unknown, // 0x02 unused @@ -57,96 +56,31 @@ static const DWARFFormValue::FormClass DWARF4FormClasses[] = { DWARFFormValue::FC_SectionOffset, // 0x17 DW_FORM_sec_offset DWARFFormValue::FC_Exprloc, // 0x18 DW_FORM_exprloc DWARFFormValue::FC_Flag, // 0x19 DW_FORM_flag_present -}; - -Optional<uint8_t> -DWARFFormValue::getFixedByteSize(dwarf::Form Form, - const DWARFFormParams Params) { - switch (Form) { - case DW_FORM_addr: - assert(Params.Version && Params.AddrSize && "Invalid Params for form"); - return Params.AddrSize; - - case DW_FORM_block: // ULEB128 length L followed by L bytes. - case DW_FORM_block1: // 1 byte length L followed by L bytes. - case DW_FORM_block2: // 2 byte length L followed by L bytes. - case DW_FORM_block4: // 4 byte length L followed by L bytes. - case DW_FORM_string: // C-string with null terminator. - case DW_FORM_sdata: // SLEB128. - case DW_FORM_udata: // ULEB128. - case DW_FORM_ref_udata: // ULEB128. - case DW_FORM_indirect: // ULEB128. - case DW_FORM_exprloc: // ULEB128 length L followed by L bytes. - case DW_FORM_strx: // ULEB128. - case DW_FORM_addrx: // ULEB128. - case DW_FORM_loclistx: // ULEB128. - case DW_FORM_rnglistx: // ULEB128. - case DW_FORM_GNU_addr_index: // ULEB128. - case DW_FORM_GNU_str_index: // ULEB128. - return None; - - case DW_FORM_ref_addr: - assert(Params.Version && Params.AddrSize && "Invalid Params for form"); - return Params.getRefAddrByteSize(); - - case DW_FORM_flag: - case DW_FORM_data1: - case DW_FORM_ref1: - case DW_FORM_strx1: - case DW_FORM_addrx1: - return 1; - - case DW_FORM_data2: - case DW_FORM_ref2: - case DW_FORM_strx2: - case DW_FORM_addrx2: - return 2; - - case DW_FORM_strx3: - return 3; - - case DW_FORM_data4: - case DW_FORM_ref4: - case DW_FORM_ref_sup4: - case DW_FORM_strx4: - case DW_FORM_addrx4: - return 4; - - case DW_FORM_strp: - case DW_FORM_GNU_ref_alt: - case DW_FORM_GNU_strp_alt: - case DW_FORM_line_strp: - case DW_FORM_sec_offset: - case DW_FORM_strp_sup: - assert(Params.Version && Params.AddrSize && "Invalid Params for form"); - return Params.getDwarfOffsetByteSize(); - - case DW_FORM_data8: - case DW_FORM_ref8: - case DW_FORM_ref_sig8: - case DW_FORM_ref_sup8: - return 8; - - case DW_FORM_flag_present: - return 0; + DWARFFormValue::FC_String, // 0x1a DW_FORM_strx + DWARFFormValue::FC_Address, // 0x1b DW_FORM_addrx + DWARFFormValue::FC_Reference, // 0x1c DW_FORM_ref_sup4 + DWARFFormValue::FC_String, // 0x1d DW_FORM_strp_sup + DWARFFormValue::FC_Constant, // 0x1e DW_FORM_data16 + DWARFFormValue::FC_String, // 0x1f DW_FORM_line_strp + DWARFFormValue::FC_Reference, // 0x20 DW_FORM_ref_sig8 + DWARFFormValue::FC_Constant, // 0x21 DW_FORM_implicit_const + DWARFFormValue::FC_SectionOffset, // 0x22 DW_FORM_loclistx + DWARFFormValue::FC_SectionOffset, // 0x23 DW_FORM_rnglistx + DWARFFormValue::FC_Reference, // 0x24 DW_FORM_ref_sup8 + DWARFFormValue::FC_String, // 0x25 DW_FORM_strx1 + DWARFFormValue::FC_String, // 0x26 DW_FORM_strx2 + DWARFFormValue::FC_String, // 0x27 DW_FORM_strx3 + DWARFFormValue::FC_String, // 0x28 DW_FORM_strx4 + DWARFFormValue::FC_Address, // 0x29 DW_FORM_addrx1 + DWARFFormValue::FC_Address, // 0x2a DW_FORM_addrx2 + DWARFFormValue::FC_Address, // 0x2b DW_FORM_addrx3 + DWARFFormValue::FC_Address, // 0x2c DW_FORM_addrx4 - case DW_FORM_data16: - return 16; - - case DW_FORM_implicit_const: - // The implicit value is stored in the abbreviation as a SLEB128, and - // there no data in debug info. - return 0; - - default: - llvm_unreachable("Handle this form in this switch statement"); - } - return None; -} +}; bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, uint32_t *OffsetPtr, - const DWARFFormParams Params) { + const dwarf::FormParams Params) { bool Indirect = false; do { switch (Form) { @@ -208,7 +142,7 @@ bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, case DW_FORM_GNU_ref_alt: case DW_FORM_GNU_strp_alt: if (Optional<uint8_t> FixedSize = - DWARFFormValue::getFixedByteSize(Form, Params)) { + dwarf::getFixedFormByteSize(Form, Params)) { *OffsetPtr += *FixedSize; return true; } @@ -243,42 +177,38 @@ bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, } bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { - // First, check DWARF4 form classes. - if (Form < makeArrayRef(DWARF4FormClasses).size() && - DWARF4FormClasses[Form] == FC) + // First, check DWARF5 form classes. + if (Form < makeArrayRef(DWARF5FormClasses).size() && + DWARF5FormClasses[Form] == FC) return true; - // Check more forms from DWARF4 and DWARF5 proposals. + // Check more forms from extensions and proposals. switch (Form) { - case DW_FORM_ref_sig8: case DW_FORM_GNU_ref_alt: return (FC == FC_Reference); case DW_FORM_GNU_addr_index: return (FC == FC_Address); case DW_FORM_GNU_str_index: case DW_FORM_GNU_strp_alt: - case DW_FORM_strx: - case DW_FORM_strx1: - case DW_FORM_strx2: - case DW_FORM_strx3: - case DW_FORM_strx4: return (FC == FC_String); - case DW_FORM_implicit_const: - return (FC == FC_Constant); default: break; } // 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. Also accept DW_FORM_strp since this is .debug_str section - // offset. + // by mistake. Also accept DW_FORM_[line_]strp since these are + // .debug_[line_]str section offsets. return (Form == DW_FORM_data4 || Form == DW_FORM_data8 || - Form == DW_FORM_strp) && + Form == DW_FORM_strp || Form == DW_FORM_line_strp) && FC == FC_SectionOffset; } bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, - uint32_t *OffsetPtr, DWARFFormParams FP, + uint32_t *OffsetPtr, dwarf::FormParams FP, + const DWARFContext *Ctx, const DWARFUnit *CU) { + if (!Ctx && CU) + Ctx = &CU->getContext(); + C = Ctx; U = CU; bool Indirect = false; bool IsBlock = false; @@ -350,6 +280,7 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, break; case DW_FORM_udata: case DW_FORM_ref_udata: + case DW_FORM_rnglistx: Value.uval = Data.getULEB128(OffsetPtr); break; case DW_FORM_string: @@ -402,8 +333,9 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { uint64_t UValue = Value.uval; bool CURelativeOffset = false; - raw_ostream &AddrOS = - DumpOpts.ShowAddresses ? WithColor(OS, syntax::Address).get() : nulls(); + raw_ostream &AddrOS = DumpOpts.ShowAddresses + ? WithColor(OS, HighlightColor::Address).get() + : nulls(); switch (Form) { case DW_FORM_addr: AddrOS << format("0x%016" PRIx64, UValue); @@ -494,6 +426,11 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); dumpString(OS); break; + case DW_FORM_line_strp: + if (DumpOpts.Verbose) + OS << format(" .debug_line_str[0x%8.8x] = ", (uint32_t)UValue); + dumpString(OS); + break; case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2: @@ -514,23 +451,28 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { break; case DW_FORM_ref1: CURelativeOffset = true; - AddrOS << format("cu + 0x%2.2x", (uint8_t)UValue); + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%2.2x", (uint8_t)UValue); break; case DW_FORM_ref2: CURelativeOffset = true; - AddrOS << format("cu + 0x%4.4x", (uint16_t)UValue); + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%4.4x", (uint16_t)UValue); break; case DW_FORM_ref4: CURelativeOffset = true; - AddrOS << format("cu + 0x%4.4x", (uint32_t)UValue); + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%4.4x", (uint32_t)UValue); break; case DW_FORM_ref8: CURelativeOffset = true; - AddrOS << format("cu + 0x%8.8" PRIx64, UValue); + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%8.8" PRIx64, UValue); break; case DW_FORM_ref_udata: CURelativeOffset = true; - AddrOS << format("cu + 0x%" PRIx64, UValue); + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%" PRIx64, UValue); break; case DW_FORM_GNU_ref_alt: AddrOS << format("<alt 0x%" PRIx64 ">", UValue); @@ -542,6 +484,10 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << "DW_FORM_indirect"; break; + case DW_FORM_rnglistx: + OS << format("indexed (0x%x) rangelist = ", (uint32_t)UValue); + break; + // Should be formatted to 64-bit for DWARF64. case DW_FORM_sec_offset: AddrOS << format("0x%08x", (uint32_t)UValue); @@ -552,21 +498,23 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { break; } - if (CURelativeOffset && DumpOpts.Verbose) { - OS << " => {"; - WithColor(OS, syntax::Address).get() + if (CURelativeOffset) { + if (DumpOpts.Verbose) + OS << " => {"; + WithColor(OS, HighlightColor::Address).get() << format("0x%8.8" PRIx64, UValue + (U ? U->getOffset() : 0)); - OS << "}"; + if (DumpOpts.Verbose) + OS << "}"; } } void DWARFFormValue::dumpString(raw_ostream &OS) const { Optional<const char *> DbgStr = getAsCString(); if (DbgStr.hasValue()) { - raw_ostream &COS = WithColor(OS, syntax::String); - COS << '"'; - COS.write_escaped(DbgStr.getValue()); - COS << '"'; + auto COS = WithColor(OS, HighlightColor::String); + COS.get() << '"'; + COS.get().write_escaped(DbgStr.getValue()); + COS.get() << '"'; } } @@ -576,20 +524,32 @@ Optional<const char *> DWARFFormValue::getAsCString() const { if (Form == DW_FORM_string) return Value.cstr; // FIXME: Add support for DW_FORM_GNU_strp_alt - if (Form == DW_FORM_GNU_strp_alt || U == nullptr) + if (Form == DW_FORM_GNU_strp_alt || C == nullptr) return None; uint32_t Offset = Value.uval; + if (Form == DW_FORM_line_strp) { + // .debug_line_str is tracked in the Context. + if (const char *Str = C->getLineStringExtractor().getCStr(&Offset)) + return Str; + return None; + } if (Form == DW_FORM_GNU_str_index || Form == DW_FORM_strx || Form == DW_FORM_strx1 || Form == DW_FORM_strx2 || Form == DW_FORM_strx3 || Form == DW_FORM_strx4) { uint64_t StrOffset; - if (!U->getStringOffsetSectionItem(Offset, StrOffset)) + if (!U || !U->getStringOffsetSectionItem(Offset, StrOffset)) return None; Offset = StrOffset; } - if (const char *Str = U->getStringExtractor().getCStr(&Offset)) { - return Str; + // Prefer the Unit's string extractor, because for .dwo it will point to + // .debug_str.dwo, while the Context's extractor always uses .debug_str. + if (U) { + if (const char *Str = U->getStringExtractor().getCStr(&Offset)) + return Str; + return None; } + if (const char *Str = C->getStringExtractor().getCStr(&Offset)) + return Str; return None; } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp new file mode 100644 index 000000000000..559afc7559bd --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp @@ -0,0 +1,109 @@ +//===- DWARFListTable.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFListTable.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +template <typename... Ts> +static Error createError(char const *Fmt, const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error<StringError>(Stream.str(), inconvertibleErrorCode()); +} + +Error DWARFListTableHeader::extract(DWARFDataExtractor Data, + uint32_t *OffsetPtr) { + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createError("section is not large enough to contain a " + "%s table length at offset 0x%" PRIx32, + SectionName.data(), *OffsetPtr); + // TODO: Add support for DWARF64. + HeaderData.Length = Data.getU32(OffsetPtr); + if (HeaderData.Length == 0xffffffffu) + return createError("DWARF64 is not supported in %s at offset 0x%" PRIx32, + SectionName.data(), HeaderOffset); + Format = dwarf::DwarfFormat::DWARF32; + if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) + return createError("%s table at offset 0x%" PRIx32 + " has too small length (0x%" PRIx32 + ") to contain a complete header", + SectionName.data(), HeaderOffset, length()); + uint32_t End = HeaderOffset + length(); + if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) + return createError("section is not large enough to contain a %s table " + "of length 0x%" PRIx32 " at offset 0x%" PRIx32, + SectionName.data(), length(), HeaderOffset); + + HeaderData.Version = Data.getU16(OffsetPtr); + HeaderData.AddrSize = Data.getU8(OffsetPtr); + HeaderData.SegSize = Data.getU8(OffsetPtr); + HeaderData.OffsetEntryCount = Data.getU32(OffsetPtr); + + // Perform basic validation of the remaining header fields. + if (HeaderData.Version != 5) + return createError("unrecognised %s table version %" PRIu16 + " in table at offset 0x%" PRIx32, + SectionName.data(), HeaderData.Version, HeaderOffset); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createError("%s table at offset 0x%" PRIx32 + " has unsupported address size %hhu", + SectionName.data(), HeaderOffset, HeaderData.AddrSize); + if (HeaderData.SegSize != 0) + return createError("%s table at offset 0x%" PRIx32 + " has unsupported segment selector size %" PRIu8, + SectionName.data(), HeaderOffset, HeaderData.SegSize); + if (End < HeaderOffset + sizeof(HeaderData) + + HeaderData.OffsetEntryCount * sizeof(uint32_t)) + return createError( + "%s table at offset 0x%" PRIx32 " has more offset entries (%" PRIu32 + ") than there is space for", + SectionName.data(), HeaderOffset, HeaderData.OffsetEntryCount); + Data.setAddressSize(HeaderData.AddrSize); + for (uint32_t I = 0; I < HeaderData.OffsetEntryCount; ++I) + Offsets.push_back(Data.getU32(OffsetPtr)); + return Error::success(); +} + +void DWARFListTableHeader::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (DumpOpts.Verbose) + OS << format("0x%8.8" PRIx32 ": ", HeaderOffset); + OS << format( + "%s list header: length = 0x%8.8" PRIx32 ", version = 0x%4.4" PRIx16 ", " + "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 + ", offset_entry_count = " + "0x%8.8" PRIx32 "\n", + ListTypeString.data(), HeaderData.Length, HeaderData.Version, + HeaderData.AddrSize, HeaderData.SegSize, HeaderData.OffsetEntryCount); + + if (HeaderData.OffsetEntryCount > 0) { + OS << "offsets: ["; + for (const auto &Off : Offsets) { + OS << format("\n0x%8.8" PRIx32, Off); + if (DumpOpts.Verbose) + OS << format(" => 0x%8.8" PRIx32, + Off + HeaderOffset + sizeof(HeaderData)); + } + OS << "\n]\n"; + } +} + +uint32_t DWARFListTableHeader::length() const { + if (HeaderData.Length == 0) + return 0; + // TODO: DWARF64 support. + return HeaderData.Length + sizeof(uint32_t); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp index 206c12fa403f..00be75e1a94d 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp @@ -18,26 +18,13 @@ using namespace llvm; -bool DWARFTypeUnit::extractImpl(DataExtractor debug_info, - uint32_t *offset_ptr) { - if (!DWARFUnit::extractImpl(debug_info, offset_ptr)) - return false; - TypeHash = debug_info.getU64(offset_ptr); - TypeOffset = debug_info.getU32(offset_ptr); - // TypeOffset is relative to the beginning of the header, - // so we have to account for the leading length field. - // FIXME: The size of the length field is 12 in DWARF64. - unsigned SizeOfLength = 4; - return TypeOffset < getLength() + SizeOfLength; -} - void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { - DWARFDie TD = getDIEForOffset(TypeOffset + getOffset()); + DWARFDie TD = getDIEForOffset(getTypeOffset() + getOffset()); const char *Name = TD.getName(DINameKind::ShortName); if (DumpOpts.SummarizeTypes) { OS << "name = '" << Name << "'" - << " type_signature = " << format("0x%016" PRIx64, TypeHash) + << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) << " length = " << format("0x%08x", getLength()) << '\n'; return; } @@ -50,8 +37,8 @@ void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { OS << " abbr_offset = " << format("0x%04x", getAbbreviations()->getOffset()) << " addr_size = " << format("0x%02x", getAddressByteSize()) << " name = '" << Name << "'" - << " type_signature = " << format("0x%016" PRIx64, TypeHash) - << " type_offset = " << format("0x%04x", TypeOffset) + << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) + << " type_offset = " << format("0x%04x", getTypeOffset()) << " (next unit at " << format("0x%08x", getNextUnitOffset()) << ")\n"; if (DWARFDie TU = getUnitDIE(false)) diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index df55d7debf92..3b408857d29f 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -8,17 +8,18 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFUnit.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" #include <algorithm> #include <cassert> #include <cstddef> @@ -32,7 +33,7 @@ using namespace dwarf; void DWARFUnitSectionBase::parse(DWARFContext &C, const DWARFSection &Section) { const DWARFObject &D = C.getDWARFObj(); - parseImpl(C, Section, C.getDebugAbbrev(), &D.getRangeSection(), + parseImpl(C, D, Section, C.getDebugAbbrev(), &D.getRangeSection(), D.getStringSection(), D.getStringOffsetSection(), &D.getAddrSection(), D.getLineSection(), D.isLittleEndian(), false, false); @@ -41,22 +42,22 @@ void DWARFUnitSectionBase::parse(DWARFContext &C, const DWARFSection &Section) { void DWARFUnitSectionBase::parseDWO(DWARFContext &C, const DWARFSection &DWOSection, bool Lazy) { const DWARFObject &D = C.getDWARFObj(); - parseImpl(C, DWOSection, C.getDebugAbbrevDWO(), &D.getRangeDWOSection(), + parseImpl(C, D, DWOSection, C.getDebugAbbrevDWO(), &D.getRangeDWOSection(), D.getStringDWOSection(), D.getStringOffsetDWOSection(), &D.getAddrSection(), D.getLineDWOSection(), C.isLittleEndian(), true, Lazy); } DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, + const DWARFUnitHeader &Header, const DWARFDebugAbbrev *DA, const DWARFSection *RS, StringRef SS, const DWARFSection &SOS, const DWARFSection *AOS, const DWARFSection &LS, bool LE, - bool IsDWO, const DWARFUnitSectionBase &UnitSection, - const DWARFUnitIndex::Entry *IndexEntry) - : Context(DC), InfoSection(Section), Abbrev(DA), RangeSection(RS), - LineSection(LS), StringSection(SS), StringOffsetSection(SOS), - AddrOffsetSection(AOS), isLittleEndian(LE), isDWO(IsDWO), - UnitSection(UnitSection), IndexEntry(IndexEntry) { + bool IsDWO, const DWARFUnitSectionBase &UnitSection) + : Context(DC), InfoSection(Section), Header(Header), Abbrev(DA), + RangeSection(RS), LineSection(LS), StringSection(SS), + StringOffsetSection(SOS), AddrOffsetSection(AOS), isLittleEndian(LE), + isDWO(IsDWO), UnitSection(UnitSection) { clear(); } @@ -92,9 +93,16 @@ bool DWARFUnit::getStringOffsetSectionItem(uint32_t Index, return true; } -bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { +bool DWARFUnitHeader::extract(DWARFContext &Context, + const DWARFDataExtractor &debug_info, + uint32_t *offset_ptr, + DWARFSectionKind SectionKind, + const DWARFUnitIndex *Index) { + Offset = *offset_ptr; + IndexEntry = Index ? Index->getFromOffset(*offset_ptr) : nullptr; Length = debug_info.getU32(offset_ptr); // FIXME: Support DWARF64. + unsigned SizeOfLength = 4; FormParams.Format = DWARF32; FormParams.Version = debug_info.getU16(offset_ptr); if (FormParams.Version >= 5) { @@ -102,8 +110,14 @@ bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { FormParams.AddrSize = debug_info.getU8(offset_ptr); AbbrOffset = debug_info.getU32(offset_ptr); } else { - AbbrOffset = debug_info.getU32(offset_ptr); + AbbrOffset = debug_info.getRelocatedValue(4, offset_ptr); FormParams.AddrSize = debug_info.getU8(offset_ptr); + // Fake a unit type based on the section type. This isn't perfect, + // but distinguishing compile and type units is generally enough. + if (SectionKind == DW_SECT_TYPES) + UnitType = DW_UT_type; + else + UnitType = DW_UT_compile; } if (IndexEntry) { if (AbbrOffset) @@ -116,12 +130,27 @@ bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { return false; AbbrOffset = AbbrEntry->Offset; } - + if (isTypeUnit()) { + TypeHash = debug_info.getU64(offset_ptr); + TypeOffset = debug_info.getU32(offset_ptr); + } else if (UnitType == DW_UT_split_compile || UnitType == DW_UT_skeleton) + DWOId = debug_info.getU64(offset_ptr); + + // Header fields all parsed, capture the size of this unit header. + assert(*offset_ptr - Offset <= 255 && "unexpected header size"); + Size = uint8_t(*offset_ptr - Offset); + + // Type offset is unit-relative; should be after the header and before + // the end of the current unit. + bool TypeOffsetOK = + !isTypeUnit() + ? true + : TypeOffset >= Size && TypeOffset < getLength() + SizeOfLength; bool LengthOK = debug_info.isValidOffset(getNextUnitOffset() - 1); bool VersionOK = DWARFContext::isSupportedVersion(getVersion()); bool AddrSizeOK = getAddressByteSize() == 4 || getAddressByteSize() == 8; - if (!LengthOK || !VersionOK || !AddrSizeOK) + if (!LengthOK || !VersionOK || !AddrSizeOK || !TypeOffsetOK) return false; // Keep track of the highest DWARF version we encounter across all units. @@ -129,24 +158,31 @@ bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { return true; } -bool DWARFUnit::extract(DataExtractor debug_info, uint32_t *offset_ptr) { - clear(); - - Offset = *offset_ptr; - - if (debug_info.isValidOffset(*offset_ptr)) { - if (extractImpl(debug_info, offset_ptr)) - return true; - - // reset the offset to where we tried to parse from if anything went wrong - *offset_ptr = Offset; +// Parse the rangelist table header, including the optional array of offsets +// following it (DWARF v5 and later). +static Expected<DWARFDebugRnglistTable> +parseRngListTableHeader(DWARFDataExtractor &DA, uint32_t Offset) { + // TODO: Support DWARF64 + // We are expected to be called with Offset 0 or pointing just past the table + // header, which is 12 bytes long for DWARF32. + if (Offset > 0) { + if (Offset < 12U) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format( + "Did not detect a valid range list table with base = 0x%x", Offset); + return make_error<StringError>(Stream.str(), inconvertibleErrorCode()); + } + Offset -= 12U; } - - return false; + llvm::DWARFDebugRnglistTable Table; + if (Error E = Table.extractHeaderAndOffsets(DA, &Offset)) + return std::move(E); + return Table; } -bool DWARFUnit::extractRangeList(uint32_t RangeListOffset, - DWARFDebugRangeList &RangeList) const { +Error DWARFUnit::extractRangeList(uint32_t RangeListOffset, + DWARFDebugRangeList &RangeList) const { // Require that compile unit is extracted. assert(!DieArray.empty()); DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, @@ -156,10 +192,7 @@ bool DWARFUnit::extractRangeList(uint32_t RangeListOffset, } void DWARFUnit::clear() { - Offset = 0; - Length = 0; Abbrevs = nullptr; - FormParams = DWARFFormParams({0, 0, DWARF32}); BaseAddr.reset(); RangeSectionBase = 0; AddrOffsetSectionBase = 0; @@ -171,10 +204,6 @@ const char *DWARFUnit::getCompilationDir() { return dwarf::toString(getUnitDIE().find(DW_AT_comp_dir), nullptr); } -Optional<uint64_t> DWARFUnit::getDWOId() { - return toUnsigned(getUnitDIE().find(DW_AT_GNU_dwo_id)); -} - void DWARFUnit::extractDIEsToVector( bool AppendCUDie, bool AppendNonCUDies, std::vector<DWARFDebugInfoEntry> &Dies) const { @@ -183,7 +212,7 @@ void DWARFUnit::extractDIEsToVector( // Set the offset to that of the first DIE and calculate the start of the // next compilation unit header. - uint32_t DIEOffset = Offset + getHeaderSize(); + uint32_t DIEOffset = getOffset() + getHeaderSize(); uint32_t NextCUOffset = getNextUnitOffset(); DWARFDebugInfoEntry DIE; DWARFDataExtractor DebugInfoData = getDebugInfoExtractor(); @@ -224,8 +253,9 @@ void DWARFUnit::extractDIEsToVector( // should always terminate at or before the start of the next compilation // unit header). if (DIEOffset > NextCUOffset) - fprintf(stderr, "warning: DWARF compile unit extends beyond its " - "bounds cu 0x%8.8x at 0x%8.8x'\n", getOffset(), DIEOffset); + WithColor::warning() << format("DWARF compile unit extends beyond its " + "bounds cu 0x%8.8x at 0x%8.8x\n", + getOffset(), DIEOffset); } size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { @@ -242,10 +272,8 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { // If CU DIE was just parsed, copy several attribute values from it. if (!HasCUDie) { DWARFDie UnitDie = getUnitDIE(); - Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); - if (Optional<uint64_t> Addr = toAddress(PC)) - setBaseAddress({*Addr, PC->getSectionIndex()}); - + if (Optional<uint64_t> DWOId = toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id))) + Header.setDWOId(*DWOId); if (!isDWO) { assert(AddrOffsetSectionBase == 0); assert(RangeSectionBase == 0); @@ -263,6 +291,7 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { // which may differ from the unit's format. uint64_t StringOffsetsContributionBase = isDWO ? 0 : toSectionOffset(UnitDie.find(DW_AT_str_offsets_base), 0); + auto IndexEntry = Header.getIndexEntry(); if (IndexEntry) if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS)) StringOffsetsContributionBase += C->Offset; @@ -277,6 +306,34 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { StringOffsetsTableContribution = determineStringOffsetsTableContribution( DA, StringOffsetsContributionBase); + // DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to + // describe address ranges. + if (getVersion() >= 5) { + if (isDWO) + setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); + else + setRangesSection(&Context.getDWARFObj().getRnglistsSection(), + toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0)); + if (RangeSection->Data.size()) { + // Parse the range list table header. Individual range lists are + // extracted lazily. + DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, + isLittleEndian, 0); + if (auto TableOrError = + parseRngListTableHeader(RangesDA, RangeSectionBase)) + RngListTable = TableOrError.get(); + else + WithColor::error() << "parsing a range list table: " + << toString(TableOrError.takeError()) + << '\n'; + + // In a split dwarf unit, there is no DW_AT_rnglists_base attribute. + // Adjust RangeSectionBase to point past the table header. + if (isDWO && RngListTable) + RangeSectionBase = RngListTable->getHeaderSize(); + } + } + // 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. } @@ -315,8 +372,23 @@ bool DWARFUnit::parseDWO() { DWO = std::shared_ptr<DWARFCompileUnit>(std::move(DWOContext), DWOCU); // Share .debug_addr and .debug_ranges section with compile unit in .dwo DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); - auto DWORangesBase = UnitDie.getRangesBaseAttribute(); - DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); + if (getVersion() >= 5) { + DWO->setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); + DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, + isLittleEndian, 0); + if (auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase)) + DWO->RngListTable = TableOrError.get(); + else + WithColor::error() << "parsing a range list table: " + << toString(TableOrError.takeError()) + << '\n'; + if (DWO->RngListTable) + DWO->RangeSectionBase = DWO->RngListTable->getHeaderSize(); + } else { + auto DWORangesBase = UnitDie.getRangesBaseAttribute(); + DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); + } + return true; } @@ -327,16 +399,56 @@ void DWARFUnit::clearDIEs(bool KeepCUDie) { } } +Expected<DWARFAddressRangesVector> +DWARFUnit::findRnglistFromOffset(uint32_t Offset) { + if (getVersion() <= 4) { + DWARFDebugRangeList RangeList; + if (Error E = extractRangeList(Offset, RangeList)) + return std::move(E); + return RangeList.getAbsoluteRanges(getBaseAddress()); + } + if (RngListTable) { + DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, + isLittleEndian, RngListTable->getAddrSize()); + auto RangeListOrError = RngListTable->findList(RangesData, Offset); + if (RangeListOrError) + return RangeListOrError.get().getAbsoluteRanges(getBaseAddress()); + return RangeListOrError.takeError(); + } + + return make_error<StringError>("missing or invalid range list table", + inconvertibleErrorCode()); +} + +Expected<DWARFAddressRangesVector> +DWARFUnit::findRnglistFromIndex(uint32_t Index) { + if (auto Offset = getRnglistOffset(Index)) + return findRnglistFromOffset(*Offset + RangeSectionBase); + + std::string Buffer; + raw_string_ostream Stream(Buffer); + if (RngListTable) + Stream << format("invalid range list table index %d", Index); + else + Stream << "missing or invalid range list table"; + return make_error<StringError>(Stream.str(), inconvertibleErrorCode()); +} + void DWARFUnit::collectAddressRanges(DWARFAddressRangesVector &CURanges) { DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) return; // First, check if unit DIE describes address ranges for the whole unit. - const auto &CUDIERanges = UnitDie.getAddressRanges(); - if (!CUDIERanges.empty()) { - CURanges.insert(CURanges.end(), CUDIERanges.begin(), CUDIERanges.end()); - return; - } + auto CUDIERangesOrError = UnitDie.getAddressRanges(); + if (CUDIERangesOrError) { + if (!CUDIERangesOrError.get().empty()) { + CURanges.insert(CURanges.end(), CUDIERangesOrError.get().begin(), + CUDIERangesOrError.get().end()); + return; + } + } else + WithColor::error() << "decoding address ranges: " + << toString(CUDIERangesOrError.takeError()) << '\n'; // This function is usually called if there in no .debug_aranges section // in order to produce a compile unit level set of address ranges that @@ -360,378 +472,49 @@ void DWARFUnit::collectAddressRanges(DWARFAddressRangesVector &CURanges) { clearDIEs(true); } -// Populates a map from PC addresses to subprogram DIEs. -// -// This routine tries to look at the smallest amount of the debug info it can -// to locate the DIEs. This is because many subprograms will never end up being -// read or needed at all. We want to be as lazy as possible. -void DWARFUnit::buildSubprogramDIEAddrMap() { - assert(SubprogramDIEAddrMap.empty() && "Must only build this map once!"); - SmallVector<DWARFDie, 16> Worklist; - Worklist.push_back(getUnitDIE()); - do { - DWARFDie Die = Worklist.pop_back_val(); - - // Queue up child DIEs to recurse through. - // FIXME: This causes us to read a lot more debug info than we really need. - // We should look at pruning out DIEs which cannot transitively hold - // separate subprograms. - for (DWARFDie Child : Die.children()) - Worklist.push_back(Child); - - // If handling a non-subprogram DIE, nothing else to do. - if (!Die.isSubprogramDIE()) - continue; - - // For subprogram DIEs, store them, and insert relevant markers into the - // address map. We don't care about overlap at all here as DWARF doesn't - // meaningfully support that, so we simply will insert a range with no DIE - // starting from the high PC. In the event there are overlaps, sorting - // these may truncate things in surprising ways but still will allow - // lookups to proceed. - int DIEIndex = SubprogramDIEAddrInfos.size(); - SubprogramDIEAddrInfos.push_back({Die, (uint64_t)-1, {}}); - for (const auto &R : Die.getAddressRanges()) { - // Ignore 0-sized ranges. - if (R.LowPC == R.HighPC) - continue; - - SubprogramDIEAddrMap.push_back({R.LowPC, DIEIndex}); - SubprogramDIEAddrMap.push_back({R.HighPC, -1}); - - if (R.LowPC < SubprogramDIEAddrInfos.back().SubprogramBasePC) - SubprogramDIEAddrInfos.back().SubprogramBasePC = R.LowPC; - } - } while (!Worklist.empty()); - - if (SubprogramDIEAddrMap.empty()) { - // If we found no ranges, create a no-op map so that lookups remain simple - // but never find anything. - SubprogramDIEAddrMap.push_back({0, -1}); - return; - } - - // Next, sort the ranges and remove both exact duplicates and runs with the - // same DIE index. We order the ranges so that non-empty ranges are - // preferred. Because there may be ties, we also need to use stable sort. - std::stable_sort(SubprogramDIEAddrMap.begin(), SubprogramDIEAddrMap.end(), - [](const std::pair<uint64_t, int64_t> &LHS, - const std::pair<uint64_t, int64_t> &RHS) { - if (LHS.first < RHS.first) - return true; - if (LHS.first > RHS.first) - return false; - - // For ranges that start at the same address, keep the one - // with a DIE. - if (LHS.second != -1 && RHS.second == -1) - return true; - - return false; - }); - SubprogramDIEAddrMap.erase( - std::unique(SubprogramDIEAddrMap.begin(), SubprogramDIEAddrMap.end(), - [](const std::pair<uint64_t, int64_t> &LHS, - const std::pair<uint64_t, int64_t> &RHS) { - // If the start addresses are exactly the same, we can - // remove all but the first one as it is the only one that - // will be found and used. - // - // If the DIE indices are the same, we can "merge" the - // ranges by eliminating the second. - return LHS.first == RHS.first || LHS.second == RHS.second; - }), - SubprogramDIEAddrMap.end()); - - assert(SubprogramDIEAddrMap.back().second == -1 && - "The last interval must not have a DIE as each DIE's address range is " - "bounded."); -} - -// Build the second level of mapping from PC to DIE, specifically one that maps -// a PC *within* a particular DWARF subprogram into a precise, maximally nested -// inlined subroutine DIE (if any exists). We build a separate map for each -// subprogram because many subprograms will never get queried for an address -// and this allows us to be significantly lazier in reading the DWARF itself. -void DWARFUnit::buildInlinedSubroutineDIEAddrMap( - SubprogramDIEAddrInfo &SPInfo) { - auto &AddrMap = SPInfo.InlinedSubroutineDIEAddrMap; - uint64_t BasePC = SPInfo.SubprogramBasePC; - - auto SubroutineAddrMapSorter = [](const std::pair<int, int> &LHS, - const std::pair<int, int> &RHS) { - if (LHS.first < RHS.first) - return true; - if (LHS.first > RHS.first) - return false; - - // For ranges that start at the same address, keep the - // non-empty one. - if (LHS.second != -1 && RHS.second == -1) - return true; - - return false; - }; - auto SubroutineAddrMapUniquer = [](const std::pair<int, int> &LHS, - const std::pair<int, int> &RHS) { - // If the start addresses are exactly the same, we can - // remove all but the first one as it is the only one that - // will be found and used. - // - // If the DIE indices are the same, we can "merge" the - // ranges by eliminating the second. - return LHS.first == RHS.first || LHS.second == RHS.second; - }; - - struct DieAndParentIntervalRange { - DWARFDie Die; - int ParentIntervalsBeginIdx, ParentIntervalsEndIdx; - }; - - SmallVector<DieAndParentIntervalRange, 16> Worklist; - auto EnqueueChildDIEs = [&](const DWARFDie &Die, int ParentIntervalsBeginIdx, - int ParentIntervalsEndIdx) { - for (DWARFDie Child : Die.children()) - Worklist.push_back( - {Child, ParentIntervalsBeginIdx, ParentIntervalsEndIdx}); - }; - EnqueueChildDIEs(SPInfo.SubprogramDIE, 0, 0); - while (!Worklist.empty()) { - DWARFDie Die = Worklist.back().Die; - int ParentIntervalsBeginIdx = Worklist.back().ParentIntervalsBeginIdx; - int ParentIntervalsEndIdx = Worklist.back().ParentIntervalsEndIdx; - Worklist.pop_back(); - - // If we encounter a nested subprogram, simply ignore it. We map to - // (disjoint) subprograms before arriving here and we don't want to examine - // any inlined subroutines of an unrelated subpragram. - if (Die.getTag() == DW_TAG_subprogram) - continue; - - // For non-subroutines, just recurse to keep searching for inlined - // subroutines. - if (Die.getTag() != DW_TAG_inlined_subroutine) { - EnqueueChildDIEs(Die, ParentIntervalsBeginIdx, ParentIntervalsEndIdx); - continue; - } - - // Capture the inlined subroutine DIE that we will reference from the map. - int DIEIndex = InlinedSubroutineDIEs.size(); - InlinedSubroutineDIEs.push_back(Die); - - int DieIntervalsBeginIdx = AddrMap.size(); - // First collect the PC ranges for this DIE into our subroutine interval - // map. - for (auto R : Die.getAddressRanges()) { - // Clamp the PCs to be above the base. - R.LowPC = std::max(R.LowPC, BasePC); - R.HighPC = std::max(R.HighPC, BasePC); - // Compute relative PCs from the subprogram base and drop down to an - // unsigned 32-bit int to represent them within the data structure. This - // lets us cover a 4gb single subprogram. Because subprograms may be - // partitioned into distant parts of a binary (think hot/cold - // partitioning) we want to preserve as much as we can here without - // burning extra memory. Past that, we will simply truncate and lose the - // ability to map those PCs to a DIE more precise than the subprogram. - const uint32_t MaxRelativePC = std::numeric_limits<uint32_t>::max(); - uint32_t RelativeLowPC = (R.LowPC - BasePC) > (uint64_t)MaxRelativePC - ? MaxRelativePC - : (uint32_t)(R.LowPC - BasePC); - uint32_t RelativeHighPC = (R.HighPC - BasePC) > (uint64_t)MaxRelativePC - ? MaxRelativePC - : (uint32_t)(R.HighPC - BasePC); - // Ignore empty or bogus ranges. - if (RelativeLowPC >= RelativeHighPC) - continue; - AddrMap.push_back({RelativeLowPC, DIEIndex}); - AddrMap.push_back({RelativeHighPC, -1}); - } - - // If there are no address ranges, there is nothing to do to map into them - // and there cannot be any child subroutine DIEs with address ranges of - // interest as those would all be required to nest within this DIE's - // non-existent ranges, so we can immediately continue to the next DIE in - // the worklist. - if (DieIntervalsBeginIdx == (int)AddrMap.size()) - continue; - - // The PCs from this DIE should never overlap, so we can easily sort them - // here. - std::sort(AddrMap.begin() + DieIntervalsBeginIdx, AddrMap.end(), - SubroutineAddrMapSorter); - // Remove any dead ranges. These should only come from "empty" ranges that - // were clobbered by some other range. - AddrMap.erase(std::unique(AddrMap.begin() + DieIntervalsBeginIdx, - AddrMap.end(), SubroutineAddrMapUniquer), - AddrMap.end()); - - // Compute the end index of this DIE's addr map intervals. - int DieIntervalsEndIdx = AddrMap.size(); - - assert(DieIntervalsBeginIdx != DieIntervalsEndIdx && - "Must not have an empty map for this layer!"); - assert(AddrMap.back().second == -1 && "Must end with an empty range!"); - assert(std::is_sorted(AddrMap.begin() + DieIntervalsBeginIdx, AddrMap.end(), - less_first()) && - "Failed to sort this DIE's interals!"); - - // If we have any parent intervals, walk the newly added ranges and find - // the parent ranges they were inserted into. Both of these are sorted and - // neither has any overlaps. We need to append new ranges to split up any - // parent ranges these new ranges would overlap when we merge them. - if (ParentIntervalsBeginIdx != ParentIntervalsEndIdx) { - int ParentIntervalIdx = ParentIntervalsBeginIdx; - for (int i = DieIntervalsBeginIdx, e = DieIntervalsEndIdx - 1; i < e; - ++i) { - const uint32_t IntervalStart = AddrMap[i].first; - const uint32_t IntervalEnd = AddrMap[i + 1].first; - const int IntervalDieIdx = AddrMap[i].second; - if (IntervalDieIdx == -1) { - // For empty intervals, nothing is required. This is a bit surprising - // however. If the prior interval overlaps a parent interval and this - // would be necessary to mark the end, we will synthesize a new end - // that switches back to the parent DIE below. And this interval will - // get dropped in favor of one with a DIE attached. However, we'll - // still include this and so worst-case, it will still end the prior - // interval. - continue; - } - - // We are walking the new ranges in order, so search forward from the - // last point for a parent range that might overlap. - auto ParentIntervalsRange = - make_range(AddrMap.begin() + ParentIntervalIdx, - AddrMap.begin() + ParentIntervalsEndIdx); - assert(std::is_sorted(ParentIntervalsRange.begin(), - ParentIntervalsRange.end(), less_first()) && - "Unsorted parent intervals can't be searched!"); - auto PI = std::upper_bound( - ParentIntervalsRange.begin(), ParentIntervalsRange.end(), - IntervalStart, - [](uint32_t LHS, const std::pair<uint32_t, int32_t> &RHS) { - return LHS < RHS.first; - }); - if (PI == ParentIntervalsRange.begin() || - PI == ParentIntervalsRange.end()) - continue; - - ParentIntervalIdx = PI - AddrMap.begin(); - int32_t &ParentIntervalDieIdx = std::prev(PI)->second; - uint32_t &ParentIntervalStart = std::prev(PI)->first; - const uint32_t ParentIntervalEnd = PI->first; - - // If the new range starts exactly at the position of the parent range, - // we need to adjust the parent range. Note that these collisions can - // only happen with the original parent range because we will merge any - // adjacent ranges in the child. - if (IntervalStart == ParentIntervalStart) { - // If there will be a tail, just shift the start of the parent - // forward. Note that this cannot change the parent ordering. - if (IntervalEnd < ParentIntervalEnd) { - ParentIntervalStart = IntervalEnd; - continue; - } - // Otherwise, mark this as becoming empty so we'll remove it and - // prefer the child range. - ParentIntervalDieIdx = -1; +void DWARFUnit::updateAddressDieMap(DWARFDie Die) { + if (Die.isSubroutineDIE()) { + auto DIERangesOrError = Die.getAddressRanges(); + if (DIERangesOrError) { + for (const auto &R : DIERangesOrError.get()) { + // Ignore 0-sized ranges. + if (R.LowPC == R.HighPC) continue; + auto B = AddrDieMap.upper_bound(R.LowPC); + if (B != AddrDieMap.begin() && R.LowPC < (--B)->second.first) { + // The range is a sub-range of existing ranges, we need to split the + // existing range. + if (R.HighPC < B->second.first) + AddrDieMap[R.HighPC] = B->second; + if (R.LowPC > B->first) + AddrDieMap[B->first].first = R.LowPC; } - - // Finally, if the parent interval will need to remain as a prefix to - // this one, insert a new interval to cover any tail. - if (IntervalEnd < ParentIntervalEnd) - AddrMap.push_back({IntervalEnd, ParentIntervalDieIdx}); + AddrDieMap[R.LowPC] = std::make_pair(R.HighPC, Die); } - } - - // Note that we don't need to re-sort even this DIE's address map intervals - // after this. All of the newly added intervals actually fill in *gaps* in - // this DIE's address map, and we know that children won't need to lookup - // into those gaps. - - // Recurse through its children, giving them the interval map range of this - // DIE to use as their parent intervals. - EnqueueChildDIEs(Die, DieIntervalsBeginIdx, DieIntervalsEndIdx); - } - - if (AddrMap.empty()) { - AddrMap.push_back({0, -1}); - return; + } else + llvm::consumeError(DIERangesOrError.takeError()); } - - // Now that we've added all of the intervals needed, we need to resort and - // unique them. Most notably, this will remove all the empty ranges that had - // a parent range covering, etc. We only expect a single non-empty interval - // at any given start point, so we just use std::sort. This could potentially - // produce non-deterministic maps for invalid DWARF. - std::sort(AddrMap.begin(), AddrMap.end(), SubroutineAddrMapSorter); - AddrMap.erase( - std::unique(AddrMap.begin(), AddrMap.end(), SubroutineAddrMapUniquer), - AddrMap.end()); + // Parent DIEs are added to the AddrDieMap prior to the Children DIEs to + // simplify the logic to update AddrDieMap. The child's range will always + // be equal or smaller than the parent's range. With this assumption, when + // adding one range into the map, it will at most split a range into 3 + // sub-ranges. + for (DWARFDie Child = Die.getFirstChild(); Child; Child = Child.getSibling()) + updateAddressDieMap(Child); } DWARFDie DWARFUnit::getSubroutineForAddress(uint64_t Address) { extractDIEsIfNeeded(false); - - // We use a two-level mapping structure to locate subroutines for a given PC - // address. - // - // First, we map the address to a subprogram. This can be done more cheaply - // because subprograms cannot nest within each other. It also allows us to - // avoid detailed examination of many subprograms, instead only focusing on - // the ones which we end up actively querying. - if (SubprogramDIEAddrMap.empty()) - buildSubprogramDIEAddrMap(); - - assert(!SubprogramDIEAddrMap.empty() && - "We must always end up with a non-empty map!"); - - auto I = std::upper_bound( - SubprogramDIEAddrMap.begin(), SubprogramDIEAddrMap.end(), Address, - [](uint64_t LHS, const std::pair<uint64_t, int64_t> &RHS) { - return LHS < RHS.first; - }); - // If we find the beginning, then the address is before the first subprogram. - if (I == SubprogramDIEAddrMap.begin()) + if (AddrDieMap.empty()) + updateAddressDieMap(getUnitDIE()); + auto R = AddrDieMap.upper_bound(Address); + if (R == AddrDieMap.begin()) return DWARFDie(); - // Back up to the interval containing the address and see if it - // has a DIE associated with it. - --I; - if (I->second == -1) + // upper_bound's previous item contains Address. + --R; + if (Address >= R->second.first) return DWARFDie(); - - auto &SPInfo = SubprogramDIEAddrInfos[I->second]; - - // Now that we have the subprogram for this address, we do the second level - // mapping by building a map within a subprogram's PC range to any specific - // inlined subroutine. - if (SPInfo.InlinedSubroutineDIEAddrMap.empty()) - buildInlinedSubroutineDIEAddrMap(SPInfo); - - // We lookup within the inlined subroutine using a subprogram-relative - // address. - assert(Address >= SPInfo.SubprogramBasePC && - "Address isn't above the start of the subprogram!"); - uint32_t RelativeAddr = ((Address - SPInfo.SubprogramBasePC) > - (uint64_t)std::numeric_limits<uint32_t>::max()) - ? std::numeric_limits<uint32_t>::max() - : (uint32_t)(Address - SPInfo.SubprogramBasePC); - - auto J = - std::upper_bound(SPInfo.InlinedSubroutineDIEAddrMap.begin(), - SPInfo.InlinedSubroutineDIEAddrMap.end(), RelativeAddr, - [](uint32_t LHS, const std::pair<uint32_t, int32_t> &RHS) { - return LHS < RHS.first; - }); - // If we find the beginning, the address is before any inlined subroutine so - // return the subprogram DIE. - if (J == SPInfo.InlinedSubroutineDIEAddrMap.begin()) - return SPInfo.SubprogramDIE; - // Back up `J` and return the inlined subroutine if we have one or the - // subprogram if we don't. - --J; - return J->second == -1 ? SPInfo.SubprogramDIE - : InlinedSubroutineDIEs[J->second]; + return R->second.second; } void @@ -745,11 +528,15 @@ DWARFUnit::getInlinedChainForAddress(uint64_t Address, DWARFDie SubroutineDIE = (DWO ? DWO.get() : this)->getSubroutineForAddress(Address); - while (SubroutineDIE) { - if (SubroutineDIE.isSubroutineDIE()) + if (!SubroutineDIE) + return; + + while (!SubroutineDIE.isSubprogramDIE()) { + if (SubroutineDIE.getTag() == DW_TAG_inlined_subroutine) InlinedChain.push_back(SubroutineDIE); SubroutineDIE = SubroutineDIE.getParent(); } + InlinedChain.push_back(SubroutineDIE); } const DWARFUnitIndex &llvm::getDWARFUnitIndex(DWARFContext &Context, @@ -799,6 +586,25 @@ DWARFDie DWARFUnit::getSibling(const DWARFDebugInfoEntry *Die) { return DWARFDie(); } +DWARFDie DWARFUnit::getPreviousSibling(const DWARFDebugInfoEntry *Die) { + if (!Die) + return DWARFDie(); + uint32_t Depth = Die->getDepth(); + // Unit DIEs always have a depth of zero and never have siblings. + if (Depth == 0) + return DWARFDie(); + + // Find the previous DIE whose depth is the same as the Die's depth. + for (size_t I = getDIEIndex(Die); I > 0;) { + --I; + if (DieArray[I].getDepth() == Depth - 1) + return DWARFDie(); + if (DieArray[I].getDepth() == Depth) + return DWARFDie(this, &DieArray[I]); + } + return DWARFDie(); +} + DWARFDie DWARFUnit::getFirstChild(const DWARFDebugInfoEntry *Die) { if (!Die->hasChildren()) return DWARFDie(); @@ -810,12 +616,39 @@ DWARFDie DWARFUnit::getFirstChild(const DWARFDebugInfoEntry *Die) { return DWARFDie(this, &DieArray[I]); } +DWARFDie DWARFUnit::getLastChild(const DWARFDebugInfoEntry *Die) { + 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?"); + } + return DWARFDie(); +} + const DWARFAbbreviationDeclarationSet *DWARFUnit::getAbbreviations() const { if (!Abbrevs) - Abbrevs = Abbrev->getAbbreviationDeclarationSet(AbbrOffset); + Abbrevs = Abbrev->getAbbreviationDeclarationSet(Header.getAbbrOffset()); return Abbrevs; } +llvm::Optional<BaseAddress> DWARFUnit::getBaseAddress() { + if (BaseAddr) + return BaseAddr; + + DWARFDie UnitDie = getUnitDIE(); + Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); + if (Optional<uint64_t> Addr = toAddress(PC)) + BaseAddr = {*Addr, PC->getSectionIndex()}; + + return BaseAddr; +} + Optional<StrOffsetsContributionDescriptor> StrOffsetsContributionDescriptor::validateContributionSize( DWARFDataExtractor &DA) { @@ -843,7 +676,9 @@ parseDWARF64StringOffsetsTableHeader(DWARFDataExtractor &DA, uint32_t Offset) { uint64_t Size = DA.getU64(&Offset); uint8_t Version = DA.getU16(&Offset); (void)DA.getU16(&Offset); // padding - return StrOffsetsContributionDescriptor(Offset, Size, Version, DWARF64); + // The encoded length includes the 2-byte version field and the 2-byte + // padding, so we need to subtract them out when we populate the descriptor. + return StrOffsetsContributionDescriptor(Offset, Size - 4, Version, DWARF64); //return Optional<StrOffsetsContributionDescriptor>(Descriptor); } @@ -858,7 +693,10 @@ parseDWARF32StringOffsetsTableHeader(DWARFDataExtractor &DA, uint32_t Offset) { return Optional<StrOffsetsContributionDescriptor>(); uint8_t Version = DA.getU16(&Offset); (void)DA.getU16(&Offset); // padding - return StrOffsetsContributionDescriptor(Offset, ContributionSize, Version, DWARF32); + // The encoded length includes the 2-byte version field and the 2-byte + // padding, so we need to subtract them out when we populate the descriptor. + return StrOffsetsContributionDescriptor(Offset, ContributionSize - 4, Version, + DWARF32); //return Optional<StrOffsetsContributionDescriptor>(Descriptor); } @@ -891,6 +729,7 @@ DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor &DA, // index table (in a package file). In a .dwo file it is simply // the length of the string offsets section. uint64_t Size = 0; + auto IndexEntry = Header.getIndexEntry(); if (!IndexEntry) Size = StringOffsetSection.Data.size(); else if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS)) diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index 3d473698b463..82d52c467bc0 100644 --- a/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#include "SyntaxHighlighting.h" #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" @@ -16,8 +16,9 @@ #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" -#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/Support/DJB.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <map> #include <set> @@ -26,7 +27,6 @@ using namespace llvm; using namespace dwarf; using namespace object; -using namespace syntax; DWARFVerifier::DieRangeInfo::address_range_iterator DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { @@ -171,7 +171,7 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, return Success; } -bool DWARFVerifier::verifyUnitContents(DWARFUnit Unit, uint8_t UnitType) { +bool DWARFVerifier::verifyUnitContents(DWARFUnit &Unit, uint8_t UnitType) { uint32_t NumUnitErrors = 0; unsigned NumDies = Unit.getNumDIEs(); for (unsigned I = 0; I < NumDies; ++I) { @@ -274,16 +274,17 @@ bool DWARFVerifier::handleDebugInfo() { if (isUnitDWARF64) break; } else { + DWARFUnitHeader Header; + Header.extract(DCtx, DebugInfoData, &OffsetStart); std::unique_ptr<DWARFUnit> Unit; switch (UnitType) { case dwarf::DW_UT_type: case dwarf::DW_UT_split_type: { Unit.reset(new DWARFTypeUnit( - DCtx, DObj.getInfoSection(), DCtx.getDebugAbbrev(), + DCtx, DObj.getInfoSection(), Header, DCtx.getDebugAbbrev(), &DObj.getRangeSection(), DObj.getStringSection(), DObj.getStringOffsetSection(), &DObj.getAppleObjCSection(), - DObj.getLineSection(), DCtx.isLittleEndian(), false, TUSection, - nullptr)); + DObj.getLineSection(), DCtx.isLittleEndian(), false, TUSection)); break; } case dwarf::DW_UT_skeleton: @@ -294,16 +295,14 @@ bool DWARFVerifier::handleDebugInfo() { // verifying a compile unit in DWARF v4. case 0: { Unit.reset(new DWARFCompileUnit( - DCtx, DObj.getInfoSection(), DCtx.getDebugAbbrev(), + DCtx, DObj.getInfoSection(), Header, DCtx.getDebugAbbrev(), &DObj.getRangeSection(), DObj.getStringSection(), DObj.getStringOffsetSection(), &DObj.getAppleObjCSection(), - DObj.getLineSection(), DCtx.isLittleEndian(), false, CUSection, - nullptr)); + DObj.getLineSection(), DCtx.isLittleEndian(), false, CUSection)); break; } default: { llvm_unreachable("Invalid UnitType."); } } - Unit->extract(DebugInfoData, &OffsetStart); if (!verifyUnitContents(*Unit, UnitType)) ++NumDebugInfoErrors; } @@ -325,8 +324,15 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, if (!Die.isValid()) return NumErrors; - DWARFAddressRangesVector Ranges = Die.getAddressRanges(); + auto RangesOrError = Die.getAddressRanges(); + if (!RangesOrError) { + // FIXME: Report the error. + ++NumErrors; + llvm::consumeError(RangesOrError.takeError()); + return NumErrors; + } + DWARFAddressRangesVector Ranges = RangesOrError.get(); // Build RI for this DIE and check that ranges within this DIE do not // overlap. DieRangeInfo RI(Die); @@ -363,10 +369,9 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, ParentRI.Die.getTag() == DW_TAG_subprogram); if (ShouldBeContained && !ParentRI.contains(RI)) { ++NumErrors; - error() << "DIE address ranges are not " - "contained in its parent's ranges:"; - Die.dump(OS, 0); + error() << "DIE address ranges are not contained in its parent's ranges:"; ParentRI.Die.dump(OS, 0); + Die.dump(OS, 2); OS << "\n"; } @@ -410,22 +415,27 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, ReportError("DIE has invalid DW_AT_stmt_list encoding:"); break; case DW_AT_location: { - Optional<ArrayRef<uint8_t>> Expr = AttrValue.Value.getAsBlock(); - if (!Expr) { - ReportError("DIE has invalid DW_AT_location encoding:"); - break; + auto VerifyLocationExpr = [&](StringRef D) { + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data(D, DCtx.isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getVersion(), + U->getAddressByteSize()); + bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.isError(); + }); + if (Error) + ReportError("DIE contains invalid DWARF expression:"); + }; + if (Optional<ArrayRef<uint8_t>> Expr = AttrValue.Value.getAsBlock()) { + // Verify inlined location. + VerifyLocationExpr(llvm::toStringRef(*Expr)); + } else if (auto LocOffset = AttrValue.Value.getAsSectionOffset()) { + // Verify location list. + if (auto DebugLoc = DCtx.getDebugLoc()) + if (auto LocList = DebugLoc->getLocationListAtOffset(*LocOffset)) + for (const auto &Entry : LocList->Entries) + VerifyLocationExpr({Entry.Loc.data(), Entry.Loc.size()}); } - - DWARFUnit *U = Die.getDwarfUnit(); - DataExtractor Data( - StringRef(reinterpret_cast<const char *>(Expr->data()), Expr->size()), - DCtx.isLittleEndian(), 0); - DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); - bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { - return Op.isError(); - }); - if (Error) - ReportError("DIE contains invalid DWARF expression:"); break; } @@ -669,13 +679,13 @@ bool DWARFVerifier::handleDebugLine() { return NumDebugLineErrors == 0; } -unsigned DWARFVerifier::verifyAccelTable(const DWARFSection *AccelSection, - DataExtractor *StrData, - const char *SectionName) { +unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, + DataExtractor *StrData, + const char *SectionName) { unsigned NumErrors = 0; DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection, DCtx.isLittleEndian(), 0); - DWARFAcceleratorTable AccelTable(AccelSectionData, *StrData); + AppleAcceleratorTable AccelTable(AccelSectionData, *StrData); OS << "Verifying " << SectionName << "...\n"; @@ -773,33 +783,572 @@ unsigned DWARFVerifier::verifyAccelTable(const DWARFSection *AccelSection, return NumErrors; } +unsigned +DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) { + // A map from CU offset to the (first) Name Index offset which claims to index + // this CU. + DenseMap<uint32_t, uint32_t> CUMap; + const uint32_t NotIndexed = std::numeric_limits<uint32_t>::max(); + + CUMap.reserve(DCtx.getNumCompileUnits()); + for (const auto &CU : DCtx.compile_units()) + CUMap[CU->getOffset()] = NotIndexed; + + unsigned NumErrors = 0; + for (const DWARFDebugNames::NameIndex &NI : AccelTable) { + if (NI.getCUCount() == 0) { + error() << formatv("Name Index @ {0:x} does not index any CU\n", + NI.getUnitOffset()); + ++NumErrors; + continue; + } + for (uint32_t CU = 0, End = NI.getCUCount(); CU < End; ++CU) { + uint32_t Offset = NI.getCUOffset(CU); + auto Iter = CUMap.find(Offset); + + if (Iter == CUMap.end()) { + error() << formatv( + "Name Index @ {0:x} references a non-existing CU @ {1:x}\n", + NI.getUnitOffset(), Offset); + ++NumErrors; + continue; + } + + if (Iter->second != NotIndexed) { + error() << formatv("Name Index @ {0:x} references a CU @ {1:x}, but " + "this CU is already indexed by Name Index @ {2:x}\n", + NI.getUnitOffset(), Offset, Iter->second); + continue; + } + Iter->second = NI.getUnitOffset(); + } + } + + for (const auto &KV : CUMap) { + if (KV.second == NotIndexed) + warn() << formatv("CU @ {0:x} not covered by any Name Index\n", KV.first); + } + + return NumErrors; +} + +unsigned +DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, + const DataExtractor &StrData) { + struct BucketInfo { + uint32_t Bucket; + uint32_t Index; + + constexpr BucketInfo(uint32_t Bucket, uint32_t Index) + : Bucket(Bucket), Index(Index) {} + bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; }; + }; + + uint32_t NumErrors = 0; + if (NI.getBucketCount() == 0) { + warn() << formatv("Name Index @ {0:x} does not contain a hash table.\n", + NI.getUnitOffset()); + return NumErrors; + } + + // Build up a list of (Bucket, Index) pairs. We use this later to verify that + // each Name is reachable from the appropriate bucket. + std::vector<BucketInfo> BucketStarts; + BucketStarts.reserve(NI.getBucketCount() + 1); + for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) { + uint32_t Index = NI.getBucketArrayEntry(Bucket); + if (Index > NI.getNameCount()) { + error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid " + "value {2}. Valid range is [0, {3}].\n", + Bucket, NI.getUnitOffset(), Index, NI.getNameCount()); + ++NumErrors; + continue; + } + if (Index > 0) + BucketStarts.emplace_back(Bucket, Index); + } + + // If there were any buckets with invalid values, skip further checks as they + // will likely produce many errors which will only confuse the actual root + // problem. + if (NumErrors > 0) + return NumErrors; + + // Sort the list in the order of increasing "Index" entries. + array_pod_sort(BucketStarts.begin(), BucketStarts.end()); + + // Insert a sentinel entry at the end, so we can check that the end of the + // table is covered in the loop below. + BucketStarts.emplace_back(NI.getBucketCount(), NI.getNameCount() + 1); + + // Loop invariant: NextUncovered is the (1-based) index of the first Name + // which is not reachable by any of the buckets we processed so far (and + // hasn't been reported as uncovered). + uint32_t NextUncovered = 1; + for (const BucketInfo &B : BucketStarts) { + // Under normal circumstances B.Index be equal to NextUncovered, but it can + // be less if a bucket points to names which are already known to be in some + // bucket we processed earlier. In that case, we won't trigger this error, + // but report the mismatched hash value error instead. (We know the hash + // will not match because we have already verified that the name's hash + // puts it into the previous bucket.) + if (B.Index > NextUncovered) { + error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] " + "are not covered by the hash table.\n", + NI.getUnitOffset(), NextUncovered, B.Index - 1); + ++NumErrors; + } + uint32_t Idx = B.Index; + + // The rest of the checks apply only to non-sentinel entries. + if (B.Bucket == NI.getBucketCount()) + break; + + // This triggers if a non-empty bucket points to a name with a mismatched + // hash. Clients are likely to interpret this as an empty bucket, because a + // mismatched hash signals the end of a bucket, but if this is indeed an + // empty bucket, the producer should have signalled this by marking the + // bucket as empty. + uint32_t FirstHash = NI.getHashArrayEntry(Idx); + if (FirstHash % NI.getBucketCount() != B.Bucket) { + error() << formatv( + "Name Index @ {0:x}: Bucket {1} is not empty but points to a " + "mismatched hash value {2:x} (belonging to bucket {3}).\n", + NI.getUnitOffset(), B.Bucket, FirstHash, + FirstHash % NI.getBucketCount()); + ++NumErrors; + } + + // This find the end of this bucket and also verifies that all the hashes in + // this bucket are correct by comparing the stored hashes to the ones we + // compute ourselves. + while (Idx <= NI.getNameCount()) { + uint32_t Hash = NI.getHashArrayEntry(Idx); + if (Hash % NI.getBucketCount() != B.Bucket) + break; + + const char *Str = NI.getNameTableEntry(Idx).getString(); + if (caseFoldingDjbHash(Str) != Hash) { + error() << formatv("Name Index @ {0:x}: String ({1}) at index {2} " + "hashes to {3:x}, but " + "the Name Index hash is {4:x}\n", + NI.getUnitOffset(), Str, Idx, + caseFoldingDjbHash(Str), Hash); + ++NumErrors; + } + + ++Idx; + } + NextUncovered = std::max(NextUncovered, Idx); + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyNameIndexAttribute( + const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr, + DWARFDebugNames::AttributeEncoding AttrEnc) { + StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form); + if (FormName.empty()) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " + "unknown form: {3}.\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, + AttrEnc.Form); + return 1; + } + + if (AttrEnc.Index == DW_IDX_type_hash) { + if (AttrEnc.Form != dwarf::DW_FORM_data8) { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash " + "uses an unexpected form {2} (should be {3}).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8); + return 1; + } + } + + // A list of known index attributes and their expected form classes. + // DW_IDX_type_hash is handled specially in the check above, as it has a + // specific form (not just a form class) we should expect. + struct FormClassTable { + dwarf::Index Index; + DWARFFormValue::FormClass Class; + StringLiteral ClassName; + }; + static constexpr FormClassTable Table[] = { + {dwarf::DW_IDX_compile_unit, DWARFFormValue::FC_Constant, {"constant"}}, + {dwarf::DW_IDX_type_unit, DWARFFormValue::FC_Constant, {"constant"}}, + {dwarf::DW_IDX_die_offset, DWARFFormValue::FC_Reference, {"reference"}}, + {dwarf::DW_IDX_parent, DWARFFormValue::FC_Constant, {"constant"}}, + }; + + ArrayRef<FormClassTable> TableRef(Table); + auto Iter = find_if(TableRef, [AttrEnc](const FormClassTable &T) { + return T.Index == AttrEnc.Index; + }); + if (Iter == TableRef.end()) { + warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an " + "unknown index attribute: {2}.\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index); + return 0; + } + + if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " + "unexpected form {3} (expected form class {4}).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, + AttrEnc.Form, Iter->ClassName); + return 1; + } + return 0; +} + +unsigned +DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { + if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) { + warn() << formatv("Name Index @ {0:x}: Verifying indexes of type units is " + "not currently supported.\n", + NI.getUnitOffset()); + return 0; + } + + unsigned NumErrors = 0; + for (const auto &Abbrev : NI.getAbbrevs()) { + StringRef TagName = dwarf::TagString(Abbrev.Tag); + if (TagName.empty()) { + warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} references an " + "unknown tag: {2}.\n", + NI.getUnitOffset(), Abbrev.Code, Abbrev.Tag); + } + SmallSet<unsigned, 5> Attributes; + for (const auto &AttrEnc : Abbrev.Attributes) { + if (!Attributes.insert(AttrEnc.Index).second) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains " + "multiple {2} attributes.\n", + NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index); + ++NumErrors; + continue; + } + NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc); + } + + if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) { + error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units " + "and abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, + dwarf::DW_IDX_compile_unit); + ++NumErrors; + } + if (!Attributes.count(dwarf::DW_IDX_die_offset)) { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset); + ++NumErrors; + } + } + return NumErrors; +} + +static SmallVector<StringRef, 2> getNames(const DWARFDie &DIE) { + SmallVector<StringRef, 2> Result; + if (const char *Str = DIE.getName(DINameKind::ShortName)) + Result.emplace_back(Str); + else if (DIE.getTag() == dwarf::DW_TAG_namespace) + Result.emplace_back("(anonymous namespace)"); + + if (const char *Str = DIE.getName(DINameKind::LinkageName)) { + if (Result.empty() || Result[0] != Str) + Result.emplace_back(Str); + } + + return Result; +} + +unsigned DWARFVerifier::verifyNameIndexEntries( + const DWARFDebugNames::NameIndex &NI, + const DWARFDebugNames::NameTableEntry &NTE) { + // Verifying type unit indexes not supported. + if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) + return 0; + + const char *CStr = NTE.getString(); + if (!CStr) { + error() << formatv( + "Name Index @ {0:x}: Unable to get string associated with name {1}.\n", + NI.getUnitOffset(), NTE.getIndex()); + return 1; + } + StringRef Str(CStr); + + unsigned NumErrors = 0; + unsigned NumEntries = 0; + uint32_t EntryID = NTE.getEntryOffset(); + uint32_t NextEntryID = EntryID; + Expected<DWARFDebugNames::Entry> EntryOr = NI.getEntry(&NextEntryID); + for (; EntryOr; ++NumEntries, EntryID = NextEntryID, + EntryOr = NI.getEntry(&NextEntryID)) { + uint32_t CUIndex = *EntryOr->getCUIndex(); + if (CUIndex > NI.getCUCount()) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an " + "invalid CU index ({2}).\n", + NI.getUnitOffset(), EntryID, CUIndex); + ++NumErrors; + continue; + } + uint32_t CUOffset = NI.getCUOffset(CUIndex); + uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset(); + DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset); + if (!DIE) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a " + "non-existing DIE @ {2:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset); + ++NumErrors; + continue; + } + if (DIE.getDwarfUnit()->getOffset() != CUOffset) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of " + "DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, CUOffset, + DIE.getDwarfUnit()->getOffset()); + ++NumErrors; + } + if (DIE.getTag() != EntryOr->tag()) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of " + "DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(), + DIE.getTag()); + ++NumErrors; + } + + auto EntryNames = getNames(DIE); + if (!is_contained(EntryNames, Str)) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " + "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, Str, + make_range(EntryNames.begin(), EntryNames.end())); + ++NumErrors; + } + } + handleAllErrors(EntryOr.takeError(), + [&](const DWARFDebugNames::SentinelError &) { + if (NumEntries > 0) + return; + error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is " + "not associated with any entries.\n", + NI.getUnitOffset(), NTE.getIndex(), Str); + ++NumErrors; + }, + [&](const ErrorInfoBase &Info) { + error() + << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n", + NI.getUnitOffset(), NTE.getIndex(), Str, + Info.message()); + ++NumErrors; + }); + return NumErrors; +} + +static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) { + Optional<DWARFFormValue> Location = Die.findRecursively(DW_AT_location); + if (!Location) + return false; + + auto ContainsInterestingOperators = [&](StringRef D) { + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data(D, DCtx.isLittleEndian(), U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + return any_of(Expression, [](DWARFExpression::Operation &Op) { + return !Op.isError() && (Op.getCode() == DW_OP_addr || + Op.getCode() == DW_OP_form_tls_address || + Op.getCode() == DW_OP_GNU_push_tls_address); + }); + }; + + if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) { + // Inlined location. + if (ContainsInterestingOperators(toStringRef(*Expr))) + return true; + } else if (Optional<uint64_t> Offset = Location->getAsSectionOffset()) { + // Location list. + if (const DWARFDebugLoc *DebugLoc = DCtx.getDebugLoc()) { + if (const DWARFDebugLoc::LocationList *LocList = + DebugLoc->getLocationListAtOffset(*Offset)) { + if (any_of(LocList->Entries, [&](const DWARFDebugLoc::Entry &E) { + return ContainsInterestingOperators({E.Loc.data(), E.Loc.size()}); + })) + return true; + } + } + } + return false; +} + +unsigned DWARFVerifier::verifyNameIndexCompleteness( + const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) { + + // First check, if the Die should be indexed. The code follows the DWARF v5 + // wording as closely as possible. + + // "All non-defining declarations (that is, debugging information entries + // with a DW_AT_declaration attribute) are excluded." + if (Die.find(DW_AT_declaration)) + return 0; + + // "DW_TAG_namespace debugging information entries without a DW_AT_name + // attribute are included with the name “(anonymous namespace)”. + // All other debugging information entries without a DW_AT_name attribute + // are excluded." + // "If a subprogram or inlined subroutine is included, and has a + // DW_AT_linkage_name attribute, there will be an additional index entry for + // the linkage name." + auto EntryNames = getNames(Die); + if (EntryNames.empty()) + return 0; + + // We deviate from the specification here, which says: + // "The name index must contain an entry for each debugging information entry + // that defines a named subprogram, label, variable, type, or namespace, + // subject to ..." + // Instead whitelisting all TAGs representing a "type" or a "subprogram", to + // make sure we catch any missing items, we instead blacklist all TAGs that we + // know shouldn't be indexed. + switch (Die.getTag()) { + // Compile unit has a name but it shouldn't be indexed. + case DW_TAG_compile_unit: + return 0; + + // Function and template parameters are not globally visible, so we shouldn't + // index them. + case DW_TAG_formal_parameter: + case DW_TAG_template_value_parameter: + case DW_TAG_template_type_parameter: + case DW_TAG_GNU_template_parameter_pack: + case DW_TAG_GNU_template_template_param: + return 0; + + // Object members aren't globally visible. + case DW_TAG_member: + return 0; + + // According to a strict reading of the specification, enumerators should not + // be indexed (and LLVM currently does not do that). However, this causes + // problems for the debuggers, so we may need to reconsider this. + case DW_TAG_enumerator: + return 0; + + // Imported declarations should not be indexed according to the specification + // and LLVM currently does not do that. + case DW_TAG_imported_declaration: + return 0; + + // "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging + // information entries without an address attribute (DW_AT_low_pc, + // DW_AT_high_pc, DW_AT_ranges, or DW_AT_entry_pc) are excluded." + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_label: + if (Die.findRecursively( + {DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc})) + break; + return 0; + + // "DW_TAG_variable debugging information entries with a DW_AT_location + // attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are + // included; otherwise, they are excluded." + // + // LLVM extension: We also add DW_OP_GNU_push_tls_address to this list. + case DW_TAG_variable: + if (isVariableIndexable(Die, DCtx)) + break; + return 0; + + default: + break; + } + + // Now we know that our Die should be present in the Index. Let's check if + // that's the case. + unsigned NumErrors = 0; + uint64_t DieUnitOffset = Die.getOffset() - Die.getDwarfUnit()->getOffset(); + for (StringRef Name : EntryNames) { + if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) { + return E.getDIEUnitOffset() == DieUnitOffset; + })) { + error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with " + "name {3} missing.\n", + NI.getUnitOffset(), Die.getOffset(), Die.getTag(), + Name); + ++NumErrors; + } + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, + const DataExtractor &StrData) { + unsigned NumErrors = 0; + DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), AccelSection, + DCtx.isLittleEndian(), 0); + DWARFDebugNames AccelTable(AccelSectionData, StrData); + + OS << "Verifying .debug_names...\n"; + + // This verifies that we can read individual name indices and their + // abbreviation tables. + if (Error E = AccelTable.extract()) { + error() << toString(std::move(E)) << '\n'; + return 1; + } + + NumErrors += verifyDebugNamesCULists(AccelTable); + for (const auto &NI : AccelTable) + NumErrors += verifyNameIndexBuckets(NI, StrData); + for (const auto &NI : AccelTable) + NumErrors += verifyNameIndexAbbrevs(NI); + + // Don't attempt Entry validation if any of the previous checks found errors + if (NumErrors > 0) + return NumErrors; + for (const auto &NI : AccelTable) + for (DWARFDebugNames::NameTableEntry NTE : NI) + NumErrors += verifyNameIndexEntries(NI, NTE); + + if (NumErrors > 0) + return NumErrors; + + for (const std::unique_ptr<DWARFCompileUnit> &CU : DCtx.compile_units()) { + if (const DWARFDebugNames::NameIndex *NI = + AccelTable.getCUNameIndex(CU->getOffset())) { + for (const DWARFDebugInfoEntry &Die : CU->dies()) + NumErrors += verifyNameIndexCompleteness(DWARFDie(CU.get(), &Die), *NI); + } + } + return NumErrors; +} + bool DWARFVerifier::handleAccelTables() { const DWARFObject &D = DCtx.getDWARFObj(); DataExtractor StrData(D.getStringSection(), DCtx.isLittleEndian(), 0); unsigned NumErrors = 0; if (!D.getAppleNamesSection().Data.empty()) NumErrors += - verifyAccelTable(&D.getAppleNamesSection(), &StrData, ".apple_names"); + verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData, ".apple_names"); if (!D.getAppleTypesSection().Data.empty()) NumErrors += - verifyAccelTable(&D.getAppleTypesSection(), &StrData, ".apple_types"); + verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData, ".apple_types"); if (!D.getAppleNamespacesSection().Data.empty()) - NumErrors += verifyAccelTable(&D.getAppleNamespacesSection(), &StrData, + NumErrors += verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData, ".apple_namespaces"); if (!D.getAppleObjCSection().Data.empty()) NumErrors += - verifyAccelTable(&D.getAppleObjCSection(), &StrData, ".apple_objc"); + verifyAppleAccelTable(&D.getAppleObjCSection(), &StrData, ".apple_objc"); + + if (!D.getDebugNamesSection().Data.empty()) + NumErrors += verifyDebugNames(D.getDebugNamesSection(), StrData); return NumErrors == 0; } -raw_ostream &DWARFVerifier::error() const { - return WithColor(OS, syntax::Error).get() << "error: "; -} +raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); } -raw_ostream &DWARFVerifier::warn() const { - return WithColor(OS, syntax::Warning).get() << "warning: "; -} +raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); } -raw_ostream &DWARFVerifier::note() const { - return WithColor(OS, syntax::Note).get() << "note: "; -} +raw_ostream &DWARFVerifier::note() const { return WithColor::note(OS); } diff --git a/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp b/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp deleted file mode 100644 index 65d66fc8f514..000000000000 --- a/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp +++ /dev/null @@ -1,43 +0,0 @@ -//===- SyntaxHighlighting.cpp ---------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "SyntaxHighlighting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; -using namespace dwarf; -using namespace syntax; - -static cl::opt<cl::boolOrDefault> - UseColor("color", - cl::desc("use colored syntax highlighting (default=autodetect)"), - cl::init(cl::BOU_UNSET)); - -WithColor::WithColor(raw_ostream &OS, enum HighlightColor Type) : OS(OS) { - // Detect color from terminal type unless the user passed the --color option. - if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE) { - switch (Type) { - case Address: OS.changeColor(raw_ostream::YELLOW); break; - case String: OS.changeColor(raw_ostream::GREEN); break; - case Tag: OS.changeColor(raw_ostream::BLUE); break; - case Attribute: OS.changeColor(raw_ostream::CYAN); break; - case Enumerator: OS.changeColor(raw_ostream::MAGENTA); break; - case Macro: OS.changeColor(raw_ostream::RED); break; - case Error: OS.changeColor(raw_ostream::RED, true); break; - case Warning: OS.changeColor(raw_ostream::MAGENTA, true); break; - case Note: OS.changeColor(raw_ostream::BLACK, true); break; - } - } -} - -WithColor::~WithColor() { - if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE) - OS.resetColor(); -} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.h b/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.h deleted file mode 100644 index 686cf2c77608..000000000000 --- a/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.h +++ /dev/null @@ -1,52 +0,0 @@ -//===- SyntaxHighlighting.h -------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H -#define LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H - -namespace llvm { - -class raw_ostream; - -namespace dwarf { -namespace syntax { - -// Symbolic names for various syntax elements. -enum HighlightColor { - Address, - String, - Tag, - Attribute, - Enumerator, - Macro, - Error, - Warning, - Note -}; - -/// An RAII object that temporarily switches an output stream to a -/// specific color. -class WithColor { - raw_ostream &OS; - -public: - /// To be used like this: WithColor(OS, syntax::String) << "text"; - WithColor(raw_ostream &OS, enum HighlightColor Type); - ~WithColor(); - - raw_ostream &get() { return OS; } - operator raw_ostream &() { return OS; } -}; - -} // end namespace syntax -} // end namespace dwarf - -} // end namespace llvm - -#endif // LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H |