diff options
Diffstat (limited to 'contrib/llvm/lib/DebugInfo/DWARF')
27 files changed, 10659 insertions, 0 deletions
diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 000000000000..f49ab40fad9a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,216 @@ +//===- DWARFAbbreviationDeclaration.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/DWARFAbbreviationDeclaration.h" + +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#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> + +using namespace llvm; +using namespace dwarf; + +void DWARFAbbreviationDeclaration::clear() { + Code = 0; + Tag = DW_TAG_null; + CodeByteSize = 0; + HasChildren = false; + AttributeSpecs.clear(); + FixedAttributeSize.reset(); +} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() { + clear(); +} + +bool +DWARFAbbreviationDeclaration::extract(DataExtractor Data, + uint32_t* OffsetPtr) { + clear(); + const uint32_t Offset = *OffsetPtr; + Code = Data.getULEB128(OffsetPtr); + if (Code == 0) { + return false; + } + CodeByteSize = *OffsetPtr - Offset; + Tag = static_cast<llvm::dwarf::Tag>(Data.getULEB128(OffsetPtr)); + if (Tag == DW_TAG_null) { + clear(); + return false; + } + uint8_t ChildrenByte = Data.getU8(OffsetPtr); + HasChildren = (ChildrenByte == DW_CHILDREN_yes); + // Assign a value to our optional FixedAttributeSize member variable. If + // this member variable still has a value after the while loop below, then + // all attribute data in this abbreviation declaration has a fixed byte size. + FixedAttributeSize = FixedSizeInfo(); + + // Read all of the abbreviation attributes and forms. + while (true) { + auto A = static_cast<Attribute>(Data.getULEB128(OffsetPtr)); + auto F = static_cast<Form>(Data.getULEB128(OffsetPtr)); + if (A && F) { + bool IsImplicitConst = (F == DW_FORM_implicit_const); + if (IsImplicitConst) { + int64_t V = Data.getSLEB128(OffsetPtr); + AttributeSpecs.push_back(AttributeSpec(A, F, V)); + continue; + } + Optional<uint8_t> ByteSize; + // If this abbrevation still has a fixed byte size, then update the + // FixedAttributeSize as needed. + switch (F) { + case DW_FORM_addr: + if (FixedAttributeSize) + ++FixedAttributeSize->NumAddrs; + break; + + case DW_FORM_ref_addr: + if (FixedAttributeSize) + ++FixedAttributeSize->NumRefAddrs; + break; + + 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: + if (FixedAttributeSize) + ++FixedAttributeSize->NumDwarfOffsets; + break; + + 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 = dwarf::getFixedFormByteSize(F, dwarf::FormParams()))) { + if (FixedAttributeSize) + FixedAttributeSize->NumBytes += *ByteSize; + break; + } + // Indicate we no longer have a fixed byte size for this + // abbreviation by clearing the FixedAttributeSize optional value + // so it doesn't have a value. + FixedAttributeSize.reset(); + break; + } + // Record this attribute and its fixed size if it has one. + AttributeSpecs.push_back(AttributeSpec(A, F, ByteSize)); + } else if (A == 0 && F == 0) { + // We successfully reached the end of this abbreviation declaration + // since both attribute and form are zero. + break; + } else { + // Attribute and form pairs must either both be non-zero, in which case + // they are added to the abbreviation declaration, or both be zero to + // terminate the abbrevation declaration. In this case only one was + // zero which is an error. + clear(); + return false; + } + } + return true; +} + +void DWARFAbbreviationDeclaration::dump(raw_ostream &OS) const { + OS << '[' << getCode() << "] "; + OS << formatv("{0}", getTag()); + OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no") << '\n'; + for (const AttributeSpec &Spec : AttributeSpecs) { + OS << formatv("\t{0}\t{1}", Spec.Attr, Spec.Form); + if (Spec.isImplicitConst()) + OS << '\t' << Spec.getImplicitConstValue(); + OS << '\n'; + } + OS << '\n'; +} + +Optional<uint32_t> +DWARFAbbreviationDeclaration::findAttributeIndex(dwarf::Attribute Attr) const { + for (uint32_t i = 0, e = AttributeSpecs.size(); i != e; ++i) { + if (AttributeSpecs[i].Attr == Attr) + return i; + } + return None; +} + +Optional<DWARFFormValue> DWARFAbbreviationDeclaration::getAttributeValue( + const uint32_t DIEOffset, const dwarf::Attribute Attr, + const DWARFUnit &U) const { + Optional<uint32_t> MatchAttrIndex = findAttributeIndex(Attr); + if (!MatchAttrIndex) + return None; + + auto DebugInfoData = U.getDebugInfoExtractor(); + + // Add the byte size of ULEB that for the abbrev Code so we can start + // skipping the attribute data. + uint32_t Offset = DIEOffset + CodeByteSize; + uint32_t AttrIndex = 0; + for (const auto &Spec : AttributeSpecs) { + if (*MatchAttrIndex == AttrIndex) { + // We have arrived at the attribute to extract, extract if from Offset. + DWARFFormValue FormValue(Spec.Form); + if (Spec.isImplicitConst()) { + FormValue.setSValue(Spec.getImplicitConstValue()); + return FormValue; + } + if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U)) + return FormValue; + } + // March Offset along until we get to the attribute we want. + if (auto FixedSize = Spec.getByteSize(U)) + Offset += *FixedSize; + else + DWARFFormValue::skipValue(Spec.Form, DebugInfoData, &Offset, + U.getFormParams()); + ++AttrIndex; + } + return None; +} + +size_t DWARFAbbreviationDeclaration::FixedSizeInfo::getByteSize( + const DWARFUnit &U) const { + size_t ByteSize = NumBytes; + if (NumAddrs) + ByteSize += NumAddrs * U.getAddressByteSize(); + if (NumRefAddrs) + ByteSize += NumRefAddrs * U.getRefAddrByteSize(); + if (NumDwarfOffsets) + ByteSize += NumDwarfOffsets * U.getDwarfOffsetByteSize(); + return ByteSize; +} + +Optional<int64_t> DWARFAbbreviationDeclaration::AttributeSpec::getByteSize( + const DWARFUnit &U) const { + if (isImplicitConst()) + return 0; + if (ByteSize.HasByteSize) + return ByteSize.ByteSize; + Optional<int64_t> S; + auto FixedByteSize = dwarf::getFixedFormByteSize(Form, U.getFormParams()); + if (FixedByteSize) + S = *FixedByteSize; + return S; +} + +Optional<size_t> DWARFAbbreviationDeclaration::getFixedAttributesByteSize( + const DWARFUnit &U) const { + if (FixedAttributeSize) + return FixedAttributeSize->getByteSize(U); + return None; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp new file mode 100644 index 000000000000..54daf34ff253 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -0,0 +1,887 @@ +//===- DWARFAcceleratorTable.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/DWARFAcceleratorTable.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/Errc.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> +#include <utility> + +using namespace llvm; + +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. + if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength) + 4)) + return createStringError(errc::illegal_byte_sequence, + "Section too small: cannot read header."); + + Hdr.Magic = AccelSection.getU32(&Offset); + Hdr.Version = AccelSection.getU16(&Offset); + Hdr.HashFunction = AccelSection.getU16(&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 + // section (see SourceLevelDebugging.rst for the structure of the index). + // 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.BucketCount * 4 + Hdr.HashCount * 8 - 1)) + return createStringError( + errc::illegal_byte_sequence, + "Section too small: cannot read buckets and hashes."); + + HdrData.DIEOffsetBase = AccelSection.getU32(&Offset); + uint32_t NumAtoms = AccelSection.getU32(&Offset); + + for (unsigned i = 0; i < NumAtoms; ++i) { + uint16_t AtomType = AccelSection.getU16(&Offset); + auto AtomForm = static_cast<dwarf::Form>(AccelSection.getU16(&Offset)); + HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm)); + } + + IsValid = true; + return Error::success(); +} + +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<AppleAcceleratorTable::HeaderData::AtomType, + AppleAcceleratorTable::HeaderData::Form>> +AppleAcceleratorTable::getAtomsDesc() { + return HdrData.Atoms; +} + +bool AppleAcceleratorTable::validateForms() { + for (auto Atom : getAtomsDesc()) { + DWARFFormValue FormValue(Atom.second); + switch (Atom.first) { + case dwarf::DW_ATOM_die_offset: + case dwarf::DW_ATOM_die_tag: + case dwarf::DW_ATOM_type_flags: + if ((!FormValue.isFormClass(DWARFFormValue::FC_Constant) && + !FormValue.isFormClass(DWARFFormValue::FC_Flag)) || + FormValue.getForm() == dwarf::DW_FORM_sdata) + return false; + break; + default: + break; + } + } + return true; +} + +std::pair<uint32_t, dwarf::Tag> +AppleAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { + uint32_t DieOffset = dwarf::DW_INVALID_OFFSET; + dwarf::Tag DieTag = dwarf::DW_TAG_null; + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + + for (auto Atom : getAtomsDesc()) { + DWARFFormValue FormValue(Atom.second); + FormValue.extractValue(AccelSection, &HashDataOffset, FormParams); + switch (Atom.first) { + case dwarf::DW_ATOM_die_offset: + DieOffset = *FormValue.getAsUnsignedConstant(); + break; + case dwarf::DW_ATOM_die_tag: + DieTag = (dwarf::Tag)*FormValue.getAsUnsignedConstant(); + break; + default: + break; + } + } + return {DieOffset, DieTag}; +} + +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; + + 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; + { + 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.BucketCount * 4; + unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4; + + for (unsigned Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) { + unsigned Index = AccelSection.getU32(&Offset); + + ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); + if (Index == UINT32_MAX) { + W.printString("EMPTY"); + continue; + } + + 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.BucketCount != Bucket) + break; + + unsigned DataOffset = AccelSection.getU32(&OffsetsOffset); + ListScope HashScope(W, ("Hash 0x" + Twine::utohexstr(Hash)).str()); + if (!AccelSection.isValidOffset(DataOffset)) { + W.printString("Invalid section offset"); + continue; + } + while (dumpName(W, AtomForms, &DataOffset)) + /*empty*/; + } + } +} + +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; + + // Read the first entry. + NumData = AccelTable.AccelSection.getU32(&DataOffset); + 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; + } + Current.extract(*AccelTable, &DataOffset); + ++Data; +} + +iterator_range<AppleAcceleratorTable::ValueIterator> +AppleAcceleratorTable::equal_range(StringRef Key) const { + if (!IsValid) + return make_range(ValueIterator(), ValueIterator()); + + // Find the bucket. + unsigned HashValue = djbHash(Key); + unsigned Bucket = HashValue % Hdr.BucketCount; + unsigned BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength; + 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.HashCount; ++HashIdx) { + unsigned HashOffset = HashesBase + HashIdx * 4; + unsigned OffsetsOffset = OffsetsBase + HashIdx * 4; + uint32_t Hash = AccelSection.getU32(&HashOffset); + + if (Hash % Hdr.BucketCount != Bucket) + // We are already in the next bucket. + break; + + unsigned DataOffset = AccelSection.getU32(&OffsetsOffset); + unsigned StringOffset = AccelSection.getRelocatedValue(4, &DataOffset); + if (!StringOffset) + break; + + // Finally, compare the key. + if (Key == StringSection.getCStr(&StringOffset)) + return make_range({*this, DataOffset}, ValueIterator()); + } + 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 createStringError(errc::illegal_byte_sequence, + "Section too small: cannot read header."); + + 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 createStringError( + errc::illegal_byte_sequence, + "Section too small: cannot read header augmentation."); + 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 createStringError(errc::illegal_byte_sequence, + "Incorrectly terminated abbreviation table."); + } + + 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 createStringError(errc::illegal_byte_sequence, + "Incorrectly terminated abbreviation table."); + } + + 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 createStringError(errc::illegal_byte_sequence, + "Section too small: cannot read abbreviations."); + + 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 createStringError(errc::invalid_argument, + "Duplicate abbreviation code."); + } +} +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 createStringError(errc::illegal_byte_sequence, + "Incorrectly terminated entry list."); + + 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 createStringError(errc::invalid_argument, "Invalid abbreviation."); + + 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 createStringError(errc::io_error, + "Error extracting index attribute values."); + } + 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 new file mode 100644 index 000000000000..00a23b3898fa --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,37 @@ +//===-- DWARFCompileUnit.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/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void DWARFCompileUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + OS << format("0x%08x", getOffset()) << ": Compile Unit:" + << " length = " << format("0x%08x", getLength()) + << " version = " << format("0x%04x", getVersion()); + if (getVersion() >= 5) + OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << " abbr_offset = " << format("0x%04x", getAbbreviations()->getOffset()) + << " 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); + else + OS << "<compile unit can't be parsed!>\n\n"; +} + +// VTable anchor. +DWARFCompileUnit::~DWARFCompileUnit() = default; diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp new file mode 100644 index 000000000000..e6620ee3dd1d --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -0,0 +1,1693 @@ +//===- DWARFContext.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/DWARFContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#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" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Object/Decompressor.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/RelocVisitor.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#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> +#include <vector> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +#define DEBUG_TYPE "dwarf" + +using DWARFLineTable = DWARFDebugLine::LineTable; +using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; +using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; + +DWARFContext::DWARFContext(std::unique_ptr<const DWARFObject> DObj, + std::string DWPName) + : DIContext(CK_DWARF), DWPName(std::move(DWPName)), DObj(std::move(DObj)) {} + +DWARFContext::~DWARFContext() = default; + +/// Dump the UUID load command. +static void dumpUUID(raw_ostream &OS, const ObjectFile &Obj) { + auto *MachO = dyn_cast<MachOObjectFile>(&Obj); + if (!MachO) + return; + for (auto LC : MachO->load_commands()) { + raw_ostream::uuid_t UUID; + if (LC.C.cmd == MachO::LC_UUID) { + if (LC.C.cmdsize < sizeof(UUID) + sizeof(LC.C)) { + OS << "error: UUID load command is too short.\n"; + return; + } + OS << "UUID: "; + memcpy(&UUID, LC.Ptr+sizeof(LC.C), sizeof(UUID)); + OS.write_uuid(UUID); + Triple T = MachO->getArchTriple(); + OS << " (" << T.getArchName() << ')'; + OS << ' ' << MachO->getFileName() << '\n'; + } + } +} + +using ContributionCollection = + std::vector<Optional<StrOffsetsContributionDescriptor>>; + +// Collect all the contributions to the string offsets table from all units, +// sort them by their starting offsets and remove duplicates. +static ContributionCollection +collectContributionData(DWARFContext::unit_iterator_range Units) { + ContributionCollection Contributions; + for (const auto &U : Units) + Contributions.push_back(U->getStringOffsetsTableContribution()); + // Sort the contributions so that any invalid ones are placed at + // the start of the contributions vector. This way they are reported + // first. + llvm::sort(Contributions, + [](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 + // to report them more than once. + Contributions.erase( + std::unique(Contributions.begin(), Contributions.end(), + [](const Optional<StrOffsetsContributionDescriptor> &L, + const Optional<StrOffsetsContributionDescriptor> &R) { + if (L && R) + return L->Base == R->Base && L->Size == R->Size; + return false; + }), + Contributions.end()); + return Contributions; +} + +static void dumpDWARFv5StringOffsetsSection( + raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, + const DWARFSection &StringOffsetsSection, StringRef StringSection, + DWARFContext::unit_iterator_range Units, bool LittleEndian) { + auto Contributions = collectContributionData(Units); + DWARFDataExtractor StrOffsetExt(Obj, StringOffsetsSection, LittleEndian, 0); + DataExtractor StrData(StringSection, LittleEndian, 0); + uint64_t SectionSize = StringOffsetsSection.Data.size(); + uint32_t Offset = 0; + for (auto &Contribution : Contributions) { + // Report an ill-formed contribution. + if (!Contribution) { + OS << "error: invalid contribution to string offsets table in section ." + << SectionName << ".\n"; + return; + } + + dwarf::DwarfFormat Format = Contribution->getFormat(); + uint16_t Version = Contribution->getVersion(); + uint64_t ContributionHeader = Contribution->Base; + // In DWARF v5 there is a contribution header that immediately precedes + // the string offsets base (the location we have previously retrieved from + // the CU DIE's DW_AT_str_offsets attribute). The header is located either + // 8 or 16 bytes before the base, depending on the contribution's format. + if (Version >= 5) + ContributionHeader -= Format == DWARF32 ? 8 : 16; + + // Detect overlapping contributions. + if (Offset > ContributionHeader) { + OS << "error: overlapping contributions to string offsets table in " + "section ." + << SectionName << ".\n"; + return; + } + // Report a gap in the table. + if (Offset < ContributionHeader) { + OS << format("0x%8.8x: Gap, length = ", Offset); + OS << (ContributionHeader - Offset) << "\n"; + } + OS << format("0x%8.8x: ", (uint32_t)ContributionHeader); + // 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"; + + Offset = Contribution->Base; + unsigned EntrySize = Contribution->getDwarfOffsetByteSize(); + while (Offset - Contribution->Base < Contribution->Size) { + OS << format("0x%8.8x: ", Offset); + // FIXME: We can only extract strings if the offset fits in 32 bits. + uint64_t StringOffset = + StrOffsetExt.getRelocatedValue(EntrySize, &Offset); + // Extract the string if we can and display it. Otherwise just report + // the offset. + if (StringOffset <= std::numeric_limits<uint32_t>::max()) { + uint32_t StringOffset32 = (uint32_t)StringOffset; + OS << format("%8.8x ", StringOffset32); + const char *S = StrData.getCStr(&StringOffset32); + if (S) + OS << format("\"%s\"", S); + } else + OS << format("%16.16" PRIx64 " ", StringOffset); + OS << "\n"; + } + } + // Report a gap at the end of the table. + if (Offset < SectionSize) { + OS << format("0x%8.8x: Gap, length = ", Offset); + OS << (SectionSize - Offset) << "\n"; + } +} + +// Dump a DWARF string offsets section. This may be a DWARF v5 formatted +// string offsets section, where each compile or type unit contributes a +// number of entries (string offsets), with each contribution preceded by +// a header containing size and version number. Alternatively, it may be a +// monolithic series of string offsets, as generated by the pre-DWARF v5 +// implementation of split DWARF. +static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, + const DWARFObject &Obj, + const DWARFSection &StringOffsetsSection, + StringRef StringSection, + DWARFContext::unit_iterator_range Units, + bool LittleEndian, unsigned MaxVersion) { + // If we have at least one (compile or type) unit with DWARF v5 or greater, + // we assume that the section is formatted like a DWARF v5 string offsets + // section. + if (MaxVersion >= 5) + dumpDWARFv5StringOffsetsSection(OS, SectionName, Obj, StringOffsetsSection, + StringSection, Units, LittleEndian); + else { + DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0); + uint32_t offset = 0; + uint64_t size = StringOffsetsSection.Data.size(); + // Ensure that size is a multiple of the size of an entry. + if (size & ((uint64_t)(sizeof(uint32_t) - 1))) { + OS << "error: size of ." << SectionName << " is not a multiple of " + << sizeof(uint32_t) << ".\n"; + size &= -(uint64_t)sizeof(uint32_t); + } + DataExtractor StrData(StringSection, LittleEndian, 0); + while (offset < size) { + OS << format("0x%8.8x: ", offset); + uint32_t StringOffset = strOffsetExt.getU32(&offset); + OS << format("%8.8x ", StringOffset); + const char *S = StrData.getCStr(&StringOffset); + if (S) + OS << format("\"%s\"", S); + OS << "\n"; + } + } +} + +// Dump the .debug_addr section. +static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, + DIDumpOptions DumpOpts, uint16_t Version, + uint8_t AddrSize) { + uint32_t Offset = 0; + while (AddrData.isValidOffset(Offset)) { + DWARFDebugAddrTable AddrTable; + uint32_t TableOffset = Offset; + if (Error Err = AddrTable.extract(AddrData, &Offset, Version, AddrSize, + DWARFContext::dumpWarning)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + // 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 (!AddrTable.hasValidLength()) + break; + uint64_t Length = AddrTable.getLength(); + Offset = TableOffset + Length; + } else { + AddrTable.dump(OS, DumpOpts); + } + } +} + +// Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). +static void +dumpRnglistsSection(raw_ostream &OS, DWARFDataExtractor &rnglistData, + llvm::function_ref<Optional<SectionedAddress>(uint32_t)> + LookupPooledAddress, + 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, LookupPooledAddress, DumpOpts); + } + } +} + +static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, + DWARFDataExtractor Data, + const MCRegisterInfo *MRI, + Optional<uint64_t> DumpOffset) { + uint32_t Offset = 0; + DWARFDebugLoclists Loclists; + + DWARFListTableHeader Header(".debug_loclists", "locations"); + if (Error E = Header.extract(Data, &Offset)) { + WithColor::error() << toString(std::move(E)) << '\n'; + return; + } + + Header.dump(OS, DumpOpts); + DataExtractor LocData(Data.getData().drop_front(Offset), + Data.isLittleEndian(), Header.getAddrSize()); + + Loclists.parse(LocData, Header.getVersion()); + Loclists.dump(OS, 0, MRI, DumpOffset); +} + +void DWARFContext::dump( + raw_ostream &OS, DIDumpOptions DumpOpts, + std::array<Optional<uint64_t>, DIDT_ID_Count> DumpOffsets) { + + uint64_t DumpType = DumpOpts.DumpType; + + StringRef Extension = sys::path::extension(DObj->getFileName()); + bool IsDWO = (Extension == ".dwo") || (Extension == ".dwp"); + + // Print UUID header. + const auto *ObjFile = DObj->getFile(); + if (DumpType & DIDT_UUID) + dumpUUID(OS, *ObjFile); + + // Print a header for each explicitly-requested section. + // Otherwise just print one for non-empty sections. + // Only print empty .dwo section headers when dumping a .dwo file. + bool Explicit = DumpType != DIDT_All && !IsDWO; + bool ExplicitDWO = Explicit && IsDWO; + auto shouldDump = [&](bool Explicit, const char *Name, unsigned ID, + StringRef Section) -> Optional<uint64_t> * { + unsigned Mask = 1U << ID; + bool Should = (DumpType & Mask) && (Explicit || !Section.empty()); + if (!Should) + return nullptr; + OS << "\n" << Name << " contents:\n"; + return &DumpOffsets[ID]; + }; + + // Dump individual sections. + if (shouldDump(Explicit, ".debug_abbrev", DIDT_ID_DebugAbbrev, + DObj->getAbbrevSection())) + getDebugAbbrev()->dump(OS); + if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_ID_DebugAbbrev, + DObj->getAbbrevDWOSection())) + getDebugAbbrevDWO()->dump(OS); + + auto dumpDebugInfo = [&](const char *Name, unit_iterator_range Units) { + OS << '\n' << Name << " contents:\n"; + if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugInfo]) + for (const auto &U : Units) + U->getDIEForOffset(DumpOffset.getValue()) + .dump(OS, 0, DumpOpts.noImplicitRecursion()); + else + for (const auto &U : Units) + U->dump(OS, DumpOpts); + }; + if ((DumpType & DIDT_DebugInfo)) { + if (Explicit || getNumCompileUnits()) + dumpDebugInfo(".debug_info", info_section_units()); + if (ExplicitDWO || getNumDWOCompileUnits()) + dumpDebugInfo(".debug_info.dwo", dwo_info_section_units()); + } + + auto dumpDebugType = [&](const char *Name, unit_iterator_range Units) { + OS << '\n' << Name << " contents:\n"; + for (const auto &U : Units) + if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugTypes]) + U->getDIEForOffset(*DumpOffset) + .dump(OS, 0, DumpOpts.noImplicitRecursion()); + else + U->dump(OS, DumpOpts); + }; + if ((DumpType & DIDT_DebugTypes)) { + if (Explicit || getNumTypeUnits()) + dumpDebugType(".debug_types", types_section_units()); + if (ExplicitDWO || getNumDWOTypeUnits()) + dumpDebugType(".debug_types.dwo", dwo_types_section_units()); + } + + if (const auto *Off = shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, + DObj->getLocSection().Data)) { + getDebugLoc()->dump(OS, getRegisterInfo(), *Off); + } + if (const auto *Off = + shouldDump(Explicit, ".debug_loclists", DIDT_ID_DebugLoclists, + DObj->getLoclistsSection().Data)) { + DWARFDataExtractor Data(*DObj, DObj->getLoclistsSection(), isLittleEndian(), + 0); + dumpLoclistsSection(OS, DumpOpts, Data, getRegisterInfo(), *Off); + } + if (const auto *Off = + shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, + DObj->getLocDWOSection().Data)) { + getDebugLocDWO()->dump(OS, 0, getRegisterInfo(), *Off); + } + + if (const auto *Off = shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, + DObj->getDebugFrameSection())) + getDebugFrame()->dump(OS, getRegisterInfo(), *Off); + + if (const auto *Off = shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, + DObj->getEHFrameSection())) + getEHFrame()->dump(OS, getRegisterInfo(), *Off); + + if (DumpType & DIDT_DebugMacro) { + if (Explicit || !getDebugMacro()->empty()) { + OS << "\n.debug_macinfo contents:\n"; + getDebugMacro()->dump(OS); + } + } + + if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, + DObj->getARangeSection())) { + uint32_t offset = 0; + DataExtractor arangesData(DObj->getARangeSection(), isLittleEndian(), 0); + DWARFDebugArangeSet set; + while (set.extract(arangesData, &offset)) + set.dump(OS); + } + + auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser, + DIDumpOptions DumpOpts, + Optional<uint64_t> DumpOffset) { + while (!Parser.done()) { + if (DumpOffset && Parser.getOffset() != *DumpOffset) { + Parser.skip(dumpWarning); + continue; + } + OS << "debug_line[" << format("0x%8.8x", Parser.getOffset()) << "]\n"; + if (DumpOpts.Verbose) { + Parser.parseNext(dumpWarning, dumpWarning, &OS); + } else { + DWARFDebugLine::LineTable LineTable = + Parser.parseNext(dumpWarning, dumpWarning); + LineTable.dump(OS, DumpOpts); + } + } + }; + + if (const auto *Off = 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_units()); + DumpLineSection(Parser, DumpOpts, *Off); + } + + if (const auto *Off = + shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, + DObj->getLineDWOSection().Data)) { + DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(), + isLittleEndian(), 0); + DWARFDebugLine::SectionParser Parser(LineData, *this, dwo_compile_units(), + dwo_type_units()); + DumpLineSection(Parser, DumpOpts, *Off); + } + + if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, + DObj->getCUIndexSection())) { + getCUIndex().dump(OS); + } + + if (shouldDump(Explicit, ".debug_tu_index", DIDT_ID_DebugTUIndex, + DObj->getTUIndexSection())) { + getTUIndex().dump(OS); + } + + if (shouldDump(Explicit, ".debug_str", DIDT_ID_DebugStr, + DObj->getStringSection())) { + DataExtractor strData(DObj->getStringSection(), isLittleEndian(), 0); + uint32_t offset = 0; + uint32_t strOffset = 0; + while (const char *s = strData.getCStr(&offset)) { + OS << format("0x%8.8x: \"%s\"\n", strOffset, s); + strOffset = offset; + } + } + if (shouldDump(ExplicitDWO, ".debug_str.dwo", DIDT_ID_DebugStr, + DObj->getStringDWOSection())) { + DataExtractor strDWOData(DObj->getStringDWOSection(), isLittleEndian(), 0); + uint32_t offset = 0; + uint32_t strDWOOffset = 0; + while (const char *s = strDWOData.getCStr(&offset)) { + OS << format("0x%8.8x: \"%s\"\n", strDWOOffset, s); + 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_addr", DIDT_ID_DebugAddr, + DObj->getAddrSection().Data)) { + DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(), + isLittleEndian(), 0); + dumpAddrSection(OS, AddrData, DumpOpts, getMaxVersion(), getCUAddrSize()); + } + + if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, + DObj->getRangeSection().Data)) { + uint8_t savedAddressByteSize = getCUAddrSize(); + DWARFDataExtractor rangesData(*DObj, DObj->getRangeSection(), + isLittleEndian(), savedAddressByteSize); + uint32_t offset = 0; + DWARFDebugRangeList rangeList; + while (rangesData.isValidOffset(offset)) { + if (Error E = rangeList.extract(rangesData, &offset)) { + WithColor::error() << toString(std::move(E)) << '\n'; + break; + } + rangeList.dump(OS); + } + } + + auto LookupPooledAddress = [&](uint32_t Index) -> Optional<SectionedAddress> { + const auto &CUs = compile_units(); + auto I = CUs.begin(); + if (I == CUs.end()) + return None; + return (*I)->getAddrOffsetSectionItem(Index); + }; + + if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, + DObj->getRnglistsSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); + } + + if (shouldDump(ExplicitDWO, ".debug_rnglists.dwo", DIDT_ID_DebugRnglists, + DObj->getRnglistsDWOSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsDWOSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); + } + + if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, + DObj->getPubNamesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getPubNamesSection(), isLittleEndian(), false) + .dump(OS); + + if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, + DObj->getPubTypesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getPubTypesSection(), isLittleEndian(), false) + .dump(OS); + + if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, + DObj->getGnuPubNamesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getGnuPubNamesSection(), isLittleEndian(), + true /* GnuStyle */) + .dump(OS); + + if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, + DObj->getGnuPubTypesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getGnuPubTypesSection(), isLittleEndian(), + true /* GnuStyle */) + .dump(OS); + + if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, + DObj->getStringOffsetSection().Data)) + dumpStringOffsetsSection(OS, "debug_str_offsets", *DObj, + DObj->getStringOffsetSection(), + DObj->getStringSection(), normal_units(), + isLittleEndian(), getMaxVersion()); + if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_ID_DebugStrOffsets, + DObj->getStringOffsetDWOSection().Data)) + dumpStringOffsetsSection(OS, "debug_str_offsets.dwo", *DObj, + DObj->getStringOffsetDWOSection(), + DObj->getStringDWOSection(), dwo_units(), + isLittleEndian(), getMaxDWOVersion()); + + if (shouldDump(Explicit, ".gdb_index", DIDT_ID_GdbIndex, + DObj->getGdbIndexSection())) { + getGdbIndex().dump(OS); + } + + if (shouldDump(Explicit, ".apple_names", DIDT_ID_AppleNames, + DObj->getAppleNamesSection().Data)) + getAppleNames().dump(OS); + + if (shouldDump(Explicit, ".apple_types", DIDT_ID_AppleTypes, + DObj->getAppleTypesSection().Data)) + getAppleTypes().dump(OS); + + if (shouldDump(Explicit, ".apple_namespaces", DIDT_ID_AppleNamespaces, + DObj->getAppleNamespacesSection().Data)) + getAppleNamespaces().dump(OS); + + 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) { + parseDWOUnits(LazyParse); + + if (const auto &CUI = getCUIndex()) { + if (const auto *R = CUI.getFromHash(Hash)) + return dyn_cast_or_null<DWARFCompileUnit>( + DWOUnits.getUnitForIndexEntry(*R)); + return nullptr; + } + + // If there's no index, just search through the CUs in the DWO - there's + // 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()) { + // 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 dyn_cast<DWARFCompileUnit>(DWOCU.get()); + } + return nullptr; +} + +DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) { + parseNormalUnits(); + if (auto *CU = NormalUnits.getUnitForOffset(Offset)) + return CU->getDIEForOffset(Offset); + return DWARFDie(); +} + +bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { + bool Success = true; + DWARFVerifier verifier(OS, *this, DumpOpts); + + Success &= verifier.handleDebugAbbrev(); + if (DumpOpts.DumpType & DIDT_DebugInfo) + Success &= verifier.handleDebugInfo(); + if (DumpOpts.DumpType & DIDT_DebugLine) + Success &= verifier.handleDebugLine(); + Success &= verifier.handleAccelTables(); + return Success; +} + +const DWARFUnitIndex &DWARFContext::getCUIndex() { + if (CUIndex) + return *CUIndex; + + DataExtractor CUIndexData(DObj->getCUIndexSection(), isLittleEndian(), 0); + + CUIndex = llvm::make_unique<DWARFUnitIndex>(DW_SECT_INFO); + CUIndex->parse(CUIndexData); + return *CUIndex; +} + +const DWARFUnitIndex &DWARFContext::getTUIndex() { + if (TUIndex) + return *TUIndex; + + DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); + + TUIndex = llvm::make_unique<DWARFUnitIndex>(DW_SECT_TYPES); + TUIndex->parse(TUIndexData); + return *TUIndex; +} + +DWARFGdbIndex &DWARFContext::getGdbIndex() { + if (GdbIndex) + return *GdbIndex; + + DataExtractor GdbIndexData(DObj->getGdbIndexSection(), true /*LE*/, 0); + GdbIndex = llvm::make_unique<DWARFGdbIndex>(); + GdbIndex->parse(GdbIndexData); + return *GdbIndex; +} + +const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { + if (Abbrev) + return Abbrev.get(); + + DataExtractor abbrData(DObj->getAbbrevSection(), isLittleEndian(), 0); + + Abbrev.reset(new DWARFDebugAbbrev()); + Abbrev->extract(abbrData); + return Abbrev.get(); +} + +const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { + if (AbbrevDWO) + return AbbrevDWO.get(); + + DataExtractor abbrData(DObj->getAbbrevDWOSection(), isLittleEndian(), 0); + AbbrevDWO.reset(new DWARFDebugAbbrev()); + AbbrevDWO->extract(abbrData); + return AbbrevDWO.get(); +} + +const DWARFDebugLoc *DWARFContext::getDebugLoc() { + if (Loc) + return Loc.get(); + + Loc.reset(new DWARFDebugLoc); + // Assume all units have the same address byte size. + if (getNumCompileUnits()) { + DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), + getUnitAtIndex(0)->getAddressByteSize()); + Loc->parse(LocData); + } + return Loc.get(); +} + +const DWARFDebugLoclists *DWARFContext::getDebugLocDWO() { + if (LocDWO) + return LocDWO.get(); + + LocDWO.reset(new DWARFDebugLoclists()); + // Assume all compile units have the same address byte size. + // FIXME: We don't need AddressSize for split DWARF since relocatable + // addresses cannot appear there. At the moment DWARFExpression requires it. + DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), 4); + // Use version 4. DWO does not support the DWARF v5 .debug_loclists yet and + // that means we are parsing the new style .debug_loc (pre-standatized version + // of the .debug_loclists). + LocDWO->parse(LocData, 4 /* Version */); + return LocDWO.get(); +} + +const DWARFDebugAranges *DWARFContext::getDebugAranges() { + if (Aranges) + return Aranges.get(); + + Aranges.reset(new DWARFDebugAranges()); + Aranges->generate(this); + return Aranges.get(); +} + +const DWARFDebugFrame *DWARFContext::getDebugFrame() { + if (DebugFrame) + return DebugFrame.get(); + + // There's a "bug" in the DWARFv3 standard with respect to the target address + // size within debug frame sections. While DWARF is supposed to be independent + // of its container, FDEs have fields with size being "target address size", + // which isn't specified in DWARF in general. It's only specified for CUs, but + // .eh_frame can appear without a .debug_info section. Follow the example of + // other tools (libdwarf) and extract this from the container (ObjectFile + // 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 + DWARFDataExtractor debugFrameData(DObj->getDebugFrameSection(), + isLittleEndian(), DObj->getAddressSize()); + DebugFrame.reset(new DWARFDebugFrame(getArch(), false /* IsEH */)); + DebugFrame->parse(debugFrameData); + return DebugFrame.get(); +} + +const DWARFDebugFrame *DWARFContext::getEHFrame() { + if (EHFrame) + return EHFrame.get(); + + DWARFDataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(), + DObj->getAddressSize()); + DebugFrame.reset(new DWARFDebugFrame(getArch(), true /* IsEH */)); + DebugFrame->parse(debugFrameData); + return DebugFrame.get(); +} + +const DWARFDebugMacro *DWARFContext::getDebugMacro() { + if (Macro) + return Macro.get(); + + DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); + Macro.reset(new DWARFDebugMacro()); + Macro->parse(MacinfoData); + return Macro.get(); +} + +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 T(AccelSection, StrData)); + if (Error E = Cache->extract()) + llvm::consumeError(std::move(E)); + return *Cache; +} + +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 AppleAcceleratorTable &DWARFContext::getAppleTypes() { + return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleNamespaces() { + return getAccelTable(AppleNamespaces, *DObj, + DObj->getAppleNamespacesSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleObjC() { + return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const DWARFDebugLine::LineTable * +DWARFContext::getLineTableForUnit(DWARFUnit *U) { + Expected<const DWARFDebugLine::LineTable *> ExpectedLineTable = + getLineTableForUnit(U, dumpWarning); + if (!ExpectedLineTable) { + dumpWarning(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); + + auto UnitDIE = U->getUnitDIE(); + if (!UnitDIE) + return nullptr; + + auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); + if (!Offset) + return nullptr; // No line table for this compile unit. + + uint32_t stmtOffset = *Offset + U->getLineTableOffset(); + // See if the line table is cached. + if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) + return lt; + + // Make sure the offset is good before we try to parse. + if (stmtOffset >= U->getLineSection().Data.size()) + return nullptr; + + // We have to parse it first. + DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), + U->getAddressByteSize()); + return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, + RecoverableErrorCallback); +} + +void DWARFContext::parseNormalUnits() { + if (!NormalUnits.empty()) + return; + DObj->forEachInfoSections([&](const DWARFSection &S) { + NormalUnits.addUnitsForSection(*this, S, DW_SECT_INFO); + }); + NormalUnits.finishedInfoUnits(); + DObj->forEachTypesSections([&](const DWARFSection &S) { + NormalUnits.addUnitsForSection(*this, S, DW_SECT_TYPES); + }); +} + +void DWARFContext::parseDWOUnits(bool Lazy) { + if (!DWOUnits.empty()) + return; + DObj->forEachInfoDWOSections([&](const DWARFSection &S) { + DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_INFO, Lazy); + }); + DWOUnits.finishedInfoUnits(); + DObj->forEachTypesDWOSections([&](const DWARFSection &S) { + DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_TYPES, Lazy); + }); +} + +DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint32_t Offset) { + parseNormalUnits(); + return dyn_cast_or_null<DWARFCompileUnit>( + NormalUnits.getUnitForOffset(Offset)); +} + +DWARFCompileUnit *DWARFContext::getCompileUnitForAddress(uint64_t Address) { + // First, get the offset of the compile unit. + uint32_t CUOffset = getDebugAranges()->findAddress(Address); + // Retrieve the compile unit. + return getCompileUnitForOffset(CUOffset); +} + +DWARFContext::DIEsForAddress DWARFContext::getDIEsForAddress(uint64_t Address) { + DIEsForAddress Result; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return Result; + + Result.CompileUnit = CU; + Result.FunctionDIE = CU->getSubroutineForAddress(Address); + + std::vector<DWARFDie> Worklist; + Worklist.push_back(Result.FunctionDIE); + while (!Worklist.empty()) { + DWARFDie DIE = Worklist.back(); + Worklist.pop_back(); + + if (DIE.getTag() == DW_TAG_lexical_block && + DIE.addressRangeContainsAddress(Address)) { + Result.BlockDIE = DIE; + break; + } + + for (auto Child : DIE) + Worklist.push_back(Child); + } + + return Result; +} + +static bool getFunctionNameAndStartLineForAddress(DWARFCompileUnit *CU, + uint64_t Address, + FunctionNameKind Kind, + std::string &FunctionName, + uint32_t &StartLine) { + // The address may correspond to instruction in some inlined function, + // so we have to build the chain of inlined functions and take the + // name of the topmost function in it. + SmallVector<DWARFDie, 4> InlinedChain; + CU->getInlinedChainForAddress(Address, InlinedChain); + if (InlinedChain.empty()) + return false; + + const DWARFDie &DIE = InlinedChain[0]; + bool FoundResult = false; + const char *Name = nullptr; + if (Kind != FunctionNameKind::None && (Name = DIE.getSubroutineName(Kind))) { + FunctionName = Name; + FoundResult = true; + } + if (auto DeclLineResult = DIE.getDeclLine()) { + StartLine = DeclLineResult; + FoundResult = true; + } + + return FoundResult; +} + +DILineInfo DWARFContext::getLineInfoForAddress(uint64_t Address, + DILineInfoSpecifier Spec) { + DILineInfo Result; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return Result; + getFunctionNameAndStartLineForAddress(CU, Address, Spec.FNKind, + Result.FunctionName, + Result.StartLine); + if (Spec.FLIKind != FileLineInfoKind::None) { + if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) + LineTable->getFileLineInfoForAddress(Address, CU->getCompilationDir(), + Spec.FLIKind, Result); + } + return Result; +} + +DILineInfoTable +DWARFContext::getLineInfoForAddressRange(uint64_t Address, uint64_t Size, + DILineInfoSpecifier Spec) { + DILineInfoTable Lines; + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return Lines; + + std::string FunctionName = "<invalid>"; + uint32_t StartLine = 0; + getFunctionNameAndStartLineForAddress(CU, Address, Spec.FNKind, FunctionName, + StartLine); + + // If the Specifier says we don't need FileLineInfo, just + // return the top-most function at the starting address. + if (Spec.FLIKind == FileLineInfoKind::None) { + DILineInfo Result; + Result.FunctionName = FunctionName; + Result.StartLine = StartLine; + Lines.push_back(std::make_pair(Address, Result)); + return Lines; + } + + const DWARFLineTable *LineTable = getLineTableForUnit(CU); + + // Get the index of row we're looking for in the line table. + std::vector<uint32_t> RowVector; + if (!LineTable->lookupAddressRange(Address, Size, RowVector)) + return Lines; + + for (uint32_t RowIndex : RowVector) { + // Take file number and line/column from the row. + const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex]; + DILineInfo Result; + LineTable->getFileNameByIndex(Row.File, CU->getCompilationDir(), + Spec.FLIKind, Result.FileName); + Result.FunctionName = FunctionName; + Result.Line = Row.Line; + Result.Column = Row.Column; + Result.StartLine = StartLine; + Lines.push_back(std::make_pair(Row.Address, Result)); + } + + return Lines; +} + +DIInliningInfo +DWARFContext::getInliningInfoForAddress(uint64_t Address, + DILineInfoSpecifier Spec) { + DIInliningInfo InliningInfo; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return InliningInfo; + + const DWARFLineTable *LineTable = nullptr; + SmallVector<DWARFDie, 4> InlinedChain; + CU->getInlinedChainForAddress(Address, InlinedChain); + if (InlinedChain.size() == 0) { + // If there is no DIE for address (e.g. it is in unavailable .dwo file), + // try to at least get file/line info from symbol table. + if (Spec.FLIKind != FileLineInfoKind::None) { + DILineInfo Frame; + LineTable = getLineTableForUnit(CU); + if (LineTable && + LineTable->getFileLineInfoForAddress(Address, CU->getCompilationDir(), + Spec.FLIKind, Frame)) + InliningInfo.addFrame(Frame); + } + return InliningInfo; + } + + uint32_t CallFile = 0, CallLine = 0, CallColumn = 0, CallDiscriminator = 0; + for (uint32_t i = 0, n = InlinedChain.size(); i != n; i++) { + DWARFDie &FunctionDIE = InlinedChain[i]; + DILineInfo Frame; + // Get function name if necessary. + if (const char *Name = FunctionDIE.getSubroutineName(Spec.FNKind)) + Frame.FunctionName = Name; + if (auto DeclLineResult = FunctionDIE.getDeclLine()) + Frame.StartLine = DeclLineResult; + if (Spec.FLIKind != FileLineInfoKind::None) { + if (i == 0) { + // For the topmost frame, initialize the line table of this + // compile unit and fetch file/line info from it. + LineTable = getLineTableForUnit(CU); + // For the topmost routine, get file/line info from line table. + if (LineTable) + LineTable->getFileLineInfoForAddress(Address, CU->getCompilationDir(), + Spec.FLIKind, Frame); + } else { + // Otherwise, use call file, call line and call column from + // previous DIE in inlined chain. + if (LineTable) + LineTable->getFileNameByIndex(CallFile, CU->getCompilationDir(), + Spec.FLIKind, Frame.FileName); + Frame.Line = CallLine; + Frame.Column = CallColumn; + Frame.Discriminator = CallDiscriminator; + } + // Get call file/line/column of a current DIE. + if (i + 1 < n) { + FunctionDIE.getCallerFrame(CallFile, CallLine, CallColumn, + CallDiscriminator); + } + } + InliningInfo.addFrame(Frame); + } + return InliningInfo; +} + +std::shared_ptr<DWARFContext> +DWARFContext::getDWOContext(StringRef AbsolutePath) { + if (auto S = DWP.lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + std::weak_ptr<DWOFile> *Entry = &DWOFiles[AbsolutePath]; + + if (auto S = Entry->lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + Expected<OwningBinary<ObjectFile>> Obj = [&] { + if (!CheckedForDWP) { + SmallString<128> DWPName; + auto Obj = object::ObjectFile::createObjectFile( + this->DWPName.empty() + ? (DObj->getFileName() + ".dwp").toStringRef(DWPName) + : StringRef(this->DWPName)); + if (Obj) { + Entry = &DWP; + return Obj; + } else { + CheckedForDWP = true; + // TODO: Should this error be handled (maybe in a high verbosity mode) + // before falling back to .dwo files? + consumeError(Obj.takeError()); + } + } + + return object::ObjectFile::createObjectFile(AbsolutePath); + }(); + + if (!Obj) { + // TODO: Actually report errors helpfully. + consumeError(Obj.takeError()); + return nullptr; + } + + auto S = std::make_shared<DWOFile>(); + S->File = std::move(Obj.get()); + S->Context = DWARFContext::create(*S->File.getBinary()); + *Entry = S; + auto *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); +} + +static Error createError(const Twine &Reason, llvm::Error E) { + return make_error<StringError>(Reason + toString(std::move(E)), + inconvertibleErrorCode()); +} + +/// SymInfo contains information about symbol: it's address +/// and section index which is -1LL for absolute symbols. +struct SymInfo { + uint64_t Address; + uint64_t SectionIndex; +}; + +/// Returns the address of symbol relocation used against and a section index. +/// Used for futher relocations computation. Symbol's section load address is +static Expected<SymInfo> getSymbolInfo(const object::ObjectFile &Obj, + const RelocationRef &Reloc, + const LoadedObjectInfo *L, + std::map<SymbolRef, SymInfo> &Cache) { + SymInfo Ret = {0, (uint64_t)-1LL}; + object::section_iterator RSec = Obj.section_end(); + object::symbol_iterator Sym = Reloc.getSymbol(); + + std::map<SymbolRef, SymInfo>::iterator CacheIt = Cache.end(); + // First calculate the address of the symbol or section as it appears + // in the object file + if (Sym != Obj.symbol_end()) { + bool New; + std::tie(CacheIt, New) = Cache.insert({*Sym, {0, 0}}); + if (!New) + return CacheIt->second; + + Expected<uint64_t> SymAddrOrErr = Sym->getAddress(); + if (!SymAddrOrErr) + return createError("failed to compute symbol address: ", + SymAddrOrErr.takeError()); + + // Also remember what section this symbol is in for later + auto SectOrErr = Sym->getSection(); + if (!SectOrErr) + return createError("failed to get symbol section: ", + SectOrErr.takeError()); + + RSec = *SectOrErr; + Ret.Address = *SymAddrOrErr; + } else if (auto *MObj = dyn_cast<MachOObjectFile>(&Obj)) { + RSec = MObj->getRelocationSection(Reloc.getRawDataRefImpl()); + Ret.Address = RSec->getAddress(); + } + + if (RSec != Obj.section_end()) + Ret.SectionIndex = RSec->getIndex(); + + // If we are given load addresses for the sections, we need to adjust: + // SymAddr = (Address of Symbol Or Section in File) - + // (Address of Section in File) + + // (Load Address of Section) + // RSec is now either the section being targeted or the section + // containing the symbol being targeted. In either case, + // we need to perform the same computation. + if (L && RSec != Obj.section_end()) + if (uint64_t SectionLoadAddress = L->getSectionLoadAddress(*RSec)) + Ret.Address += SectionLoadAddress - RSec->getAddress(); + + if (CacheIt != Cache.end()) + CacheIt->second = Ret; + + return Ret; +} + +static bool isRelocScattered(const object::ObjectFile &Obj, + const RelocationRef &Reloc) { + const MachOObjectFile *MachObj = dyn_cast<MachOObjectFile>(&Obj); + if (!MachObj) + return false; + // MachO also has relocations that point to sections and + // scattered relocations. + auto RelocInfo = MachObj->getRelocation(Reloc.getRawDataRefImpl()); + return MachObj->isRelocationScattered(RelocInfo); +} + +ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { + WithColor::error() << toString(std::move(E)) << '\n'; + return ErrorPolicy::Continue; +} + +namespace { +struct DWARFSectionMap final : public DWARFSection { + RelocAddrMap Relocs; +}; + +class DWARFObjInMemory final : public DWARFObject { + bool IsLittleEndian; + uint8_t AddressSize; + StringRef FileName; + const object::ObjectFile *Obj = nullptr; + std::vector<SectionName> SectionNames; + + using InfoSectionMap = MapVector<object::SectionRef, DWARFSectionMap, + std::map<object::SectionRef, unsigned>>; + + InfoSectionMap InfoSections; + InfoSectionMap TypesSections; + InfoSectionMap InfoDWOSections; + InfoSectionMap TypesDWOSections; + + DWARFSectionMap LocSection; + DWARFSectionMap LocListsSection; + DWARFSectionMap LineSection; + DWARFSectionMap RangeSection; + DWARFSectionMap RnglistsSection; + DWARFSectionMap StringOffsetSection; + DWARFSectionMap LineDWOSection; + DWARFSectionMap LocDWOSection; + DWARFSectionMap StringOffsetDWOSection; + DWARFSectionMap RangeDWOSection; + DWARFSectionMap RnglistsDWOSection; + DWARFSectionMap AddrSection; + DWARFSectionMap AppleNamesSection; + DWARFSectionMap AppleTypesSection; + DWARFSectionMap AppleNamespacesSection; + DWARFSectionMap AppleObjCSection; + DWARFSectionMap DebugNamesSection; + DWARFSectionMap PubNamesSection; + DWARFSectionMap PubTypesSection; + DWARFSectionMap GnuPubNamesSection; + DWARFSectionMap GnuPubTypesSection; + + DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { + return StringSwitch<DWARFSectionMap *>(Name) + .Case("debug_loc", &LocSection) + .Case("debug_loclists", &LocListsSection) + .Case("debug_line", &LineSection) + .Case("debug_str_offsets", &StringOffsetSection) + .Case("debug_ranges", &RangeSection) + .Case("debug_rnglists", &RnglistsSection) + .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) + .Case("debug_pubnames", &PubNamesSection) + .Case("debug_pubtypes", &PubTypesSection) + .Case("debug_gnu_pubnames", &GnuPubNamesSection) + .Case("debug_gnu_pubtypes", &GnuPubTypesSection) + .Case("apple_types", &AppleTypesSection) + .Case("apple_namespaces", &AppleNamespacesSection) + .Case("apple_namespac", &AppleNamespacesSection) + .Case("apple_objc", &AppleObjCSection) + .Default(nullptr); + } + + StringRef AbbrevSection; + StringRef ARangeSection; + StringRef DebugFrameSection; + StringRef EHFrameSection; + StringRef StringSection; + StringRef MacinfoSection; + StringRef AbbrevDWOSection; + StringRef StringDWOSection; + StringRef CUIndexSection; + StringRef GdbIndexSection; + StringRef TUIndexSection; + StringRef LineStringSection; + + // 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)) + return &Sec->Data; + return StringSwitch<StringRef *>(Name) + .Case("debug_abbrev", &AbbrevSection) + .Case("debug_aranges", &ARangeSection) + .Case("debug_frame", &DebugFrameSection) + .Case("eh_frame", &EHFrameSection) + .Case("debug_str", &StringSection) + .Case("debug_macinfo", &MacinfoSection) + .Case("debug_abbrev.dwo", &AbbrevDWOSection) + .Case("debug_str.dwo", &StringDWOSection) + .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); + } + + /// If Sec is compressed section, decompresses and updates its contents + /// provided by Data. Otherwise leaves it unchanged. + Error maybeDecompress(const object::SectionRef &Sec, StringRef Name, + StringRef &Data) { + if (!Decompressor::isCompressed(Sec)) + return Error::success(); + + Expected<Decompressor> Decompressor = + Decompressor::create(Name, Data, IsLittleEndian, AddressSize == 8); + if (!Decompressor) + return Decompressor.takeError(); + + SmallString<0> Out; + if (auto Err = Decompressor->resizeAndDecompress(Out)) + return Err; + + UncompressedSections.push_back(std::move(Out)); + Data = UncompressedSections.back(); + + return Error::success(); + } + +public: + DWARFObjInMemory(const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, + uint8_t AddrSize, bool IsLittleEndian) + : IsLittleEndian(IsLittleEndian) { + for (const auto &SecIt : Sections) { + if (StringRef *SectionData = mapSectionToMember(SecIt.first())) + *SectionData = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_info") + // Find debug_info and debug_types data by section rather than name as + // there are multiple, comdat grouped, of these sections. + InfoSections[SectionRef()].Data = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_info.dwo") + InfoDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_types") + TypesSections[SectionRef()].Data = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_types.dwo") + TypesDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); + } + } + DWARFObjInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L, + function_ref<ErrorPolicy(Error)> HandleError) + : IsLittleEndian(Obj.isLittleEndian()), + AddressSize(Obj.getBytesInAddress()), FileName(Obj.getFileName()), + Obj(&Obj) { + + StringMap<unsigned> SectionAmountMap; + for (const SectionRef &Section : Obj.sections()) { + StringRef Name; + Section.getName(Name); + ++SectionAmountMap[Name]; + SectionNames.push_back({ Name, true }); + + // Skip BSS and Virtual sections, they aren't interesting. + if (Section.isBSS() || Section.isVirtual()) + continue; + + // Skip sections stripped by dsymutil. + if (Section.isStripped()) + continue; + + StringRef Data; + section_iterator RelocatedSection = Section.getRelocatedSection(); + // Try to obtain an already relocated version of this section. + // Else use the unrelocated section from the object file. We'll have to + // apply relocations ourselves later. + if (!L || !L->getLoadedSectionContents(*RelocatedSection, Data)) + Section.getContents(Data); + + if (auto Err = maybeDecompress(Section, Name, Data)) { + ErrorPolicy EP = HandleError(createError( + "failed to decompress '" + Name + "', ", std::move(Err))); + if (EP == ErrorPolicy::Halt) + return; + continue; + } + + // Compressed sections names in GNU style starts from ".z", + // at this point section is decompressed and we drop compression prefix. + Name = Name.substr( + Name.find_first_not_of("._z")); // Skip ".", "z" and "_" prefixes. + + // Map platform specific debug section names to DWARF standard section + // names. + Name = Obj.mapDebugSectionName(Name); + + if (StringRef *SectionData = mapSectionToMember(Name)) { + *SectionData = Data; + if (Name == "debug_ranges") { + // FIXME: Use the other dwo range section when we emit it. + RangeDWOSection.Data = Data; + } + } else if (Name == "debug_info") { + // Find debug_info and debug_types data by section rather than name as + // there are multiple, comdat grouped, of these sections. + InfoSections[Section].Data = Data; + } else if (Name == "debug_info.dwo") { + InfoDWOSections[Section].Data = Data; + } else if (Name == "debug_types") { + TypesSections[Section].Data = Data; + } else if (Name == "debug_types.dwo") { + TypesDWOSections[Section].Data = Data; + } + + if (RelocatedSection == Obj.section_end()) + continue; + + StringRef RelSecName; + StringRef RelSecData; + RelocatedSection->getName(RelSecName); + + // If the section we're relocating was relocated already by the JIT, + // then we used the relocated version above, so we do not need to process + // relocations for it now. + if (L && L->getLoadedSectionContents(*RelocatedSection, RelSecData)) + continue; + + // In Mach-o files, the relocations do not need to be applied if + // there is no load offset to apply. The value read at the + // relocation point already factors in the section address + // (actually applying the relocations will produce wrong results + // as the section address will be added twice). + if (!L && isa<MachOObjectFile>(&Obj)) + continue; + + RelSecName = RelSecName.substr( + RelSecName.find_first_not_of("._z")); // Skip . and _ prefixes. + + // TODO: Add support for relocations in other sections as needed. + // Record relocations for the debug_info and debug_line sections. + DWARFSectionMap *Sec = mapNameToDWARFSection(RelSecName); + RelocAddrMap *Map = Sec ? &Sec->Relocs : nullptr; + if (!Map) { + // Find debug_info and debug_types relocs by section rather than name + // as there are multiple, comdat grouped, of these sections. + if (RelSecName == "debug_info") + Map = &static_cast<DWARFSectionMap &>(InfoSections[*RelocatedSection]) + .Relocs; + else if (RelSecName == "debug_info.dwo") + Map = &static_cast<DWARFSectionMap &>( + InfoDWOSections[*RelocatedSection]) + .Relocs; + else if (RelSecName == "debug_types") + Map = + &static_cast<DWARFSectionMap &>(TypesSections[*RelocatedSection]) + .Relocs; + else if (RelSecName == "debug_types.dwo") + Map = &static_cast<DWARFSectionMap &>( + TypesDWOSections[*RelocatedSection]) + .Relocs; + else + continue; + } + + if (Section.relocation_begin() == Section.relocation_end()) + continue; + + // Symbol to [address, section index] cache mapping. + std::map<SymbolRef, SymInfo> AddrCache; + for (const RelocationRef &Reloc : Section.relocations()) { + // FIXME: it's not clear how to correctly handle scattered + // relocations. + if (isRelocScattered(Obj, Reloc)) + continue; + + Expected<SymInfo> SymInfoOrErr = + getSymbolInfo(Obj, Reloc, L, AddrCache); + if (!SymInfoOrErr) { + if (HandleError(SymInfoOrErr.takeError()) == ErrorPolicy::Halt) + return; + continue; + } + + object::RelocVisitor V(Obj); + uint64_t Val = V.visit(Reloc.getType(), Reloc, SymInfoOrErr->Address); + if (V.error()) { + SmallString<32> Type; + Reloc.getTypeName(Type); + ErrorPolicy EP = HandleError( + createError("failed to compute relocation: " + Type + ", ", + errorCodeToError(object_error::parse_failed))); + if (EP == ErrorPolicy::Halt) + return; + continue; + } + RelocAddrEntry Rel = {SymInfoOrErr->SectionIndex, Val}; + Map->insert({Reloc.getOffset(), Rel}); + } + } + + for (SectionName &S : SectionNames) + if (SectionAmountMap[S.Name] > 1) + S.IsNameUnique = false; + } + + Optional<RelocAddrEntry> find(const DWARFSection &S, + uint64_t Pos) const override { + auto &Sec = static_cast<const DWARFSectionMap &>(S); + RelocAddrMap::const_iterator AI = Sec.Relocs.find(Pos); + if (AI == Sec.Relocs.end()) + return None; + return AI->second; + } + + const object::ObjectFile *getFile() const override { return Obj; } + + ArrayRef<SectionName> getSectionNames() const override { + return SectionNames; + } + + bool isLittleEndian() const override { return IsLittleEndian; } + StringRef getAbbrevDWOSection() const override { return AbbrevDWOSection; } + const DWARFSection &getLineDWOSection() const override { + return LineDWOSection; + } + const DWARFSection &getLocDWOSection() const override { + return LocDWOSection; + } + StringRef getStringDWOSection() const override { return StringDWOSection; } + const DWARFSection &getStringOffsetDWOSection() const override { + return StringOffsetDWOSection; + } + 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; } + StringRef getTUIndexSection() const override { return TUIndexSection; } + + // DWARF v5 + const DWARFSection &getStringOffsetSection() const override { + return StringOffsetSection; + } + StringRef getLineStringSection() const override { return LineStringSection; } + + // Sections for DWARF5 split dwarf proposal. + void forEachInfoDWOSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : InfoDWOSections) + F(P.second); + } + void forEachTypesDWOSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : TypesDWOSections) + F(P.second); + } + + StringRef getAbbrevSection() const override { return AbbrevSection; } + const DWARFSection &getLocSection() const override { return LocSection; } + const DWARFSection &getLoclistsSection() const override { return LocListsSection; } + StringRef getARangeSection() const override { return ARangeSection; } + StringRef getDebugFrameSection() const override { return DebugFrameSection; } + StringRef getEHFrameSection() const override { return EHFrameSection; } + 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; } + const DWARFSection &getPubNamesSection() const override { return PubNamesSection; } + const DWARFSection &getPubTypesSection() const override { return PubTypesSection; } + const DWARFSection &getGnuPubNamesSection() const override { + return GnuPubNamesSection; + } + const DWARFSection &getGnuPubTypesSection() const override { + return GnuPubTypesSection; + } + const DWARFSection &getAppleNamesSection() const override { + return AppleNamesSection; + } + const DWARFSection &getAppleTypesSection() const override { + return AppleTypesSection; + } + const DWARFSection &getAppleNamespacesSection() const override { + return AppleNamespacesSection; + } + 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; } + void forEachInfoSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : InfoSections) + F(P.second); + } + void forEachTypesSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : TypesSections) + F(P.second); + } +}; +} // namespace + +std::unique_ptr<DWARFContext> +DWARFContext::create(const object::ObjectFile &Obj, const LoadedObjectInfo *L, + function_ref<ErrorPolicy(Error)> HandleError, + std::string DWPName) { + auto DObj = llvm::make_unique<DWARFObjInMemory>(Obj, L, HandleError); + return llvm::make_unique<DWARFContext>(std::move(DObj), std::move(DWPName)); +} + +std::unique_ptr<DWARFContext> +DWARFContext::create(const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, + uint8_t AddrSize, bool isLittleEndian) { + auto DObj = + llvm::make_unique<DWARFObjInMemory>(Sections, AddrSize, isLittleEndian); + return llvm::make_unique<DWARFContext>(std::move(DObj), ""); +} + +Error DWARFContext::loadRegisterInfo(const object::ObjectFile &Obj) { + // Detect the architecture from the object file. We usually don't need OS + // info to lookup a target and create register info. + Triple TT; + TT.setArch(Triple::ArchType(Obj.getArch())); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::UnknownOS); + std::string TargetLookupError; + const Target *TheTarget = + TargetRegistry::lookupTarget(TT.str(), TargetLookupError); + if (!TargetLookupError.empty()) + return createStringError(errc::invalid_argument, + TargetLookupError.c_str()); + RegInfo.reset(TheTarget->createMCRegInfo(TT.str())); + return Error::success(); +} + +uint8_t DWARFContext::getCUAddrSize() { + // In theory, different compile units may have different address byte + // sizes, but for simplicity we just use the address byte size of the + // last compile unit. In practice the address size field is repeated across + // various DWARF headers (at least in version 5) to make it easier to dump + // them independently, not to enable varying the address size. + uint8_t Addr = 0; + for (const auto &CU : compile_units()) { + Addr = CU->getAddressByteSize(); + break; + } + return Addr; +} + +void DWARFContext::dumpWarning(Error Warning) { + handleAllErrors(std::move(Warning), [](ErrorInfoBase &Info) { + WithColor::warning() << Info.message() << '\n'; + }); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp new file mode 100644 index 000000000000..03e317461396 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -0,0 +1,96 @@ +//===- DWARFDataExtractor.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/DWARFDataExtractor.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" + +using namespace llvm; + +uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint32_t *Off, + uint64_t *SecNdx) const { + if (SecNdx) + *SecNdx = -1ULL; + if (!Section) + return getUnsigned(Off, Size); + Optional<RelocAddrEntry> Rel = Obj->find(*Section, *Off); + if (!Rel) + return getUnsigned(Off, Size); + if (SecNdx) + *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/DWARFDebugAbbrev.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 000000000000..4830c36a8ee7 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,139 @@ +//===- DWARFDebugAbbrev.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/DWARFDebugAbbrev.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +DWARFAbbreviationDeclarationSet::DWARFAbbreviationDeclarationSet() { + clear(); +} + +void DWARFAbbreviationDeclarationSet::clear() { + Offset = 0; + FirstAbbrCode = 0; + Decls.clear(); +} + +bool DWARFAbbreviationDeclarationSet::extract(DataExtractor Data, + uint32_t *OffsetPtr) { + clear(); + const uint32_t BeginOffset = *OffsetPtr; + Offset = BeginOffset; + DWARFAbbreviationDeclaration AbbrDecl; + uint32_t PrevAbbrCode = 0; + while (AbbrDecl.extract(Data, OffsetPtr)) { + if (FirstAbbrCode == 0) { + FirstAbbrCode = AbbrDecl.getCode(); + } else { + if (PrevAbbrCode + 1 != AbbrDecl.getCode()) { + // Codes are not consecutive, can't do O(1) lookups. + FirstAbbrCode = UINT32_MAX; + } + } + PrevAbbrCode = AbbrDecl.getCode(); + Decls.push_back(std::move(AbbrDecl)); + } + return BeginOffset != *OffsetPtr; +} + +void DWARFAbbreviationDeclarationSet::dump(raw_ostream &OS) const { + for (const auto &Decl : Decls) + Decl.dump(OS); +} + +const DWARFAbbreviationDeclaration * +DWARFAbbreviationDeclarationSet::getAbbreviationDeclaration( + uint32_t AbbrCode) const { + if (FirstAbbrCode == UINT32_MAX) { + for (const auto &Decl : Decls) { + if (Decl.getCode() == AbbrCode) + return &Decl; + } + return nullptr; + } + if (AbbrCode < FirstAbbrCode || AbbrCode >= FirstAbbrCode + Decls.size()) + return nullptr; + return &Decls[AbbrCode - FirstAbbrCode]; +} + +DWARFDebugAbbrev::DWARFDebugAbbrev() { clear(); } + +void DWARFDebugAbbrev::clear() { + AbbrDeclSets.clear(); + PrevAbbrOffsetPos = AbbrDeclSets.end(); +} + +void DWARFDebugAbbrev::extract(DataExtractor Data) { + clear(); + this->Data = Data; +} + +void DWARFDebugAbbrev::parse() const { + if (!Data) + return; + uint32_t Offset = 0; + DWARFAbbreviationDeclarationSet AbbrDecls; + auto I = AbbrDeclSets.begin(); + while (Data->isValidOffset(Offset)) { + while (I != AbbrDeclSets.end() && I->first < Offset) + ++I; + uint32_t CUAbbrOffset = Offset; + if (!AbbrDecls.extract(*Data, &Offset)) + break; + AbbrDeclSets.insert(I, std::make_pair(CUAbbrOffset, std::move(AbbrDecls))); + } + Data = None; +} + +void DWARFDebugAbbrev::dump(raw_ostream &OS) const { + parse(); + + if (AbbrDeclSets.empty()) { + OS << "< EMPTY >\n"; + return; + } + + for (const auto &I : AbbrDeclSets) { + OS << format("Abbrev table for offset: 0x%8.8" PRIx64 "\n", I.first); + I.second.dump(OS); + } +} + +const DWARFAbbreviationDeclarationSet* +DWARFDebugAbbrev::getAbbreviationDeclarationSet(uint64_t CUAbbrOffset) const { + const auto End = AbbrDeclSets.end(); + if (PrevAbbrOffsetPos != End && PrevAbbrOffsetPos->first == CUAbbrOffset) { + return &(PrevAbbrOffsetPos->second); + } + + const auto Pos = AbbrDeclSets.find(CUAbbrOffset); + if (Pos != End) { + PrevAbbrOffsetPos = Pos; + return &(Pos->second); + } + + if (Data && CUAbbrOffset < Data->getData().size()) { + uint32_t Offset = CUAbbrOffset; + DWARFAbbreviationDeclarationSet AbbrDecls; + if (!AbbrDecls.extract(*Data, &Offset)) + return nullptr; + PrevAbbrOffsetPos = + AbbrDeclSets.insert(std::make_pair(CUAbbrOffset, std::move(AbbrDecls))) + .first; + return &PrevAbbrOffsetPos->second; + } + + return nullptr; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp new file mode 100644 index 000000000000..22759bfac26c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp @@ -0,0 +1,198 @@ +//===- DWARFDebugAddr.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/DWARFDebugAddr.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +using namespace llvm; + +void DWARFDebugAddrTable::clear() { + HeaderData = {}; + Addrs.clear(); + invalidateLength(); +} + +Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data, + uint32_t *OffsetPtr, + uint16_t Version, + uint8_t AddrSize, + std::function<void(Error)> WarnCallback) { + clear(); + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createStringError(errc::invalid_argument, + "section is not large enough to contain a " + ".debug_addr table length at offset 0x%" + PRIx32, *OffsetPtr); + uint16_t UnitVersion; + if (Version == 0) { + WarnCallback(createStringError(errc::invalid_argument, + "DWARF version is not defined in CU," + " assuming version 5")); + UnitVersion = 5; + } else { + UnitVersion = Version; + } + // TODO: Add support for DWARF64. + Format = dwarf::DwarfFormat::DWARF32; + if (UnitVersion >= 5) { + HeaderData.Length = Data.getU32(OffsetPtr); + if (HeaderData.Length == 0xffffffffu) { + invalidateLength(); + return createStringError(errc::not_supported, + "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx32, + HeaderOffset); + } + if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) { + uint32_t TmpLength = getLength(); + invalidateLength(); + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx32 + " has too small length (0x%" PRIx32 + ") to contain a complete header", + HeaderOffset, TmpLength); + } + uint32_t End = HeaderOffset + getLength(); + if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) { + uint32_t TmpLength = getLength(); + invalidateLength(); + return createStringError(errc::invalid_argument, + "section is not large enough to contain a .debug_addr table " + "of length 0x%" PRIx32 " at offset 0x%" PRIx32, + TmpLength, HeaderOffset); + } + + HeaderData.Version = Data.getU16(OffsetPtr); + HeaderData.AddrSize = Data.getU8(OffsetPtr); + HeaderData.SegSize = Data.getU8(OffsetPtr); + DataSize = getDataSize(); + } else { + HeaderData.Version = UnitVersion; + HeaderData.AddrSize = AddrSize; + // TODO: Support for non-zero SegSize. + HeaderData.SegSize = 0; + DataSize = Data.size(); + } + + // Perform basic validation of the remaining header fields. + + // We support DWARF version 5 for now as well as pre-DWARF5 + // implementations of .debug_addr table, which doesn't contain a header + // and consists only of a series of addresses. + if (HeaderData.Version > 5) { + return createStringError(errc::not_supported, "version %" PRIu16 + " of .debug_addr section at offset 0x%" PRIx32 " is not supported", + HeaderData.Version, HeaderOffset); + } + // FIXME: For now we just treat version mismatch as an error, + // however the correct way to associate a .debug_addr table + // with a .debug_info table is to look at the DW_AT_addr_base + // attribute in the info table. + if (HeaderData.Version != UnitVersion) + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx32 + " has version %" PRIu16 + " which is different from the version suggested" + " by the DWARF unit header: %" PRIu16, + HeaderOffset, HeaderData.Version, UnitVersion); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createStringError(errc::not_supported, + ".debug_addr table at offset 0x%" PRIx32 + " has unsupported address size %" PRIu8, + HeaderOffset, HeaderData.AddrSize); + if (HeaderData.AddrSize != AddrSize && AddrSize != 0) + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx32 + " has address size %" PRIu8 + " which is different from CU address size %" PRIu8, + HeaderOffset, HeaderData.AddrSize, AddrSize); + + // TODO: add support for non-zero segment selector size. + if (HeaderData.SegSize != 0) + return createStringError(errc::not_supported, + ".debug_addr table at offset 0x%" PRIx32 + " has unsupported segment selector size %" PRIu8, + HeaderOffset, HeaderData.SegSize); + if (DataSize % HeaderData.AddrSize != 0) { + invalidateLength(); + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx32 + " contains data of size %" PRIu32 + " which is not a multiple of addr size %" PRIu8, + HeaderOffset, DataSize, HeaderData.AddrSize); + } + Data.setAddressSize(HeaderData.AddrSize); + uint32_t AddrCount = DataSize / HeaderData.AddrSize; + for (uint32_t I = 0; I < AddrCount; ++I) + if (HeaderData.AddrSize == 4) + Addrs.push_back(Data.getU32(OffsetPtr)); + else + Addrs.push_back(Data.getU64(OffsetPtr)); + return Error::success(); +} + +void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (DumpOpts.Verbose) + OS << format("0x%8.8" PRIx32 ": ", HeaderOffset); + OS << format("Addr Section: length = 0x%8.8" PRIx32 + ", version = 0x%4.4" PRIx16 ", " + "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n", + HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, + HeaderData.SegSize); + + static const char *Fmt32 = "0x%8.8" PRIx64; + static const char *Fmt64 = "0x%16.16" PRIx64; + std::string AddrFmt = "\n"; + std::string AddrFmtVerbose = " => "; + if (HeaderData.AddrSize == 4) { + AddrFmt.append(Fmt32); + AddrFmtVerbose.append(Fmt32); + } + else { + AddrFmt.append(Fmt64); + AddrFmtVerbose.append(Fmt64); + } + + if (Addrs.size() > 0) { + OS << "Addrs: ["; + for (uint64_t Addr : Addrs) { + OS << format(AddrFmt.c_str(), Addr); + if (DumpOpts.Verbose) + OS << format(AddrFmtVerbose.c_str(), + Addr + HeaderOffset + sizeof(HeaderData)); + } + OS << "\n]\n"; + } +} + +Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const { + if (Index < Addrs.size()) + return Addrs[Index]; + return createStringError(errc::invalid_argument, + "Index %" PRIu32 " is out of range of the " + ".debug_addr table at offset 0x%" PRIx32, + Index, HeaderOffset); +} + +uint32_t DWARFDebugAddrTable::getLength() const { + if (HeaderData.Length == 0) + return 0; + // TODO: DWARF64 support. + return HeaderData.Length + sizeof(uint32_t); +} + +uint32_t DWARFDebugAddrTable::getDataSize() const { + if (DataSize != 0) + return DataSize; + if (getLength() == 0) + return 0; + return getLength() - getHeaderSize(); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 000000000000..b9ef6905912a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,112 @@ +//===- DWARFDebugArangeSet.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/DWARFDebugArangeSet.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <cstring> + +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)); + ArangeDescriptors.clear(); +} + +bool +DWARFDebugArangeSet::extract(DataExtractor data, uint32_t *offset_ptr) { + if (data.isValidOffset(*offset_ptr)) { + ArangeDescriptors.clear(); + Offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header consisting of: a + // 4-byte length containing the length of the set of entries for this + // compilation unit, not including the length field itself; a 2-byte + // version identifier containing the value 2 for DWARF Version 2; a + // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer + // containing the size in bytes of an address (or the offset portion of + // an address for segmented addressing) on the target system; and a + // 1-byte unsigned integer containing the size in bytes of a segment + // descriptor on the target system. This header is followed by a series + // of tuples. Each tuple consists of an address and a length, each in + // the size appropriate for an address on the target architecture. + HeaderData.Length = data.getU32(offset_ptr); + HeaderData.Version = data.getU16(offset_ptr); + HeaderData.CuOffset = data.getU32(offset_ptr); + HeaderData.AddrSize = data.getU8(offset_ptr); + HeaderData.SegSize = data.getU8(offset_ptr); + + // Perform basic validation of the header fields. + if (!data.isValidOffsetForDataOfSize(Offset, HeaderData.Length) || + (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)) { + clear(); + return false; + } + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - Offset; + const uint32_t tuple_size = HeaderData.AddrSize * 2; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = Offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + static_assert(sizeof(arangeDescriptor.Address) == + sizeof(arangeDescriptor.Length), + "Different datatypes for addresses and sizes!"); + assert(sizeof(arangeDescriptor.Address) >= HeaderData.AddrSize); + + while (data.isValidOffset(*offset_ptr)) { + arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.Address || arangeDescriptor.Length) + ArangeDescriptors.push_back(arangeDescriptor); + else + break; // We are done if we get a zero address and length + } + + return !ArangeDescriptors.empty(); + } + return false; +} + +void DWARFDebugArangeSet::dump(raw_ostream &OS) const { + OS << format("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, ", + HeaderData.Length, HeaderData.Version) + << format("cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", + HeaderData.CuOffset, HeaderData.AddrSize, HeaderData.SegSize); + + for (const auto &Desc : ArangeDescriptors) { + 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 new file mode 100644 index 000000000000..e8c5dec821b4 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,134 @@ +//===- DWARFDebugAranges.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/DWARFDebugAranges.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/WithColor.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <set> +#include <vector> + +using namespace llvm; + +void DWARFDebugAranges::extract(DataExtractor DebugArangesData) { + if (!DebugArangesData.isValidOffset(0)) + return; + uint32_t Offset = 0; + DWARFDebugArangeSet Set; + + while (Set.extract(DebugArangesData, &Offset)) { + uint32_t CUOffset = Set.getCompileUnitDIEOffset(); + for (const auto &Desc : Set.descriptors()) { + uint64_t LowPC = Desc.Address; + uint64_t HighPC = Desc.getEndAddress(); + appendRange(CUOffset, LowPC, HighPC); + } + ParsedCUOffsets.insert(CUOffset); + } +} + +void DWARFDebugAranges::generate(DWARFContext *CTX) { + clear(); + if (!CTX) + return; + + // Extract aranges from .debug_aranges section. + DataExtractor ArangesData(CTX->getDWARFObj().getARangeSection(), + CTX->isLittleEndian(), 0); + extract(ArangesData); + + // Generate aranges from DIEs: even if .debug_aranges section is present, + // it may describe only a small subset of compilation units, so we need to + // manually build aranges for the rest of them. + for (const auto &CU : CTX->compile_units()) { + uint32_t CUOffset = CU->getOffset(); + if (ParsedCUOffsets.insert(CUOffset).second) { + Expected<DWARFAddressRangesVector> CURanges = CU->collectAddressRanges(); + if (!CURanges) + WithColor::error() << toString(CURanges.takeError()) << '\n'; + else + for (const auto &R : *CURanges) + appendRange(CUOffset, R.LowPC, R.HighPC); + } + } + + construct(); +} + +void DWARFDebugAranges::clear() { + Endpoints.clear(); + Aranges.clear(); + ParsedCUOffsets.clear(); +} + +void DWARFDebugAranges::appendRange(uint32_t CUOffset, uint64_t LowPC, + uint64_t HighPC) { + if (LowPC >= HighPC) + return; + Endpoints.emplace_back(LowPC, CUOffset, true); + Endpoints.emplace_back(HighPC, CUOffset, false); +} + +void DWARFDebugAranges::construct() { + std::multiset<uint32_t> ValidCUs; // Maintain the set of CUs describing + // a current address range. + llvm::sort(Endpoints); + uint64_t PrevAddress = -1ULL; + for (const auto &E : Endpoints) { + if (PrevAddress < E.Address && !ValidCUs.empty()) { + // If the address range between two endpoints is described by some + // CU, first try to extend the last range in Aranges. If we can't + // do it, start a new range. + if (!Aranges.empty() && Aranges.back().HighPC() == PrevAddress && + ValidCUs.find(Aranges.back().CUOffset) != ValidCUs.end()) { + Aranges.back().setHighPC(E.Address); + } else { + Aranges.emplace_back(PrevAddress, E.Address, *ValidCUs.begin()); + } + } + // Update the set of valid CUs. + if (E.IsRangeStart) { + ValidCUs.insert(E.CUOffset); + } else { + auto CUPos = ValidCUs.find(E.CUOffset); + assert(CUPos != ValidCUs.end()); + ValidCUs.erase(CUPos); + } + PrevAddress = E.Address; + } + assert(ValidCUs.empty()); + + // Endpoints are not needed now. + Endpoints.clear(); + Endpoints.shrink_to_fit(); +} + +uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { + if (!Aranges.empty()) { + Range range(Address); + RangeCollIterator begin = Aranges.begin(); + RangeCollIterator end = Aranges.end(); + RangeCollIterator pos = + std::lower_bound(begin, end, range); + + if (pos != end && pos->containsAddress(Address)) { + return pos->CUOffset; + } else if (pos != begin) { + --pos; + if (pos->containsAddress(Address)) + return pos->CUOffset; + } + } + return -1U; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp new file mode 100644 index 000000000000..ba55ffc28174 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -0,0 +1,556 @@ +//===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===// +// +// 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/DWARFDebugFrame.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <string> +#include <vector> + +using namespace llvm; +using namespace dwarf; + + +// 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; + +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. + uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; + + if (Primary) { + // If it's a primary opcode, the first operand is encoded in the bottom + // bits of the opcode itself. + uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; + switch (Primary) { + default: + return createStringError(errc::illegal_byte_sequence, + "Invalid primary CFI opcode 0x%" PRIx8, + Primary); + 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: + return createStringError(errc::illegal_byte_sequence, + "Invalid extended CFI opcode 0x%" PRIx8, + 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; + } + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_val_offset_sf: { + // Operands: ULEB128, SLEB128 + // Note: see comment for the previous case + auto op1 = Data.getULEB128(Offset); + auto op2 = (uint64_t)Data.getSLEB128(Offset); + addInstruction(Opcode, op1, op2); + break; + } + 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: { + 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 { + + +} // end anonymous namespace + +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 { \ + OpTypes[OP][0] = OPTYPE0; \ + OpTypes[OP][1] = OPTYPE1; \ + } while (false) +#define DECLARE_OP1(OP, OPTYPE0) DECLARE_OP2(OP, OPTYPE0, OT_None) +#define DECLARE_OP0(OP) DECLARE_OP1(OP, OT_None) + + DECLARE_OP1(DW_CFA_set_loc, OT_Address); + DECLARE_OP1(DW_CFA_advance_loc, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc1, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc2, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc4, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_MIPS_advance_loc8, OT_FactoredCodeOffset); + DECLARE_OP2(DW_CFA_def_cfa, OT_Register, OT_Offset); + DECLARE_OP2(DW_CFA_def_cfa_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP1(DW_CFA_def_cfa_register, OT_Register); + DECLARE_OP1(DW_CFA_def_cfa_offset, OT_Offset); + DECLARE_OP1(DW_CFA_def_cfa_offset_sf, OT_SignedFactDataOffset); + DECLARE_OP1(DW_CFA_def_cfa_expression, OT_Expression); + DECLARE_OP1(DW_CFA_undefined, OT_Register); + DECLARE_OP1(DW_CFA_same_value, OT_Register); + DECLARE_OP2(DW_CFA_offset, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_offset_extended, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_offset_extended_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP2(DW_CFA_val_offset, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_val_offset_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP2(DW_CFA_register, OT_Register, OT_Register); + DECLARE_OP2(DW_CFA_expression, OT_Register, OT_Expression); + DECLARE_OP2(DW_CFA_val_expression, OT_Register, OT_Expression); + DECLARE_OP1(DW_CFA_restore, OT_Register); + DECLARE_OP1(DW_CFA_restore_extended, OT_Register); + DECLARE_OP0(DW_CFA_remember_state); + DECLARE_OP0(DW_CFA_restore_state); + DECLARE_OP0(DW_CFA_GNU_window_save); + DECLARE_OP1(DW_CFA_GNU_args_size, OT_Offset); + DECLARE_OP0(DW_CFA_nop); + +#undef DECLARE_OP0 +#undef DECLARE_OP1 +#undef DECLARE_OP2 + + return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1); +} + +/// 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); + uint8_t Opcode = Instr.Opcode; + OperandType Type = getOperandTypes()[Opcode][OperandIdx]; + + switch (Type) { + case OT_Unset: { + OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; + auto OpcodeName = CallFrameString(Opcode, Arch); + if (!OpcodeName.empty()) + OS << " " << OpcodeName; + else + OS << format(" Opcode %x", Opcode); + break; + } + case OT_None: + break; + case OT_Address: + OS << format(" %" PRIx64, Operand); + break; + case OT_Offset: + // The offsets are all encoded in a unsigned form, but in practice + // consumers use them signed. It's most certainly legacy due to + // the lack of signed variants in the first Dwarf standards. + OS << format(" %+" PRId64, int64_t(Operand)); + break; + case OT_FactoredCodeOffset: // Always Unsigned + if (CodeAlignmentFactor) + OS << format(" %" PRId64, Operand * CodeAlignmentFactor); + else + OS << format(" %" PRId64 "*code_alignment_factor" , Operand); + break; + case OT_SignedFactDataOffset: + if (DataAlignmentFactor) + OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor); + else + OS << format(" %" PRId64 "*data_alignment_factor" , int64_t(Operand)); + break; + case OT_UnsignedFactDataOffset: + if (DataAlignmentFactor) + OS << format(" %" PRId64, Operand * DataAlignmentFactor); + else + OS << format(" %" PRId64 "*data_alignment_factor" , Operand); + break; + case OT_Register: + OS << format(" reg%" PRId64, Operand); + break; + case OT_Expression: + assert(Instr.Expression && "missing DWARFExpression object"); + OS << " "; + Instr.Expression->print(OS, MRI, IsEH); + break; + } +} + +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.indent(2 * IndentLevel); + OS << CallFrameString(Opcode, Arch) << ":"; + for (unsigned i = 0; i < Instr.Ops.size(); ++i) + printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]); + OS << '\n'; + } +} + +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(Triple::ArchType Arch, + bool IsEH, uint64_t EHFrameAddress) + : Arch(Arch), IsEH(IsEH), EHFrameAddress(EHFrameAddress) {} + +DWARFDebugFrame::~DWARFDebugFrame() = default; + +static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, + uint32_t Offset, int Length) { + errs() << "DUMP: "; + for (int i = 0; i < Length; ++i) { + uint8_t c = Data.getU8(&Offset); + errs().write_hex(c); errs() << " "; + } + errs() << "\n"; +} + +// 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 +// a ReportError lambda: [StartOffset](const char *ErrorMsg). +static void LLVM_ATTRIBUTE_NORETURN ReportError(uint32_t StartOffset, + const char *ErrorMsg) { + std::string Str; + raw_string_ostream OS(Str); + OS << format(ErrorMsg, StartOffset); + OS.flush(); + report_fatal_error(Str); +} + +void DWARFDebugFrame::parse(DWARFDataExtractor Data) { + uint32_t Offset = 0; + DenseMap<uint32_t, CIE *> CIEs; + + while (Data.isValidOffset(Offset)) { + uint32_t StartOffset = Offset; + + bool IsDWARF64 = false; + uint64_t Length = Data.getU32(&Offset); + uint64_t Id; + + if (Length == UINT32_MAX) { + // DWARF-64 is distinguished by the first 32 bits of the initial length + // field being 0xffffffff. Then, the next 64 bits are the actual entry + // length. + IsDWARF64 = true; + Length = Data.getU64(&Offset); + } + + // At this point, Offset points to the next field after Length. + // Length is the structure size excluding itself. Compute an offset one + // past the end of the structure (needed to know how many instructions to + // read). + // TODO: For honest DWARF64 support, DataExtractor will have to treat + // offset_ptr as uint64_t* + uint32_t StartStructureOffset = Offset; + uint32_t EndStructureOffset = Offset + static_cast<uint32_t>(Length); + + // 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)); + + if (IsCIE) { + uint8_t Version = Data.getU8(&Offset); + const char *Augmentation = Data.getCStr(&Offset); + StringRef AugmentationString(Augmentation ? Augmentation : ""); + uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : + Data.getU8(&Offset); + Data.setAddressSize(AddressSize); + uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(&Offset); + uint64_t CodeAlignmentFactor = Data.getULEB128(&Offset); + int64_t DataAlignmentFactor = Data.getSLEB128(&Offset); + uint64_t ReturnAddressRegister = + Version == 1 ? Data.getU8(&Offset) : Data.getULEB128(&Offset); + + // Parse the augmentation data for EH CIEs + StringRef AugmentationData(""); + 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<uint64_t> AugmentationLength; + uint32_t StartAugmentationOffset; + uint32_t EndAugmentationOffset; + + // Walk the augmentation string to get all the augmentation data. + for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { + switch (AugmentationString[i]) { + default: + ReportError(StartOffset, + "Unknown augmentation character in entry at %lx"); + case 'L': + LSDAPointerEncoding = Data.getU8(&Offset); + break; + case 'P': { + if (Personality) + ReportError(StartOffset, + "Duplicate personality in entry at %lx"); + PersonalityEncoding = Data.getU8(&Offset); + 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, + "'z' must be the first character at %lx"); + // Parse the augmentation length first. We only parse it if + // the string contains a 'z'. + AugmentationLength = Data.getULEB128(&Offset); + StartAugmentationOffset = Offset; + EndAugmentationOffset = Offset + + static_cast<uint32_t>(*AugmentationLength); + break; + case 'B': + // B-Key is used for signing functions associated with this + // augmentation string + break; + } + } + + if (AugmentationLength.hasValue()) { + if (Offset != EndAugmentationOffset) + ReportError(StartOffset, "Parsing augmentation data at %lx failed"); + + AugmentationData = Data.getData().slice(StartAugmentationOffset, + EndAugmentationOffset); + } + } + + auto Cie = llvm::make_unique<CIE>( + StartOffset, Length, Version, AugmentationString, AddressSize, + SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor, + ReturnAddressRegister, AugmentationData, FDEPointerEncoding, + LSDAPointerEncoding, Personality, PersonalityEncoding, Arch); + CIEs[StartOffset] = Cie.get(); + Entries.emplace_back(std::move(Cie)); + } else { + // FDE + 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) { + // The address size is encoded in the CIE we reference. + if (!Cie) + ReportError(StartOffset, + "Parsing FDE data at %lx failed due to missing CIE"); + + 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()) { + // Parse the augmentation length and data for this FDE. + uint64_t AugmentationLength = Data.getULEB128(&Offset); + + uint32_t EndAugmentationOffset = + Offset + static_cast<uint32_t>(AugmentationLength); + + // Decode the LSDA if the CIE augmentation string said we should. + 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"); + } + } else { + InitialLocation = Data.getAddress(&Offset); + AddressRange = Data.getAddress(&Offset); + } + + Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, + InitialLocation, AddressRange, + Cie, LSDAAddress, Arch)); + } + + 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"); + } +} + +FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { + auto It = + std::lower_bound(Entries.begin(), Entries.end(), Offset, + [](const std::unique_ptr<FrameEntry> &E, + uint64_t Offset) { return E->getOffset() < Offset; }); + if (It != Entries.end() && (*It)->getOffset() == Offset) + return It->get(); + return nullptr; +} + +void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, + Optional<uint64_t> Offset) const { + if (Offset) { + if (auto *Entry = getEntryAtOffset(*Offset)) + Entry->dump(OS, MRI, IsEH); + return; + } + + OS << "\n"; + for (const auto &Entry : Entries) + Entry->dump(OS, MRI, IsEH); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 000000000000..976bc4651ae6 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,70 @@ +//===- DWARFDebugInfoEntry.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/DWARFDebugInfoEntry.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, + uint32_t *OffsetPtr) { + DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); + const uint32_t UEndOffset = U.getNextUnitOffset(); + return extractFast(U, OffsetPtr, DebugInfoData, UEndOffset, 0); +} + +bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, uint32_t *OffsetPtr, + const DWARFDataExtractor &DebugInfoData, + uint32_t UEndOffset, uint32_t D) { + Offset = *OffsetPtr; + Depth = D; + if (Offset >= UEndOffset || !DebugInfoData.isValidOffset(Offset)) + return false; + uint64_t AbbrCode = DebugInfoData.getULEB128(OffsetPtr); + if (0 == AbbrCode) { + // NULL debug tag entry. + AbbrevDecl = nullptr; + return true; + } + AbbrevDecl = U.getAbbreviations()->getAbbreviationDeclaration(AbbrCode); + if (nullptr == AbbrevDecl) { + // Restore the original offset. + *OffsetPtr = Offset; + return false; + } + // See if all attributes in this DIE have fixed byte sizes. If so, we can + // just add this size to the offset to skip to the next DIE. + if (Optional<size_t> FixedSize = AbbrevDecl->getFixedAttributesByteSize(U)) { + *OffsetPtr += *FixedSize; + return true; + } + + // Skip all data in the .debug_info for the attributes + for (const auto &AttrSpec : AbbrevDecl->attributes()) { + // Check if this attribute has a fixed byte size. + if (auto FixedSize = AttrSpec.getByteSize(U)) { + // Attribute byte size if fixed, just add the size to the offset. + *OffsetPtr += *FixedSize; + } else if (!DWARFFormValue::skipValue(AttrSpec.Form, DebugInfoData, + OffsetPtr, U.getFormParams())) { + // We failed to skip this attribute's value, restore the original offset + // and return the failure status. + *OffsetPtr = Offset; + return false; + } + } + return true; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp new file mode 100644 index 000000000000..1d621ff244f3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,1114 @@ +//===- DWARFDebugLine.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/DWARFDebugLine.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Errc.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> +#include <cinttypes> +#include <cstdint> +#include <cstdio> +#include <utility> + +using namespace llvm; +using namespace dwarf; + +using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; + +namespace { + +struct ContentDescriptor { + dwarf::LineNumberEntryFormat Type; + dwarf::Form Form; +}; + +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() { + TotalLength = PrologueLength = 0; + SegSelectorSize = 0; + MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0; + OpcodeBase = 0; + FormParams = dwarf::FormParams({0, 0, DWARF32}); + ContentTypes = ContentTypeTracker(); + StandardOpcodeLengths.clear(); + IncludeDirectories.clear(); + FileNames.clear(); +} + +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()); + if (getVersion() >= 5) + OS << format(" address_size: %u\n", getAddressSize()) + << format(" seg_select_size: %u\n", SegSelectorSize); + OS << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength) + << format(" min_inst_length: %u\n", MinInstLength) + << format(getVersion() >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst) + << format(" default_is_stmt: %u\n", DefaultIsStmt) + << format(" line_base: %i\n", LineBase) + << format(" line_range: %u\n", LineRange) + << format(" opcode_base: %u\n", OpcodeBase); + + for (uint32_t I = 0; I != StandardOpcodeLengths.size(); ++I) + OS << format("standard_opcode_lengths[%s] = %u\n", + LNStandardString(I + 1).data(), StandardOpcodeLengths[I]); + + 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()) { + // 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]:\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'; + } + } + } +} + +// Parse v2-v4 directory and file tables. +static void +parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, + uint32_t *OffsetPtr, uint64_t EndPrologueOffset, + DWARFDebugLine::ContentTypeTracker &ContentTypes, + std::vector<DWARFFormValue> &IncludeDirectories, + std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { + while (*OffsetPtr < EndPrologueOffset) { + StringRef S = DebugLineData.getCStrRef(OffsetPtr); + if (S.empty()) + break; + DWARFFormValue Dir(dwarf::DW_FORM_string); + Dir.setPValue(S.data()); + IncludeDirectories.push_back(Dir); + } + + while (*OffsetPtr < EndPrologueOffset) { + StringRef Name = DebugLineData.getCStrRef(OffsetPtr); + if (Name.empty()) + break; + DWARFDebugLine::FileNameEntry FileEntry; + 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, DWARFDebugLine::ContentTypeTracker + *ContentTypes) { + ContentDescriptors Descriptors; + int FormatCount = DebugLineData.getU8(OffsetPtr); + bool HasPath = false; + for (int I = 0; I != FormatCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return ContentDescriptors(); + ContentDescriptor Descriptor; + Descriptor.Type = + dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); + Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); + if (Descriptor.Type == dwarf::DW_LNCT_path) + HasPath = true; + if (ContentTypes) + ContentTypes->trackContentType(Descriptor.Type); + Descriptors.push_back(Descriptor); + } + return HasPath ? Descriptors : ContentDescriptors(); +} + +static bool +parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, + uint32_t *OffsetPtr, uint64_t EndPrologueOffset, + 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 = + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); + if (DirDescriptors.empty()) + return false; + + // Get the directory entries, according to the format described above. + int DirEntryCount = DebugLineData.getU8(OffsetPtr); + for (int I = 0; I != DirEntryCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return false; + for (auto Descriptor : DirDescriptors) { + DWARFFormValue Value(Descriptor.Form); + switch (Descriptor.Type) { + case DW_LNCT_path: + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) + return false; + IncludeDirectories.push_back(Value); + break; + default: + if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) + return false; + } + } + } + + // Get the file entry description. + ContentDescriptors FileDescriptors = + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, + &ContentTypes); + if (FileDescriptors.empty()) + return false; + + // Get the file entries, according to the format described above. + int FileEntryCount = DebugLineData.getU8(OffsetPtr); + for (int I = 0; I != FileEntryCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return false; + DWARFDebugLine::FileNameEntry FileEntry; + for (auto Descriptor : FileDescriptors) { + DWARFFormValue Value(Descriptor.Form); + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) + return false; + switch (Descriptor.Type) { + case DW_LNCT_path: + FileEntry.Name = Value; + break; + case DW_LNCT_LLVM_source: + FileEntry.Source = Value; + break; + case DW_LNCT_directory_index: + FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_timestamp: + FileEntry.ModTime = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_size: + FileEntry.Length = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_MD5: + assert(Value.getAsBlock().getValue().size() == 16); + std::uninitialized_copy_n(Value.getAsBlock().getValue().begin(), 16, + FileEntry.Checksum.Bytes.begin()); + break; + default: + break; + } + } + FileNames.push_back(FileEntry); + } + return true; +} + +Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, + uint32_t *OffsetPtr, + const DWARFContext &Ctx, + const DWARFUnit *U) { + const uint64_t PrologueOffset = *OffsetPtr; + + clear(); + TotalLength = DebugLineData.getU32(OffsetPtr); + if (TotalLength == UINT32_MAX) { + FormParams.Format = dwarf::DWARF64; + TotalLength = DebugLineData.getU64(OffsetPtr); + } else if (TotalLength >= 0xffffff00) { + return createStringError(errc::invalid_argument, + "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 createStringError(errc::not_supported, + "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); + assert((DebugLineData.getAddressSize() == 0 || + DebugLineData.getAddressSize() == getAddressSize()) && + "Line table header and data extractor disagree"); + SegSelectorSize = DebugLineData.getU8(OffsetPtr); + } + + PrologueLength = DebugLineData.getUnsigned(OffsetPtr, sizeofPrologueLength()); + const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; + MinInstLength = DebugLineData.getU8(OffsetPtr); + if (getVersion() >= 4) + MaxOpsPerInst = DebugLineData.getU8(OffsetPtr); + DefaultIsStmt = DebugLineData.getU8(OffsetPtr); + LineBase = DebugLineData.getU8(OffsetPtr); + LineRange = DebugLineData.getU8(OffsetPtr); + OpcodeBase = DebugLineData.getU8(OffsetPtr); + + StandardOpcodeLengths.reserve(OpcodeBase - 1); + for (uint32_t I = 1; I < OpcodeBase; ++I) { + uint8_t OpLen = DebugLineData.getU8(OffsetPtr); + StandardOpcodeLengths.push_back(OpLen); + } + + if (getVersion() >= 5) { + if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, + FormParams, Ctx, U, ContentTypes, + IncludeDirectories, FileNames)) { + return createStringError(errc::invalid_argument, + "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, + ContentTypes, IncludeDirectories, FileNames); + + if (*OffsetPtr != EndPrologueOffset) + return createStringError(errc::invalid_argument, + "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); } + +void DWARFDebugLine::Row::postAppend() { + BasicBlock = false; + PrologueEnd = false; + EpilogueBegin = false; +} + +void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { + Address = 0; + Line = 1; + Column = 0; + File = 1; + Isa = 0; + Discriminator = 0; + IsStmt = DefaultIsStmt; + BasicBlock = false; + EndSequence = false; + PrologueEnd = false; + EpilogueBegin = false; +} + +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { + OS << "Address Line Column File ISA Discriminator Flags\n" + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; +} + +void DWARFDebugLine::Row::dump(raw_ostream &OS) const { + OS << format("0x%16.16" PRIx64 " %6u %6u", Address, Line, Column) + << format(" %6u %3u %13u ", File, Isa, Discriminator) + << (IsStmt ? " is_stmt" : "") << (BasicBlock ? " basic_block" : "") + << (PrologueEnd ? " prologue_end" : "") + << (EpilogueBegin ? " epilogue_begin" : "") + << (EndSequence ? " end_sequence" : "") << '\n'; +} + +DWARFDebugLine::Sequence::Sequence() { reset(); } + +void DWARFDebugLine::Sequence::reset() { + LowPC = 0; + HighPC = 0; + FirstRowIndex = 0; + LastRowIndex = 0; + Empty = true; +} + +DWARFDebugLine::LineTable::LineTable() { clear(); } + +void DWARFDebugLine::LineTable::dump(raw_ostream &OS, + DIDumpOptions DumpOptions) const { + Prologue.dump(OS, DumpOptions); + OS << '\n'; + + if (!Rows.empty()) { + Row::dumpTableHeader(OS); + for (const Row &R : Rows) { + R.dump(OS); + } + } +} + +void DWARFDebugLine::LineTable::clear() { + Prologue.clear(); + Rows.clear(); + Sequences.clear(); +} + +DWARFDebugLine::ParsingState::ParsingState(struct LineTable *LT) + : LineTable(LT) { + resetRowAndSequence(); +} + +void DWARFDebugLine::ParsingState::resetRowAndSequence() { + Row.reset(LineTable->Prologue.DefaultIsStmt); + Sequence.reset(); +} + +void DWARFDebugLine::ParsingState::appendRowToMatrix(uint32_t Offset) { + if (Sequence.Empty) { + // Record the beginning of instruction sequence. + Sequence.Empty = false; + Sequence.LowPC = Row.Address; + Sequence.FirstRowIndex = RowNumber; + } + ++RowNumber; + LineTable->appendRow(Row); + if (Row.EndSequence) { + // Record the end of instruction sequence. + Sequence.HighPC = Row.Address; + Sequence.LastRowIndex = RowNumber; + if (Sequence.isValid()) + LineTable->appendSequence(Sequence); + Sequence.reset(); + } + Row.postAppend(); +} + +const DWARFDebugLine::LineTable * +DWARFDebugLine::getLineTable(uint32_t Offset) const { + LineTableConstIter Pos = LineTableMap.find(Offset); + if (Pos != LineTableMap.end()) + return &Pos->second; + return nullptr; +} + +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 createStringError(errc::invalid_argument, "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 (Error Err = + LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorCallback)) + return std::move(Err); + return LT; + } + return LT; +} + +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(); + + 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 (PrologueErr) + return PrologueErr; + + const uint32_t EndOffset = + DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength(); + + // See if we should tell the data extractor the address size. + if (DebugLineData.getAddressSize() == 0) + DebugLineData.setAddressSize(Prologue.getAddressSize()); + else + assert(Prologue.getAddressSize() == 0 || + Prologue.getAddressSize() == DebugLineData.getAddressSize()); + + ParsingState State(this); + + while (*OffsetPtr < EndOffset) { + if (OS) + *OS << format("0x%08.08" PRIx32 ": ", *OffsetPtr); + + uint8_t Opcode = DebugLineData.getU8(OffsetPtr); + + if (OS) + *OS << format("%02.02" PRIx8 " ", Opcode); + + if (Opcode == 0) { + // Extended Opcodes always start with a zero opcode followed by + // a uleb128 length so you can skip ones you don't know about + uint64_t Len = DebugLineData.getULEB128(OffsetPtr); + uint32_t ExtOffset = *OffsetPtr; + + // Tolerate zero-length; assume length is correct and soldier on. + if (Len == 0) { + if (OS) + *OS << "Badly formed extended line op (length 0)\n"; + continue; + } + + uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); + if (OS) + *OS << LNExtendedString(SubOpcode); + switch (SubOpcode) { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the + // state-machine registers. Then reset the registers to the initial + // values specified above. Every statement program sequence must end + // with a DW_LNE_end_sequence instruction which creates a row whose + // address is that of the byte after the last target machine instruction + // of the sequence. + State.Row.EndSequence = true; + State.appendRowToMatrix(*OffsetPtr); + if (OS) { + *OS << "\n"; + OS->indent(12); + State.Row.dump(*OS); + } + State.resetRowAndSequence(); + break; + + case DW_LNE_set_address: + // Takes a single relocatable address as an operand. The size of the + // operand is the size appropriate to hold an address on the target + // machine. Set the address register to the value given by the + // relocatable address. All of the other statement program opcodes + // that affect the address register add a delta to it. This instruction + // stores a relocatable value into it instead. + // + // Make sure the extractor knows the address size. If not, infer it + // from the size of the operand. + if (DebugLineData.getAddressSize() == 0) + DebugLineData.setAddressSize(Len - 1); + else if (DebugLineData.getAddressSize() != Len - 1) { + return createStringError(errc::invalid_argument, + "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); + break; + + case DW_LNE_define_file: + // Takes 4 arguments. The first is a null terminated string containing + // a source file name. The second is an unsigned LEB128 number + // representing the directory index of the directory in which the file + // was found. The third is an unsigned LEB128 number representing the + // time of last modification of the file. The fourth is an unsigned + // LEB128 number representing the length in bytes of the file. The time + // and length fields may contain LEB128(0) if the information is not + // available. + // + // The directory index represents an entry in the include_directories + // section of the statement program prologue. The index is LEB128(0) + // if the file was found in the current directory of the compilation, + // LEB128(1) if it was found in the first directory in the + // include_directories section, and so on. The directory index is + // ignored for file names that represent full path names. + // + // The files are numbered, starting at 1, in the order in which they + // appear; the names in the prologue come before names defined by + // the DW_LNE_define_file instruction. These numbers are used in the + // the file register of the state machine. + { + FileNameEntry FileEntry; + 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 << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" + << format("(0x%16.16" PRIx64 ")", FileEntry.ModTime) + << ", length=" << FileEntry.Length << ")"; + } + break; + + case DW_LNE_set_discriminator: + State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Discriminator << ")"; + break; + + default: + if (OS) + *OS << format("Unrecognized extended op 0x%02.02" PRIx8, SubOpcode) + << format(" length %" PRIx64, Len); + // Len doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that. + (*OffsetPtr) += Len - 1; + break; + } + // Make sure the stated and parsed lengths are the same. + // Otherwise we have an unparseable line-number program. + if (*OffsetPtr - ExtOffset != Len) + return createStringError(errc::illegal_byte_sequence, + "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); + switch (Opcode) { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the + // current values of the state-machine registers. Then set + // the basic_block register to false. + State.appendRowToMatrix(*OffsetPtr); + if (OS) { + *OS << "\n"; + OS->indent(12); + State.Row.dump(*OS); + *OS << "\n"; + } + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the + // result to the address register of the state machine. + { + uint64_t AddrOffset = + DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; + State.Row.Address += AddrOffset; + if (OS) + *OS << " (" << AddrOffset << ")"; + } + break; + + case DW_LNS_advance_line: + // Takes a single signed LEB128 operand and adds that value to + // the line register of the state machine. + State.Row.Line += DebugLineData.getSLEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Line << ")"; + break; + + case DW_LNS_set_file: + // Takes a single unsigned LEB128 operand and stores it in the file + // register of the state machine. + State.Row.File = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.File << ")"; + break; + + case DW_LNS_set_column: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + State.Row.Column = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Column << ")"; + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state + // machine to the logical negation of its current value. + State.Row.IsStmt = !State.Row.IsStmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the + // state machine to true + State.Row.BasicBlock = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state + // machine the address increment value corresponding to special + // opcode 255. The motivation for DW_LNS_const_add_pc is this: + // when the statement program needs to advance the address by a + // small amount, it can use a single special opcode, which occupies + // a single byte. When it needs to advance the address by up to + // twice the range of the last special opcode, it can use + // DW_LNS_const_add_pc followed by a special opcode, for a total + // of two bytes. Only if it needs to advance the address by more + // than twice that range will it need to use both DW_LNS_advance_pc + // and a special opcode, requiring three or more bytes. + { + uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; + uint64_t AddrOffset = + (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; + State.Row.Address += AddrOffset; + if (OS) + *OS + << format(" (0x%16.16" PRIx64 ")", AddrOffset); + } + break; + + case DW_LNS_fixed_advance_pc: + // Takes a single uhalf operand. Add to the address register of + // the state machine the value of the (unencoded) operand. This + // is the only extended opcode that takes an argument that is not + // a variable length number. The motivation for DW_LNS_fixed_advance_pc + // is this: existing assemblers cannot emit DW_LNS_advance_pc or + // special opcodes because they cannot encode LEB128 numbers or + // judge when the computation of a special opcode overflows and + // requires the use of DW_LNS_advance_pc. Such assemblers, however, + // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. + { + uint16_t PCOffset = DebugLineData.getU16(OffsetPtr); + State.Row.Address += PCOffset; + if (OS) + *OS + << format(" (0x%16.16" PRIx64 ")", PCOffset); + } + break; + + case DW_LNS_set_prologue_end: + // Takes no arguments. Set the prologue_end register of the + // state machine to true + State.Row.PrologueEnd = true; + break; + + case DW_LNS_set_epilogue_begin: + // Takes no arguments. Set the basic_block register of the + // state machine to true + State.Row.EpilogueBegin = true; + break; + + case DW_LNS_set_isa: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Isa << ")"; + break; + + default: + // Handle any unknown standard opcodes here. We know the lengths + // of such opcodes because they are specified in the prologue + // as a multiple of LEB128 operands for each opcode. + { + assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); + uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; + for (uint8_t I = 0; I < OpcodeLength; ++I) { + uint64_t Value = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << format("Skipping ULEB128 value: 0x%16.16" PRIx64 ")\n", + Value); + } + } + break; + } + } else { + // Special Opcodes + + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The "address + // advance" is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + + // (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * + // minimum_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; + uint64_t AddrOffset = + (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; + int32_t LineOffset = + Prologue.LineBase + (AdjustOpcode % Prologue.LineRange); + State.Row.Line += LineOffset; + State.Row.Address += AddrOffset; + + if (OS) { + *OS << "address += " << ((uint32_t)AdjustOpcode) + << ", line += " << LineOffset << "\n"; + OS->indent(12); + State.Row.dump(*OS); + } + + State.appendRowToMatrix(*OffsetPtr); + // Reset discriminator to 0. + State.Row.Discriminator = 0; + } + if(OS) + *OS << "\n"; + } + + if (!State.Sequence.Empty) + RecoverableErrorCallback( + createStringError(errc::illegal_byte_sequence, + "last sequence in debug line table is not terminated!")); + + // Sort all sequences so that address lookup will work faster. + if (!Sequences.empty()) { + llvm::sort(Sequences, 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. + // We don't report warning in this case. For example, + // sometimes .so compiled from multiple object files contains a few + // rudimentary sequences for address ranges [0x0, 0xsomething). + } + + return Error::success(); +} + +uint32_t +DWARFDebugLine::LineTable::findRowInSeq(const DWARFDebugLine::Sequence &Seq, + uint64_t Address) const { + if (!Seq.containsPC(Address)) + return UnknownRowIndex; + // Search for instruction address in the rows describing the sequence. + // Rows are stored in a vector, so we may use arithmetical operations with + // iterators. + DWARFDebugLine::Row Row; + Row.Address = Address; + RowIter FirstRow = Rows.begin() + Seq.FirstRowIndex; + RowIter LastRow = Rows.begin() + Seq.LastRowIndex; + LineTable::RowIter RowPos = std::lower_bound( + FirstRow, LastRow, Row, DWARFDebugLine::Row::orderByAddress); + if (RowPos == LastRow) { + return Seq.LastRowIndex - 1; + } + uint32_t Index = Seq.FirstRowIndex + (RowPos - FirstRow); + if (RowPos->Address > Address) { + if (RowPos == FirstRow) + return UnknownRowIndex; + else + Index--; + } + return Index; +} + +uint32_t DWARFDebugLine::LineTable::lookupAddress(uint64_t Address) const { + if (Sequences.empty()) + return UnknownRowIndex; + // First, find an instruction sequence containing the given address. + DWARFDebugLine::Sequence Sequence; + Sequence.LowPC = Address; + SequenceIter FirstSeq = Sequences.begin(); + SequenceIter LastSeq = Sequences.end(); + SequenceIter SeqPos = std::lower_bound( + FirstSeq, LastSeq, Sequence, DWARFDebugLine::Sequence::orderByLowPC); + DWARFDebugLine::Sequence FoundSeq; + if (SeqPos == LastSeq) { + FoundSeq = Sequences.back(); + } else if (SeqPos->LowPC == Address) { + FoundSeq = *SeqPos; + } else { + if (SeqPos == FirstSeq) + return UnknownRowIndex; + FoundSeq = *(SeqPos - 1); + } + return findRowInSeq(FoundSeq, Address); +} + +bool DWARFDebugLine::LineTable::lookupAddressRange( + uint64_t Address, uint64_t Size, std::vector<uint32_t> &Result) const { + if (Sequences.empty()) + return false; + uint64_t EndAddr = Address + Size; + // First, find an instruction sequence containing the given address. + DWARFDebugLine::Sequence Sequence; + Sequence.LowPC = Address; + SequenceIter FirstSeq = Sequences.begin(); + SequenceIter LastSeq = Sequences.end(); + SequenceIter SeqPos = std::lower_bound( + FirstSeq, LastSeq, Sequence, DWARFDebugLine::Sequence::orderByLowPC); + if (SeqPos == LastSeq || SeqPos->LowPC != Address) { + if (SeqPos == FirstSeq) + return false; + SeqPos--; + } + if (!SeqPos->containsPC(Address)) + return false; + + SequenceIter StartPos = SeqPos; + + // Add the rows from the first sequence to the vector, starting with the + // index we just calculated + + while (SeqPos != LastSeq && SeqPos->LowPC < EndAddr) { + const DWARFDebugLine::Sequence &CurSeq = *SeqPos; + // For the first sequence, we need to find which row in the sequence is the + // first in our range. + uint32_t FirstRowIndex = CurSeq.FirstRowIndex; + if (SeqPos == StartPos) + FirstRowIndex = findRowInSeq(CurSeq, Address); + + // Figure out the last row in the range. + uint32_t LastRowIndex = findRowInSeq(CurSeq, EndAddr - 1); + if (LastRowIndex == UnknownRowIndex) + LastRowIndex = CurSeq.LastRowIndex - 1; + + assert(FirstRowIndex != UnknownRowIndex); + assert(LastRowIndex != UnknownRowIndex); + + for (uint32_t I = FirstRowIndex; I <= LastRowIndex; ++I) { + Result.push_back(I); + } + + ++SeqPos; + } + + return true; +} + +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, + std::string &Result) const { + if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) + return false; + const FileNameEntry &Entry = Prologue.FileNames[FileIndex - 1]; + StringRef FileName = Entry.Name.getAsCString().getValue(); + if (Kind != FileLineInfoKind::AbsoluteFilePath || + isPathAbsoluteOnWindowsOrPosix(FileName)) { + Result = FileName; + return true; + } + + SmallString<16> FilePath; + uint64_t IncludeDirIndex = Entry.DirIdx; + StringRef IncludeDir; + // Be defensive about the contents of Entry. + if (IncludeDirIndex > 0 && + IncludeDirIndex <= Prologue.IncludeDirectories.size()) + 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 && + !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) + sys::path::append(FilePath, CompDir); + + // sys::path::append skips empty strings. + sys::path::append(FilePath, IncludeDir, FileName); + Result = FilePath.str(); + return true; +} + +bool DWARFDebugLine::LineTable::getFileLineInfoForAddress( + uint64_t Address, const char *CompDir, FileLineInfoKind Kind, + DILineInfo &Result) const { + // Get the index of row we're looking for in the line table. + uint32_t RowIndex = lookupAddress(Address); + if (RowIndex == -1U) + return false; + // Take file number and line/column from the row. + const auto &Row = Rows[RowIndex]; + if (!getFileNameByIndex(Row.File, CompDir, Kind, Result.FileName)) + return false; + 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 TUs) { + 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 &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; + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp new file mode 100644 index 000000000000..94df6946f3ae --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -0,0 +1,279 @@ +//===- DWARFDebugLoc.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/DWARFDebugLoc.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#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> +#include <cstdint> + +using namespace llvm; + +// When directly dumping the .debug_loc without a compile unit, we have to guess +// at the DWARF version. This only affects DW_OP_call_ref, which is a rare +// expression that LLVM doesn't produce. Guessing the wrong version means we +// won't be able to pretty print expressions in DWARF2 binaries produced by +// non-LLVM tools. +static void dumpExpression(raw_ostream &OS, ArrayRef<char> Data, + bool IsLittleEndian, unsigned AddressSize, + const MCRegisterInfo *MRI) { + DWARFDataExtractor Extractor(StringRef(Data.data(), Data.size()), + IsLittleEndian, AddressSize); + 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%*.*" 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); + } +} + +DWARFDebugLoc::LocationList const * +DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const { + auto It = std::lower_bound( + Locations.begin(), Locations.end(), Offset, + [](const LocationList &L, uint64_t Offset) { return L.Offset < Offset; }); + if (It != Locations.end() && It->Offset == Offset) + return &(*It); + return nullptr; +} + +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, 0, 12); + OS << "\n\n"; + }; + + if (Offset) { + if (auto *L = getLocationListAtOffset(*Offset)) + DumpLocationList(*L); + return; + } + + for (const LocationList &L : Locations) { + DumpLocationList(L); + } +} + +Optional<DWARFDebugLoc::LocationList> +DWARFDebugLoc::parseOneLocationList(DWARFDataExtractor Data, unsigned *Offset) { + LocationList LL; + LL.Offset = *Offset; + + // 2.6.2 Location Lists + // A location list entry consists of: + while (true) { + Entry E; + if (!Data.isValidOffsetForDataOfSize(*Offset, 2 * Data.getAddressSize())) { + WithColor::error() << "location list overflows the debug_loc section.\n"; + return None; + } + + // 1. A beginning address offset. ... + E.Begin = Data.getRelocatedAddress(Offset); + + // 2. An ending address offset. ... + E.End = Data.getRelocatedAddress(Offset); + + // The end of any given location list is marked by an end of list entry, + // which consists of a 0 for the beginning address offset and a 0 for the + // ending address offset. + if (E.Begin == 0 && E.End == 0) + return LL; + + if (!Data.isValidOffsetForDataOfSize(*Offset, 2)) { + WithColor::error() << "location list overflows the debug_loc section.\n"; + return None; + } + + unsigned Bytes = Data.getU16(Offset); + if (!Data.isValidOffsetForDataOfSize(*Offset, Bytes)) { + WithColor::error() << "location list overflows the debug_loc section.\n"; + return None; + } + // A single location description describing the location of the object... + StringRef str = Data.getData().substr(*Offset, Bytes); + *Offset += Bytes; + E.Loc.reserve(str.size()); + llvm::copy(str, std::back_inserter(E.Loc)); + LL.Entries.push_back(std::move(E)); + } +} + +void DWARFDebugLoc::parse(const DWARFDataExtractor &data) { + IsLittleEndian = data.isLittleEndian(); + AddressSize = data.getAddressSize(); + + uint32_t Offset = 0; + while (data.isValidOffset(Offset + data.getAddressSize() - 1)) { + if (auto LL = parseOneLocationList(data, &Offset)) + Locations.push_back(std::move(*LL)); + else + break; + } + if (data.isValidOffset(Offset)) + WithColor::error() << "failed to consume entire .debug_loc section\n"; +} + +Optional<DWARFDebugLoclists::LocationList> +DWARFDebugLoclists::parseOneLocationList(DataExtractor Data, unsigned *Offset, + unsigned Version) { + LocationList LL; + LL.Offset = *Offset; + + // dwarf::DW_LLE_end_of_list_entry is 0 and indicates the end of the list. + while (auto Kind = + static_cast<dwarf::LocationListEntry>(Data.getU8(Offset))) { + + Entry E; + E.Kind = Kind; + switch (Kind) { + case dwarf::DW_LLE_startx_length: + E.Value0 = Data.getULEB128(Offset); + // Pre-DWARF 5 has different interpretation of the length field. We have + // to support both pre- and standartized styles for the compatibility. + if (Version < 5) + E.Value1 = Data.getU32(Offset); + else + E.Value1 = Data.getULEB128(Offset); + break; + case dwarf::DW_LLE_start_length: + E.Value0 = Data.getAddress(Offset); + E.Value1 = Data.getULEB128(Offset); + break; + case dwarf::DW_LLE_offset_pair: + E.Value0 = Data.getULEB128(Offset); + E.Value1 = Data.getULEB128(Offset); + break; + case dwarf::DW_LLE_base_address: + E.Value0 = Data.getAddress(Offset); + break; + default: + WithColor::error() << "dumping support for LLE of kind " << (int)Kind + << " not implemented\n"; + return None; + } + + if (Kind != dwarf::DW_LLE_base_address) { + unsigned Bytes = + Version >= 5 ? Data.getULEB128(Offset) : Data.getU16(Offset); + // A single location description describing the location of the object... + StringRef str = Data.getData().substr(*Offset, Bytes); + *Offset += Bytes; + E.Loc.resize(str.size()); + llvm::copy(str, E.Loc.begin()); + } + + LL.Entries.push_back(std::move(E)); + } + return LL; +} + +void DWARFDebugLoclists::parse(DataExtractor data, unsigned Version) { + IsLittleEndian = data.isLittleEndian(); + AddressSize = data.getAddressSize(); + + uint32_t Offset = 0; + while (data.isValidOffset(Offset)) { + if (auto LL = parseOneLocationList(data, &Offset, Version)) + Locations.push_back(std::move(*LL)); + else + return; + } +} + +DWARFDebugLoclists::LocationList const * +DWARFDebugLoclists::getLocationListAtOffset(uint64_t Offset) const { + auto It = std::lower_bound( + Locations.begin(), Locations.end(), Offset, + [](const LocationList &L, uint64_t Offset) { return L.Offset < Offset; }); + if (It != Locations.end() && It->Offset == Offset) + return &(*It); + return nullptr; +} + +void DWARFDebugLoclists::LocationList::dump(raw_ostream &OS, uint64_t BaseAddr, + bool IsLittleEndian, + unsigned AddressSize, + const MCRegisterInfo *MRI, + unsigned Indent) const { + for (const Entry &E : Entries) { + switch (E.Kind) { + case dwarf::DW_LLE_startx_length: + OS << '\n'; + OS.indent(Indent); + OS << "Addr idx " << E.Value0 << " (w/ length " << E.Value1 << "): "; + break; + case dwarf::DW_LLE_start_length: + OS << '\n'; + OS.indent(Indent); + OS << format("[0x%*.*" PRIx64 ", 0x%*.*" PRIx64 "): ", AddressSize * 2, + AddressSize * 2, E.Value0, AddressSize * 2, AddressSize * 2, + E.Value0 + E.Value1); + break; + case dwarf::DW_LLE_offset_pair: + OS << '\n'; + OS.indent(Indent); + OS << format("[0x%*.*" PRIx64 ", 0x%*.*" PRIx64 "): ", AddressSize * 2, + AddressSize * 2, BaseAddr + E.Value0, AddressSize * 2, + AddressSize * 2, BaseAddr + E.Value1); + break; + case dwarf::DW_LLE_base_address: + BaseAddr = E.Value0; + break; + default: + llvm_unreachable("unreachable locations list kind"); + } + + dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI); + } +} + +void DWARFDebugLoclists::dump(raw_ostream &OS, uint64_t BaseAddr, + const MCRegisterInfo *MRI, + Optional<uint64_t> Offset) const { + auto DumpLocationList = [&](const LocationList &L) { + OS << format("0x%8.8x: ", L.Offset); + L.dump(OS, BaseAddr, IsLittleEndian, AddressSize, MRI, /*Indent=*/12); + OS << "\n\n"; + }; + + if (Offset) { + if (auto *L = getLocationListAtOffset(*Offset)) + DumpLocationList(*L); + return; + } + + for (const LocationList &L : Locations) { + DumpLocationList(L); + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp new file mode 100644 index 000000000000..6d789c3027a5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -0,0 +1,101 @@ +//===- DWARFDebugMacro.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/DWARFDebugMacro.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; + +void DWARFDebugMacro::dump(raw_ostream &OS) const { + unsigned IndLevel = 0; + for (const Entry &E : Macros) { + // There should not be DW_MACINFO_end_file when IndLevel is Zero. However, + // this check handles the case of corrupted ".debug_macinfo" section. + if (IndLevel > 0) + IndLevel -= (E.Type == DW_MACINFO_end_file); + // Print indentation. + for (unsigned I = 0; I < IndLevel; I++) + OS << " "; + IndLevel += (E.Type == DW_MACINFO_start_file); + + WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + OS << " - lineno: " << E.Line; + OS << " macro: " << E.MacroStr; + break; + case DW_MACINFO_start_file: + OS << " - lineno: " << E.Line; + OS << " filenum: " << E.File; + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + OS << " - constant: " << E.ExtConstant; + OS << " string: " << E.ExtStr; + break; + } + OS << "\n"; + } +} + +void DWARFDebugMacro::parse(DataExtractor data) { + uint32_t Offset = 0; + while (data.isValidOffset(Offset)) { + // A macro list entry consists of: + Entry E; + // 1. Macinfo type + E.Type = data.getULEB128(&Offset); + + if (E.Type == 0) { + // Reached end of ".debug_macinfo" section. + return; + } + + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + // Push the corrupted entry to the list and halt parsing. + E.Type = DW_MACINFO_invalid; + Macros.push_back(E); + return; + case DW_MACINFO_define: + case DW_MACINFO_undef: + // 2. Source line + E.Line = data.getULEB128(&Offset); + // 3. Macro string + E.MacroStr = data.getCStr(&Offset); + break; + case DW_MACINFO_start_file: + // 2. Source line + E.Line = data.getULEB128(&Offset); + // 3. Source file id + E.File = data.getULEB128(&Offset); + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + // 2. Vendor extension constant + E.ExtConstant = data.getULEB128(&Offset); + // 3. Vendor extension string + E.ExtStr = data.getCStr(&Offset); + break; + } + + Macros.push_back(E); + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp new file mode 100644 index 000000000000..abd1ad59a9c1 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -0,0 +1,70 @@ +//===- DWARFDebugPubTable.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/DWARFDebugPubTable.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +DWARFDebugPubTable::DWARFDebugPubTable(const DWARFObject &Obj, + const DWARFSection &Sec, + bool LittleEndian, bool GnuStyle) + : GnuStyle(GnuStyle) { + DWARFDataExtractor PubNames(Obj, Sec, LittleEndian, 0); + uint32_t Offset = 0; + while (PubNames.isValidOffset(Offset)) { + Sets.push_back({}); + Set &SetData = Sets.back(); + + SetData.Length = PubNames.getU32(&Offset); + SetData.Version = PubNames.getU16(&Offset); + SetData.Offset = PubNames.getRelocatedValue(4, &Offset); + SetData.Size = PubNames.getU32(&Offset); + + while (Offset < Sec.Data.size()) { + uint32_t DieRef = PubNames.getU32(&Offset); + if (DieRef == 0) + break; + uint8_t IndexEntryValue = GnuStyle ? PubNames.getU8(&Offset) : 0; + StringRef Name = PubNames.getCStrRef(&Offset); + SetData.Entries.push_back( + {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + } + } +} + +void DWARFDebugPubTable::dump(raw_ostream &OS) const { + for (const Set &S : Sets) { + OS << "length = " << format("0x%08x", S.Length); + OS << " version = " << format("0x%04x", S.Version); + OS << " unit_offset = " << format("0x%08x", S.Offset); + OS << " unit_size = " << format("0x%08x", S.Size) << '\n'; + OS << (GnuStyle ? "Offset Linkage Kind Name\n" + : "Offset Name\n"); + + for (const Entry &E : S.Entries) { + OS << format("0x%8.8x ", E.SecOffset); + if (GnuStyle) { + StringRef EntryLinkage = + GDBIndexEntryLinkageString(E.Descriptor.Linkage); + StringRef EntryKind = dwarf::GDBIndexEntryKindString(E.Descriptor.Kind); + OS << format("%-8s", EntryLinkage.data()) << ' ' + << format("%-8s", EntryKind.data()) << ' '; + } + OS << '\"' << E.Name << "\"\n"; + } + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp new file mode 100644 index 000000000000..dfb913000a46 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp @@ -0,0 +1,96 @@ +//===- DWARFDebugRangesList.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/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +void DWARFDebugRangeList::clear() { + Offset = -1U; + AddressSize = 0; + Entries.clear(); +} + +Error DWARFDebugRangeList::extract(const DWARFDataExtractor &data, + uint32_t *offset_ptr) { + clear(); + if (!data.isValidOffset(*offset_ptr)) + return createStringError(errc::invalid_argument, + "invalid range list offset 0x%" PRIx32, *offset_ptr); + + AddressSize = data.getAddressSize(); + if (AddressSize != 4 && AddressSize != 8) + return createStringError(errc::invalid_argument, + "invalid address size: %" PRIu8, AddressSize); + Offset = *offset_ptr; + while (true) { + RangeListEntry Entry; + Entry.SectionIndex = -1ULL; + + uint32_t prev_offset = *offset_ptr; + Entry.StartAddress = data.getRelocatedAddress(offset_ptr); + Entry.EndAddress = + data.getRelocatedAddress(offset_ptr, &Entry.SectionIndex); + + // Check that both values were extracted correctly. + if (*offset_ptr != prev_offset + 2 * AddressSize) { + clear(); + return createStringError(errc::invalid_argument, + "invalid range list entry at offset 0x%" PRIx32, + prev_offset); + } + if (Entry.isEndOfListEntry()) + break; + Entries.push_back(Entry); + } + return Error::success(); +} + +void DWARFDebugRangeList::dump(raw_ostream &OS) const { + for (const RangeListEntry &RLE : Entries) { + const char *format_str = (AddressSize == 4 + ? "%08x %08" PRIx64 " %08" PRIx64 "\n" + : "%08x %016" PRIx64 " %016" PRIx64 "\n"); + OS << format(format_str, Offset, RLE.StartAddress, RLE.EndAddress); + } + OS << format("%08x <End of list>\n", Offset); +} + +DWARFAddressRangesVector DWARFDebugRangeList::getAbsoluteRanges( + llvm::Optional<SectionedAddress> BaseAddr) const { + DWARFAddressRangesVector Res; + for (const RangeListEntry &RLE : Entries) { + if (RLE.isBaseAddressSelectionEntry(AddressSize)) { + BaseAddr = {RLE.EndAddress, RLE.SectionIndex}; + continue; + } + + DWARFAddressRange E; + E.LowPC = RLE.StartAddress; + E.HighPC = RLE.EndAddress; + E.SectionIndex = RLE.SectionIndex; + // Base address of a range list entry is determined by the closest preceding + // base address selection entry in the same range list. It defaults to the + // base address of the compilation unit if there is no such entry. + if (BaseAddr) { + E.LowPC += BaseAddr->Address; + E.HighPC += BaseAddr->Address; + if (E.SectionIndex == -1ULL) + E.SectionIndex = BaseAddr->SectionIndex; + } + Res.push_back(E); + } + return Res; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp new file mode 100644 index 000000000000..60c6eb30857f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp @@ -0,0 +1,248 @@ +//===- 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/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +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: { + uint32_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError( + errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_base_addressx encoding at offset 0x%" PRIx32, + PreviousOffset); + break; + } + case dwarf::DW_RLE_startx_endx: + return createStringError(errc::not_supported, + "unsupported rnglists encoding DW_RLE_startx_endx at " + "offset 0x%" PRIx32, + *OffsetPtr - 1); + case dwarf::DW_RLE_startx_length: { + uint32_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError( + errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_startx_length encoding at offset 0x%" PRIx32, + PreviousOffset); + break; + } + case dwarf::DW_RLE_offset_pair: { + uint32_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError(errc::invalid_argument, + "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 createStringError(errc::invalid_argument, + "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 createStringError(errc::invalid_argument, + "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 createStringError(errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_start_length encoding at offset 0x%" PRIx32, + PreviousOffset); + break; + } + default: + return createStringError(errc::not_supported, + "unknown rnglists encoding 0x%" PRIx32 + " at offset 0x%" PRIx32, + uint32_t(Encoding), *OffsetPtr - 1); + } + + EntryKind = Encoding; + return Error::success(); +} + +DWARFAddressRangesVector +DWARFDebugRnglist::getAbsoluteRanges(llvm::Optional<SectionedAddress> BaseAddr, + DWARFUnit &U) 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_addressx) { + BaseAddr = U.getAddrOffsetSectionItem(RLE.Value0); + if (!BaseAddr) + BaseAddr = {RLE.Value0, -1ULL}; + continue; + } + 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; + case dwarf::DW_RLE_startx_length: { + auto Start = U.getAddrOffsetSectionItem(RLE.Value0); + if (!Start) + Start = {0, -1ULL}; + E.SectionIndex = Start->SectionIndex; + E.LowPC = Start->Address; + 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, + llvm::function_ref<Optional<SectionedAddress>(uint32_t)> + LookupPooledAddress) 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_addressx: + case dwarf::DW_RLE_base_addressx: { + if (auto SA = LookupPooledAddress(Value0)) + CurrentBase = SA->Address; + else + CurrentBase = Value0; + if (!DumpOpts.Verbose) + return; + OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Value0); + 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; + case dwarf::DW_RLE_startx_length: { + PrintRawEntry(OS, *this, AddrSize, DumpOpts); + uint64_t Start = 0; + if (auto SA = LookupPooledAddress(Value0)) + Start = SA->Address; + DWARFAddressRange(Start, Start + Value1).dump(OS, AddrSize, DumpOpts); + break; + } 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 new file mode 100644 index 000000000000..81ef0c8c7aec --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -0,0 +1,712 @@ +//===- DWARFDie.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/DWARFDie.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" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#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> +#include <cinttypes> +#include <cstdint> +#include <string> +#include <utility> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +static void dumpApplePropertyAttribute(raw_ostream &OS, uint64_t Val) { + OS << " ("; + do { + uint64_t Shift = countTrailingZeros(Val); + assert(Shift < 64 && "undefined behavior"); + uint64_t Bit = 1ULL << Shift; + auto PropName = ApplePropertyString(Bit); + if (!PropName.empty()) + OS << PropName; + else + OS << format("DW_APPLE_PROPERTY_0x%" PRIx64, Bit); + if (!(Val ^= Bit)) + break; + OS << ", "; + } while (true); + OS << ")"; +} + +static void dumpRanges(const DWARFObject &Obj, raw_ostream &OS, + const DWARFAddressRangesVector &Ranges, + unsigned AddressSize, unsigned Indent, + const DIDumpOptions &DumpOpts) { + if (!DumpOpts.ShowAddresses) + return; + + ArrayRef<SectionName> SectionNames; + if (DumpOpts.Verbose) + SectionNames = Obj.getSectionNames(); + + for (const DWARFAddressRange &R : Ranges) { + OS << '\n'; + OS.indent(Indent); + R.dump(OS, AddressSize); + + DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, R.SectionIndex); + } +} + +static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, + DWARFUnit *U, unsigned Indent, + DIDumpOptions DumpOpts) { + DWARFContext &Ctx = U->getContext(); + const DWARFObject &Obj = Ctx.getDWARFObj(); + const MCRegisterInfo *MRI = Ctx.getRegisterInfo(); + if (FormValue.isFormClass(DWARFFormValue::FC_Block) || + FormValue.isFormClass(DWARFFormValue::FC_Exprloc)) { + ArrayRef<uint8_t> Expr = *FormValue.getAsBlock(); + DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), + Ctx.isLittleEndian(), 0); + DWARFExpression(Data, U->getVersion(), U->getAddressByteSize()) + .print(OS, MRI); + return; + } + + FormValue.dump(OS, DumpOpts); + if (FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { + uint32_t Offset = *FormValue.getAsSectionOffset(); + if (!U->isDWOUnit() && !U->getLocSection()->Data.empty()) { + DWARFDebugLoc DebugLoc; + DWARFDataExtractor Data(Obj, *U->getLocSection(), Ctx.isLittleEndian(), + Obj.getAddressSize()); + auto LL = DebugLoc.parseOneLocationList(Data, &Offset); + if (LL) { + uint64_t BaseAddr = 0; + if (Optional<SectionedAddress> BA = U->getBaseAddress()) + BaseAddr = BA->Address; + LL->dump(OS, Ctx.isLittleEndian(), Obj.getAddressSize(), MRI, BaseAddr, + Indent); + } else + OS << "error extracting location list."; + return; + } + + bool UseLocLists = !U->isDWOUnit(); + StringRef LoclistsSectionData = + UseLocLists ? Obj.getLoclistsSection().Data : U->getLocSectionData(); + + if (!LoclistsSectionData.empty()) { + DataExtractor Data(LoclistsSectionData, Ctx.isLittleEndian(), + Obj.getAddressSize()); + + // Old-style location list were used in DWARF v4 (.debug_loc.dwo section). + // Modern locations list (.debug_loclists) are used starting from v5. + // Ideally we should take the version from the .debug_loclists section + // header, but using CU's version for simplicity. + auto LL = DWARFDebugLoclists::parseOneLocationList( + Data, &Offset, UseLocLists ? U->getVersion() : 4); + + uint64_t BaseAddr = 0; + if (Optional<SectionedAddress> BA = U->getBaseAddress()) + BaseAddr = BA->Address; + + if (LL) + LL->dump(OS, BaseAddr, Ctx.isLittleEndian(), Obj.getAddressSize(), MRI, + Indent); + else + OS << "error extracting location list."; + } + } +} + +/// Dump the name encoded in the type tag. +static void dumpTypeTagName(raw_ostream &OS, dwarf::Tag T) { + StringRef TagStr = TagString(T); + if (!TagStr.startswith("DW_TAG_") || !TagStr.endswith("_type")) + return; + OS << TagStr.substr(7, TagStr.size() - 12) << " "; +} + +static void dumpArrayType(raw_ostream &OS, const DWARFDie &D) { + Optional<uint64_t> Bound; + for (const DWARFDie &C : D.children()) + if (C.getTag() == DW_TAG_subrange_type) { + Optional<uint64_t> LB; + Optional<uint64_t> Count; + Optional<uint64_t> UB; + Optional<unsigned> DefaultLB; + if (Optional<DWARFFormValue> L = C.find(DW_AT_lower_bound)) + LB = L->getAsUnsignedConstant(); + if (Optional<DWARFFormValue> CountV = C.find(DW_AT_count)) + Count = CountV->getAsUnsignedConstant(); + if (Optional<DWARFFormValue> UpperV = C.find(DW_AT_upper_bound)) + UB = UpperV->getAsUnsignedConstant(); + if (Optional<DWARFFormValue> LV = + D.getDwarfUnit()->getUnitDIE().find(DW_AT_language)) + if (Optional<uint64_t> LC = LV->getAsUnsignedConstant()) + if ((DefaultLB = + LanguageLowerBound(static_cast<dwarf::SourceLanguage>(*LC)))) + if (LB && *LB == *DefaultLB) + LB = None; + if (!LB && !Count && !UB) + OS << "[]"; + else if (!LB && (Count || UB) && DefaultLB) + OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']'; + else { + OS << "[["; + if (LB) + OS << *LB; + else + OS << '?'; + OS << ", "; + if (Count) + if (LB) + OS << *LB + *Count; + else + OS << "? + " << *Count; + else if (UB) + OS << *UB + 1; + else + OS << '?'; + OS << ")]"; + } + } +} + +/// Recursively dump the DIE type name when applicable. +static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) { + if (!D.isValid()) + return; + + if (const char *Name = D.getName(DINameKind::LinkageName)) { + OS << Name; + return; + } + + // FIXME: We should have pretty printers per language. Currently we print + // everything as if it was C++ and fall back to the TAG type name. + const dwarf::Tag T = D.getTag(); + switch (T) { + case DW_TAG_array_type: + case DW_TAG_pointer_type: + case DW_TAG_ptr_to_member_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_subroutine_type: + break; + default: + dumpTypeTagName(OS, T); + } + + // Follow the DW_AT_type if possible. + DWARFDie TypeDie = D.getAttributeValueAsReferencedDie(DW_AT_type); + dumpTypeName(OS, TypeDie); + + switch (T) { + case DW_TAG_subroutine_type: { + if (!TypeDie) + OS << "void"; + OS << '('; + bool First = true; + for (const DWARFDie &C : D.children()) { + if (C.getTag() == DW_TAG_formal_parameter) { + if (!First) + OS << ", "; + First = false; + dumpTypeName(OS, C.getAttributeValueAsReferencedDie(DW_AT_type)); + } + } + OS << ')'; + break; + } + case DW_TAG_array_type: { + dumpArrayType(OS, D); + break; + } + case DW_TAG_pointer_type: + OS << '*'; + break; + case DW_TAG_ptr_to_member_type: + if (DWARFDie Cont = + D.getAttributeValueAsReferencedDie(DW_AT_containing_type)) { + dumpTypeName(OS << ' ', Cont); + OS << "::"; + } + OS << '*'; + break; + case DW_TAG_reference_type: + OS << '&'; + break; + case DW_TAG_rvalue_reference_type: + OS << "&&"; + break; + default: + break; + } +} + +static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, + uint32_t *OffsetPtr, dwarf::Attribute Attr, + dwarf::Form Form, unsigned Indent, + DIDumpOptions DumpOpts) { + if (!Die.isValid()) + return; + const char BaseIndent[] = " "; + OS << BaseIndent; + OS.indent(Indent + 2); + WithColor(OS, HighlightColor::Attribute) << formatv("{0}", Attr); + + if (DumpOpts.Verbose || DumpOpts.ShowForm) + OS << formatv(" [{0}]", Form); + + DWARFUnit *U = Die.getDwarfUnit(); + DWARFFormValue formValue(Form); + + if (!formValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, + U->getFormParams(), U)) + return; + + OS << "\t("; + + StringRef Name; + std::string File; + auto Color = HighlightColor::Enumerator; + if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { + Color = HighlightColor::String; + if (const auto *LT = U->getContext().getLineTableForUnit(U)) + if (LT->getFileNameByIndex( + formValue.getAsUnsignedConstant().getValue(), + U->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { + File = '"' + File + '"'; + Name = File; + } + } else if (Optional<uint64_t> Val = formValue.getAsUnsignedConstant()) + Name = AttributeValueString(Attr, *Val); + + if (!Name.empty()) + WithColor(OS, Color) << Name; + else if (Attr == DW_AT_decl_line || Attr == DW_AT_call_line) + OS << *formValue.getAsUnsignedConstant(); + else if (Attr == DW_AT_high_pc && !DumpOpts.ShowForm && !DumpOpts.Verbose && + formValue.getAsUnsignedConstant()) { + if (DumpOpts.ShowAddresses) { + // Print the actual address rather than the offset. + uint64_t LowPC, HighPC, Index; + if (Die.getLowAndHighPC(LowPC, HighPC, Index)) + OS << format("0x%016" PRIx64, HighPC); + else + formValue.dump(OS, DumpOpts); + } + } else if (Attr == DW_AT_location || Attr == DW_AT_frame_base || + Attr == DW_AT_data_member_location || + Attr == DW_AT_GNU_call_site_value) + dumpLocation(OS, formValue, U, sizeof(BaseIndent) + Indent + 4, DumpOpts); + else + formValue.dump(OS, DumpOpts); + + std::string Space = DumpOpts.ShowAddresses ? " " : ""; + + // We have dumped the attribute raw value. For some attributes + // having both the raw value and the pretty-printed value is + // interesting. These attributes are handled below. + if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin) { + if (const char *Name = + Die.getAttributeValueAsReferencedDie(formValue).getName( + DINameKind::LinkageName)) + OS << Space << "\"" << Name << '\"'; + } else if (Attr == DW_AT_type) { + OS << Space << "\""; + dumpTypeName(OS, Die.getAttributeValueAsReferencedDie(formValue)); + OS << '"'; + } else if (Attr == DW_AT_APPLE_property_attribute) { + if (Optional<uint64_t> OptVal = formValue.getAsUnsignedConstant()) + dumpApplePropertyAttribute(OS, *OptVal); + } else if (Attr == DW_AT_ranges) { + const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); + // For DW_FORM_rnglistx we need to dump the offset separately, since + // we have only dumped the index so far. + if (formValue.getForm() == DW_FORM_rnglistx) + if (auto RangeListOffset = + U->getRnglistOffset(*formValue.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"; +} + +bool DWARFDie::isSubprogramDIE() const { return getTag() == DW_TAG_subprogram; } + +bool DWARFDie::isSubroutineDIE() const { + auto Tag = getTag(); + return Tag == DW_TAG_subprogram || Tag == DW_TAG_inlined_subroutine; +} + +Optional<DWARFFormValue> DWARFDie::find(dwarf::Attribute Attr) const { + if (!isValid()) + return None; + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) + return AbbrevDecl->getAttributeValue(getOffset(), Attr, *U); + return None; +} + +Optional<DWARFFormValue> +DWARFDie::find(ArrayRef<dwarf::Attribute> Attrs) const { + if (!isValid()) + return None; + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) { + for (auto Attr : Attrs) { + if (auto Value = AbbrevDecl->getAttributeValue(getOffset(), Attr, *U)) + return Value; + } + } + return None; +} + +Optional<DWARFFormValue> +DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const { + 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; +} + +DWARFDie +DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const { + if (Optional<DWARFFormValue> F = find(Attr)) + return getAttributeValueAsReferencedDie(*F); + return DWARFDie(); +} + +DWARFDie +DWARFDie::getAttributeValueAsReferencedDie(const DWARFFormValue &V) const { + if (auto SpecRef = toReference(V)) { + if (auto SpecUnit = U->getUnitVector().getUnitForOffset(*SpecRef)) + return SpecUnit->getDIEForOffset(*SpecRef); + } + return DWARFDie(); +} + +Optional<uint64_t> DWARFDie::getRangesBaseAttribute() const { + return toSectionOffset(find({DW_AT_rnglists_base, DW_AT_GNU_ranges_base})); +} + +Optional<uint64_t> DWARFDie::getHighPC(uint64_t LowPC) const { + if (auto FormValue = find(DW_AT_high_pc)) { + if (auto Address = FormValue->getAsAddress()) { + // High PC is an address. + return Address; + } + if (auto Offset = FormValue->getAsUnsignedConstant()) { + // High PC is an offset from LowPC. + return LowPC + *Offset; + } + } + return None; +} + +bool DWARFDie::getLowAndHighPC(uint64_t &LowPC, uint64_t &HighPC, + uint64_t &SectionIndex) const { + auto F = find(DW_AT_low_pc); + auto LowPcAddr = toSectionedAddress(F); + if (!LowPcAddr) + return false; + if (auto HighPcAddr = getHighPC(LowPcAddr->Address)) { + LowPC = LowPcAddr->Address; + HighPC = *HighPcAddr; + SectionIndex = LowPcAddr->SectionIndex; + return true; + } + return false; +} + +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 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(); +} + +void DWARFDie::collectChildrenAddressRanges( + DWARFAddressRangesVector &Ranges) const { + if (isNULL()) + return; + if (isSubprogramDIE()) { + if (auto DIERangesOrError = getAddressRanges()) + Ranges.insert(Ranges.end(), DIERangesOrError.get().begin(), + DIERangesOrError.get().end()); + else + llvm::consumeError(DIERangesOrError.takeError()); + } + + for (auto Child : children()) + Child.collectChildrenAddressRanges(Ranges); +} + +bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { + 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; +} + +const char *DWARFDie::getSubroutineName(DINameKind Kind) const { + if (!isSubroutineDIE()) + return nullptr; + return getName(Kind); +} + +const char *DWARFDie::getName(DINameKind Kind) const { + if (!isValid() || Kind == DINameKind::None) + return nullptr; + // Try to get mangled name only if it was asked for. + if (Kind == DINameKind::LinkageName) { + if (auto Name = dwarf::toString( + findRecursively({DW_AT_MIPS_linkage_name, DW_AT_linkage_name}), + nullptr)) + return Name; + } + if (auto Name = dwarf::toString(findRecursively(DW_AT_name), nullptr)) + return Name; + return nullptr; +} + +uint64_t DWARFDie::getDeclLine() const { + return toUnsigned(findRecursively(DW_AT_decl_line), 0); +} + +void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, + uint32_t &CallColumn, + uint32_t &CallDiscriminator) const { + CallFile = toUnsigned(find(DW_AT_call_file), 0); + CallLine = toUnsigned(find(DW_AT_call_line), 0); + CallColumn = toUnsigned(find(DW_AT_call_column), 0); + CallDiscriminator = toUnsigned(find(DW_AT_GNU_discriminator), 0); +} + +/// Helper to dump a DIE with all of its parents, but no siblings. +static unsigned dumpParentChain(DWARFDie Die, raw_ostream &OS, unsigned Indent, + DIDumpOptions DumpOpts) { + if (!Die) + return Indent; + Indent = dumpParentChain(Die.getParent(), OS, Indent, DumpOpts); + Die.dump(OS, Indent, DumpOpts); + return Indent + 2; +} + +void DWARFDie::dump(raw_ostream &OS, unsigned Indent, + DIDumpOptions DumpOpts) const { + if (!isValid()) + return; + DWARFDataExtractor debug_info_data = U->getDebugInfoExtractor(); + const uint32_t Offset = getOffset(); + uint32_t offset = Offset; + if (DumpOpts.ShowParents) { + 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, HighlightColor::Address).get() + << format("\n0x%8.8x: ", Offset); + + if (abbrCode) { + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) { + WithColor(OS, HighlightColor::Tag).get().indent(Indent) + << formatv("{0}", getTag()); + if (DumpOpts.Verbose) + OS << format(" [%u] %c", abbrCode, + AbbrevDecl->hasChildren() ? '*' : ' '); + OS << '\n'; + + // Dump all data in the DIE for the attributes. + for (const auto &AttrSpec : AbbrevDecl->attributes()) { + if (AttrSpec.Form == DW_FORM_implicit_const) { + // We are dumping .debug_info section , + // implicit_const attribute values are not really stored here, + // but in .debug_abbrev section. So we just skip such attrs. + continue; + } + dumpAttribute(OS, *this, &offset, AttrSpec.Attr, AttrSpec.Form, + Indent, DumpOpts); + } + + 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, ChildDumpOpts); + child = child.getSibling(); + } + } + } else { + OS << "Abbreviation code not found in 'debug_abbrev' class for code: " + << abbrCode << '\n'; + } + } else { + OS.indent(Indent) << "NULL\n"; + } + } +} + +LLVM_DUMP_METHOD void DWARFDie::dump() const { dump(llvm::errs(), 0); } + +DWARFDie DWARFDie::getParent() const { + if (isValid()) + return U->getParent(Die); + return DWARFDie(); +} + +DWARFDie DWARFDie::getSibling() const { + if (isValid()) + return U->getSibling(Die); + 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)); +} + +DWARFDie::attribute_iterator::attribute_iterator(DWARFDie D, bool End) + : Die(D), AttrValue(0), Index(0) { + auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); + assert(AbbrDecl && "Must have abbreviation declaration"); + if (End) { + // This is the end iterator so we set the index to the attribute count. + Index = AbbrDecl->getNumAttributes(); + } else { + // This is the begin iterator so we extract the value for this->Index. + AttrValue.Offset = D.getOffset() + AbbrDecl->getCodeByteSize(); + updateForIndex(*AbbrDecl, 0); + } +} + +void DWARFDie::attribute_iterator::updateForIndex( + const DWARFAbbreviationDeclaration &AbbrDecl, uint32_t I) { + Index = I; + // AbbrDecl must be valid before calling this function. + auto NumAttrs = AbbrDecl.getNumAttributes(); + if (Index < NumAttrs) { + AttrValue.Attr = AbbrDecl.getAttrByIndex(Index); + // Add the previous byte size of any previous attribute value. + AttrValue.Offset += AttrValue.ByteSize; + AttrValue.Value.setForm(AbbrDecl.getFormByIndex(Index)); + uint32_t ParseOffset = AttrValue.Offset; + auto U = Die.getDwarfUnit(); + assert(U && "Die must have valid DWARF unit"); + bool b = AttrValue.Value.extractValue(U->getDebugInfoExtractor(), + &ParseOffset, U->getFormParams(), U); + (void)b; + assert(b && "extractValue cannot fail on fully parsed DWARF"); + AttrValue.ByteSize = ParseOffset - AttrValue.Offset; + } else { + assert(Index == NumAttrs && "Indexes should be [0, NumAttrs) only"); + AttrValue.clear(); + } +} + +DWARFDie::attribute_iterator &DWARFDie::attribute_iterator::operator++() { + if (auto AbbrDecl = Die.getAbbreviationDeclarationPtr()) + updateForIndex(*AbbrDecl, Index + 1); + return *this; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp new file mode 100644 index 000000000000..2df4456053fb --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -0,0 +1,276 @@ +//===-- DWARFExpression.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/DWARFExpression.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/Format.h" +#include <cassert> +#include <cstdint> +#include <vector> + +using namespace llvm; +using namespace dwarf; + +namespace llvm { + +typedef std::vector<DWARFExpression::Operation::Description> DescVector; + +static DescVector getDescriptions() { + DescVector Descriptions; + typedef DWARFExpression::Operation Op; + typedef Op::Description Desc; + + Descriptions.resize(0xff); + Descriptions[DW_OP_addr] = Desc(Op::Dwarf2, Op::SizeAddr); + Descriptions[DW_OP_deref] = Desc(Op::Dwarf2); + Descriptions[DW_OP_const1u] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_const1s] = Desc(Op::Dwarf2, Op::SignedSize1); + Descriptions[DW_OP_const2u] = Desc(Op::Dwarf2, Op::Size2); + Descriptions[DW_OP_const2s] = Desc(Op::Dwarf2, Op::SignedSize2); + Descriptions[DW_OP_const4u] = Desc(Op::Dwarf2, Op::Size4); + Descriptions[DW_OP_const4s] = Desc(Op::Dwarf2, Op::SignedSize4); + Descriptions[DW_OP_const8u] = Desc(Op::Dwarf2, Op::Size8); + Descriptions[DW_OP_const8s] = Desc(Op::Dwarf2, Op::SignedSize8); + Descriptions[DW_OP_constu] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_consts] = Desc(Op::Dwarf2, Op::SignedSizeLEB); + Descriptions[DW_OP_dup] = Desc(Op::Dwarf2); + Descriptions[DW_OP_drop] = Desc(Op::Dwarf2); + Descriptions[DW_OP_over] = Desc(Op::Dwarf2); + Descriptions[DW_OP_pick] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_swap] = Desc(Op::Dwarf2); + Descriptions[DW_OP_rot] = Desc(Op::Dwarf2); + Descriptions[DW_OP_xderef] = Desc(Op::Dwarf2); + Descriptions[DW_OP_abs] = Desc(Op::Dwarf2); + Descriptions[DW_OP_and] = Desc(Op::Dwarf2); + Descriptions[DW_OP_div] = Desc(Op::Dwarf2); + Descriptions[DW_OP_minus] = Desc(Op::Dwarf2); + Descriptions[DW_OP_mod] = Desc(Op::Dwarf2); + Descriptions[DW_OP_mul] = Desc(Op::Dwarf2); + Descriptions[DW_OP_neg] = Desc(Op::Dwarf2); + Descriptions[DW_OP_not] = Desc(Op::Dwarf2); + Descriptions[DW_OP_or] = Desc(Op::Dwarf2); + Descriptions[DW_OP_plus] = Desc(Op::Dwarf2); + Descriptions[DW_OP_plus_uconst] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_shl] = Desc(Op::Dwarf2); + Descriptions[DW_OP_shr] = Desc(Op::Dwarf2); + Descriptions[DW_OP_shra] = Desc(Op::Dwarf2); + Descriptions[DW_OP_xor] = Desc(Op::Dwarf2); + Descriptions[DW_OP_skip] = Desc(Op::Dwarf2, Op::SignedSize2); + Descriptions[DW_OP_bra] = Desc(Op::Dwarf2, Op::SignedSize2); + Descriptions[DW_OP_eq] = Desc(Op::Dwarf2); + Descriptions[DW_OP_ge] = Desc(Op::Dwarf2); + Descriptions[DW_OP_gt] = Desc(Op::Dwarf2); + Descriptions[DW_OP_le] = Desc(Op::Dwarf2); + Descriptions[DW_OP_lt] = Desc(Op::Dwarf2); + Descriptions[DW_OP_ne] = Desc(Op::Dwarf2); + for (uint16_t LA = DW_OP_lit0; LA <= DW_OP_lit31; ++LA) + Descriptions[LA] = Desc(Op::Dwarf2); + for (uint16_t LA = DW_OP_reg0; LA <= DW_OP_reg31; ++LA) + Descriptions[LA] = Desc(Op::Dwarf2); + for (uint16_t LA = DW_OP_breg0; LA <= DW_OP_breg31; ++LA) + Descriptions[LA] = Desc(Op::Dwarf2, Op::SignedSizeLEB); + Descriptions[DW_OP_regx] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_fbreg] = Desc(Op::Dwarf2, Op::SignedSizeLEB); + Descriptions[DW_OP_bregx] = Desc(Op::Dwarf2, Op::SizeLEB, Op::SignedSizeLEB); + Descriptions[DW_OP_piece] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_deref_size] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_xderef_size] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_nop] = Desc(Op::Dwarf2); + Descriptions[DW_OP_push_object_address] = Desc(Op::Dwarf3); + Descriptions[DW_OP_call2] = Desc(Op::Dwarf3, Op::Size2); + Descriptions[DW_OP_call4] = Desc(Op::Dwarf3, Op::Size4); + Descriptions[DW_OP_call_ref] = Desc(Op::Dwarf3, Op::SizeRefAddr); + Descriptions[DW_OP_form_tls_address] = Desc(Op::Dwarf3); + Descriptions[DW_OP_call_frame_cfa] = Desc(Op::Dwarf3); + Descriptions[DW_OP_bit_piece] = Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeLEB); + Descriptions[DW_OP_implicit_value] = + Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeBlock); + Descriptions[DW_OP_stack_value] = Desc(Op::Dwarf3); + Descriptions[DW_OP_GNU_push_tls_address] = Desc(Op::Dwarf3); + Descriptions[DW_OP_addrx] = Desc(Op::Dwarf4, Op::SizeLEB); + Descriptions[DW_OP_GNU_addr_index] = Desc(Op::Dwarf4, Op::SizeLEB); + Descriptions[DW_OP_GNU_const_index] = Desc(Op::Dwarf4, Op::SizeLEB); + return Descriptions; +} + +static DWARFExpression::Operation::Description getOpDesc(unsigned OpCode) { + // FIXME: Make this constexpr once all compilers are smart enough to do it. + static DescVector Descriptions = getDescriptions(); + // Handle possible corrupted or unsupported operation. + if (OpCode >= Descriptions.size()) + return {}; + return Descriptions[OpCode]; +} + +static uint8_t getRefAddrSize(uint8_t AddrSize, uint16_t Version) { + return (Version == 2) ? AddrSize : 4; +} + +bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, + uint8_t AddressSize, uint32_t Offset) { + Opcode = Data.getU8(&Offset); + + Desc = getOpDesc(Opcode); + if (Desc.Version == Operation::DwarfNA) { + EndOffset = Offset; + return false; + } + + for (unsigned Operand = 0; Operand < 2; ++Operand) { + unsigned Size = Desc.Op[Operand]; + unsigned Signed = Size & Operation::SignBit; + + if (Size == Operation::SizeNA) + break; + + switch (Size & ~Operation::SignBit) { + case Operation::Size1: + Operands[Operand] = Data.getU8(&Offset); + if (Signed) + Operands[Operand] = (int8_t)Operands[Operand]; + break; + case Operation::Size2: + Operands[Operand] = Data.getU16(&Offset); + if (Signed) + Operands[Operand] = (int16_t)Operands[Operand]; + break; + case Operation::Size4: + Operands[Operand] = Data.getU32(&Offset); + if (Signed) + Operands[Operand] = (int32_t)Operands[Operand]; + break; + case Operation::Size8: + Operands[Operand] = Data.getU64(&Offset); + break; + case Operation::SizeAddr: + if (AddressSize == 8) { + Operands[Operand] = Data.getU64(&Offset); + } else { + assert(AddressSize == 4); + Operands[Operand] = Data.getU32(&Offset); + } + break; + case Operation::SizeRefAddr: + if (getRefAddrSize(AddressSize, Version) == 8) { + Operands[Operand] = Data.getU64(&Offset); + } else { + assert(getRefAddrSize(AddressSize, Version) == 4); + Operands[Operand] = Data.getU32(&Offset); + } + break; + case Operation::SizeLEB: + if (Signed) + Operands[Operand] = Data.getSLEB128(&Offset); + else + Operands[Operand] = Data.getULEB128(&Offset); + break; + case Operation::SizeBlock: + // We need a size, so this cannot be the first operand + if (Operand == 0) + return false; + // Store the offset of the block as the value. + Operands[Operand] = Offset; + Offset += Operands[Operand - 1]; + break; + default: + llvm_unreachable("Unknown DWARFExpression Op size"); + } + } + + EndOffset = Offset; + return true; +} + +static bool prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, + uint64_t Operands[2], + const MCRegisterInfo *MRI, bool isEH) { + if (!MRI) + return false; + + uint64_t DwarfRegNum; + unsigned OpNum = 0; + + if (Opcode == DW_OP_bregx || Opcode == DW_OP_regx) + DwarfRegNum = Operands[OpNum++]; + else if (Opcode >= DW_OP_breg0 && Opcode < DW_OP_bregx) + DwarfRegNum = Opcode - DW_OP_breg0; + else + DwarfRegNum = Opcode - DW_OP_reg0; + + int LLVMRegNum = MRI->getLLVMRegNum(DwarfRegNum, isEH); + if (LLVMRegNum >= 0) { + if (const char *RegName = MRI->getName(LLVMRegNum)) { + if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) || + Opcode == DW_OP_bregx) + OS << format(" %s%+" PRId64, RegName, Operands[OpNum]); + else + OS << ' ' << RegName; + return true; + } + } + + return false; +} + +bool DWARFExpression::Operation::print(raw_ostream &OS, + const DWARFExpression *Expr, + const MCRegisterInfo *RegInfo, + bool isEH) { + if (Error) { + OS << "<decoding error>"; + return false; + } + + StringRef Name = OperationEncodingString(Opcode); + assert(!Name.empty() && "DW_OP has no name!"); + OS << Name; + + if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) || + (Opcode >= DW_OP_reg0 && Opcode <= DW_OP_reg31) || + Opcode == DW_OP_bregx || Opcode == DW_OP_regx) + if (prettyPrintRegisterOp(OS, Opcode, Operands, RegInfo, isEH)) + return true; + + for (unsigned Operand = 0; Operand < 2; ++Operand) { + unsigned Size = Desc.Op[Operand]; + unsigned Signed = Size & Operation::SignBit; + + if (Size == Operation::SizeNA) + break; + + if (Size == Operation::SizeBlock) { + uint32_t Offset = Operands[Operand]; + for (unsigned i = 0; i < Operands[Operand - 1]; ++i) + OS << format(" 0x%02x", Expr->Data.getU8(&Offset)); + } else { + if (Signed) + OS << format(" %+" PRId64, (int64_t)Operands[Operand]); + else + OS << format(" 0x%" PRIx64, Operands[Operand]); + } + } + return true; +} + +void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo, + bool IsEH) const { + for (auto &Op : *this) { + if (!Op.print(OS, this, RegInfo, IsEH)) { + uint32_t FailOffset = Op.getEndOffset(); + while (FailOffset < Data.getData().size()) + OS << format(" %02x", Data.getU8(&FailOffset)); + return; + } + if (Op.getEndOffset() < Data.getData().size()) + OS << ", "; + } +} + +} // namespace llvm diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp new file mode 100644 index 000000000000..7719fea63120 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -0,0 +1,679 @@ +//===- DWARFFormValue.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/DWARFFormValue.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#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> +#include <limits> + +using namespace llvm; +using namespace dwarf; + +static const DWARFFormValue::FormClass DWARF5FormClasses[] = { + DWARFFormValue::FC_Unknown, // 0x0 + DWARFFormValue::FC_Address, // 0x01 DW_FORM_addr + DWARFFormValue::FC_Unknown, // 0x02 unused + DWARFFormValue::FC_Block, // 0x03 DW_FORM_block2 + DWARFFormValue::FC_Block, // 0x04 DW_FORM_block4 + DWARFFormValue::FC_Constant, // 0x05 DW_FORM_data2 + // --- These can be FC_SectionOffset in DWARF3 and below: + DWARFFormValue::FC_Constant, // 0x06 DW_FORM_data4 + DWARFFormValue::FC_Constant, // 0x07 DW_FORM_data8 + // --- + DWARFFormValue::FC_String, // 0x08 DW_FORM_string + DWARFFormValue::FC_Block, // 0x09 DW_FORM_block + DWARFFormValue::FC_Block, // 0x0a DW_FORM_block1 + DWARFFormValue::FC_Constant, // 0x0b DW_FORM_data1 + DWARFFormValue::FC_Flag, // 0x0c DW_FORM_flag + DWARFFormValue::FC_Constant, // 0x0d DW_FORM_sdata + DWARFFormValue::FC_String, // 0x0e DW_FORM_strp + DWARFFormValue::FC_Constant, // 0x0f DW_FORM_udata + DWARFFormValue::FC_Reference, // 0x10 DW_FORM_ref_addr + DWARFFormValue::FC_Reference, // 0x11 DW_FORM_ref1 + DWARFFormValue::FC_Reference, // 0x12 DW_FORM_ref2 + DWARFFormValue::FC_Reference, // 0x13 DW_FORM_ref4 + DWARFFormValue::FC_Reference, // 0x14 DW_FORM_ref8 + DWARFFormValue::FC_Reference, // 0x15 DW_FORM_ref_udata + DWARFFormValue::FC_Indirect, // 0x16 DW_FORM_indirect + DWARFFormValue::FC_SectionOffset, // 0x17 DW_FORM_sec_offset + DWARFFormValue::FC_Exprloc, // 0x18 DW_FORM_exprloc + DWARFFormValue::FC_Flag, // 0x19 DW_FORM_flag_present + 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 + +}; + +bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, + uint32_t *OffsetPtr, + const dwarf::FormParams Params) { + bool Indirect = false; + do { + switch (Form) { + // Blocks of inlined data that have a length field and the data bytes + // inlined in the .debug_info. + case DW_FORM_exprloc: + case DW_FORM_block: { + uint64_t size = DebugInfoData.getULEB128(OffsetPtr); + *OffsetPtr += size; + return true; + } + case DW_FORM_block1: { + uint8_t size = DebugInfoData.getU8(OffsetPtr); + *OffsetPtr += size; + return true; + } + case DW_FORM_block2: { + uint16_t size = DebugInfoData.getU16(OffsetPtr); + *OffsetPtr += size; + return true; + } + case DW_FORM_block4: { + uint32_t size = DebugInfoData.getU32(OffsetPtr); + *OffsetPtr += size; + return true; + } + + // Inlined NULL terminated C-strings. + case DW_FORM_string: + DebugInfoData.getCStr(OffsetPtr); + return true; + + case DW_FORM_addr: + case DW_FORM_ref_addr: + case DW_FORM_flag_present: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_data16: + case DW_FORM_flag: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + case DW_FORM_ref_sup4: + case DW_FORM_ref_sup8: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx4: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx4: + case DW_FORM_sec_offset: + case DW_FORM_strp: + case DW_FORM_strp_sup: + case DW_FORM_line_strp: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + if (Optional<uint8_t> FixedSize = + dwarf::getFixedFormByteSize(Form, Params)) { + *OffsetPtr += *FixedSize; + return true; + } + return false; + + // signed or unsigned LEB 128 values. + case DW_FORM_sdata: + DebugInfoData.getSLEB128(OffsetPtr); + return true; + + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_strx: + case DW_FORM_addrx: + case DW_FORM_loclistx: + case DW_FORM_rnglistx: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + DebugInfoData.getULEB128(OffsetPtr); + return true; + + case DW_FORM_indirect: + Indirect = true; + Form = static_cast<dwarf::Form>(DebugInfoData.getULEB128(OffsetPtr)); + break; + + default: + return false; + } + } while (Indirect); + return true; +} + +bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { + // First, check DWARF5 form classes. + if (Form < makeArrayRef(DWARF5FormClasses).size() && + DWARF5FormClasses[Form] == FC) + return true; + // Check more forms from extensions and proposals. + switch (Form) { + 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: + return (FC == FC_String); + 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_[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_line_strp) && + FC == FC_SectionOffset; +} + +bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, + 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; + Value.data = nullptr; + // Read the value for the form into value and follow and DW_FORM_indirect + // instances we run into + do { + Indirect = false; + switch (Form) { + case DW_FORM_addr: + case DW_FORM_ref_addr: { + uint16_t Size = + (Form == DW_FORM_addr) ? FP.AddrSize : FP.getRefAddrByteSize(); + Value.uval = Data.getRelocatedValue(Size, OffsetPtr, &Value.SectionIndex); + break; + } + case DW_FORM_exprloc: + case DW_FORM_block: + Value.uval = Data.getULEB128(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_block1: + Value.uval = Data.getU8(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_block2: + Value.uval = Data.getU16(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_block4: + Value.uval = Data.getU32(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_strx1: + case DW_FORM_addrx1: + Value.uval = Data.getU8(OffsetPtr); + break; + case DW_FORM_data2: + case DW_FORM_ref2: + case DW_FORM_strx2: + case DW_FORM_addrx2: + Value.uval = Data.getU16(OffsetPtr); + break; + case DW_FORM_strx3: + Value.uval = Data.getU24(OffsetPtr); + break; + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_ref_sup4: + case DW_FORM_strx4: + case DW_FORM_addrx4: + Value.uval = Data.getRelocatedValue(4, OffsetPtr); + break; + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sup8: + Value.uval = Data.getU64(OffsetPtr); + break; + case DW_FORM_data16: + // Treat this like a 16-byte block. + Value.uval = 16; + IsBlock = true; + break; + case DW_FORM_sdata: + Value.sval = Data.getSLEB128(OffsetPtr); + break; + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_rnglistx: + Value.uval = Data.getULEB128(OffsetPtr); + break; + case DW_FORM_string: + Value.cstr = Data.getCStr(OffsetPtr); + break; + case DW_FORM_indirect: + Form = static_cast<dwarf::Form>(Data.getULEB128(OffsetPtr)); + Indirect = true; + break; + case DW_FORM_strp: + case DW_FORM_sec_offset: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + case DW_FORM_line_strp: + case DW_FORM_strp_sup: { + Value.uval = + Data.getRelocatedValue(FP.getDwarfOffsetByteSize(), OffsetPtr); + break; + } + case DW_FORM_flag_present: + Value.uval = 1; + break; + case DW_FORM_ref_sig8: + Value.uval = Data.getU64(OffsetPtr); + break; + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + case DW_FORM_addrx: + case DW_FORM_strx: + Value.uval = Data.getULEB128(OffsetPtr); + break; + default: + // DWARFFormValue::skipValue() will have caught this and caused all + // DWARF DIEs to fail to be parsed, so this code is not be reachable. + llvm_unreachable("unsupported form"); + } + } while (Indirect); + + if (IsBlock) { + StringRef Str = Data.getData().substr(*OffsetPtr, Value.uval); + Value.data = nullptr; + if (!Str.empty()) { + Value.data = reinterpret_cast<const uint8_t *>(Str.data()); + *OffsetPtr += Value.uval; + } + } + + return true; +} + +void DWARFFormValue::dumpSectionedAddress(raw_ostream &OS, + DIDumpOptions DumpOpts, + SectionedAddress SA) const { + OS << format("0x%016" PRIx64, SA.Address); + dumpAddressSection(U->getContext().getDWARFObj(), OS, DumpOpts, + SA.SectionIndex); +} + +void DWARFFormValue::dumpAddressSection(const DWARFObject &Obj, raw_ostream &OS, + DIDumpOptions DumpOpts, + uint64_t SectionIndex) { + if (!DumpOpts.Verbose || SectionIndex == -1ULL) + return; + ArrayRef<SectionName> SectionNames = Obj.getSectionNames(); + const auto &SecRef = SectionNames[SectionIndex]; + + OS << " \"" << SecRef.Name << '\"'; + + // Print section index if name is not unique. + if (!SecRef.IsNameUnique) + OS << format(" [%" PRIu64 "]", SectionIndex); +} + +void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + uint64_t UValue = Value.uval; + bool CURelativeOffset = false; + raw_ostream &AddrOS = DumpOpts.ShowAddresses + ? WithColor(OS, HighlightColor::Address).get() + : nulls(); + switch (Form) { + case DW_FORM_addr: + dumpSectionedAddress(AddrOS, DumpOpts, {Value.uval, Value.SectionIndex}); + break; + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: + case DW_FORM_GNU_addr_index: { + Optional<SectionedAddress> A = U->getAddrOffsetSectionItem(UValue); + if (!A || DumpOpts.Verbose) + AddrOS << format("indexed (%8.8x) address = ", (uint32_t)UValue); + if (U == nullptr) + OS << "<invalid dwarf unit>"; + else if (A) + dumpSectionedAddress(AddrOS, DumpOpts, *A); + else + OS << "<no .debug_addr section>"; + break; + } + case DW_FORM_flag_present: + OS << "true"; + break; + case DW_FORM_flag: + case DW_FORM_data1: + OS << format("0x%02x", (uint8_t)UValue); + break; + case DW_FORM_data2: + OS << format("0x%04x", (uint16_t)UValue); + break; + case DW_FORM_data4: + OS << format("0x%08x", (uint32_t)UValue); + break; + case DW_FORM_ref_sig8: + AddrOS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_data8: + OS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_data16: + OS << format_bytes(ArrayRef<uint8_t>(Value.data, 16), None, 16, 16); + break; + case DW_FORM_string: + OS << '"'; + OS.write_escaped(Value.cstr); + OS << '"'; + break; + case DW_FORM_exprloc: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (UValue > 0) { + switch (Form) { + case DW_FORM_exprloc: + case DW_FORM_block: + AddrOS << format("<0x%" PRIx64 "> ", UValue); + break; + case DW_FORM_block1: + AddrOS << format("<0x%2.2x> ", (uint8_t)UValue); + break; + case DW_FORM_block2: + AddrOS << format("<0x%4.4x> ", (uint16_t)UValue); + break; + case DW_FORM_block4: + AddrOS << format("<0x%8.8x> ", (uint32_t)UValue); + break; + default: + break; + } + + const uint8_t *DataPtr = Value.data; + if (DataPtr) { + // UValue contains size of block + const uint8_t *EndDataPtr = DataPtr + UValue; + while (DataPtr < EndDataPtr) { + AddrOS << format("%2.2x ", *DataPtr); + ++DataPtr; + } + } else + OS << "NULL"; + } + break; + + case DW_FORM_sdata: + OS << Value.sval; + break; + case DW_FORM_udata: + OS << Value.uval; + break; + case DW_FORM_strp: + if (DumpOpts.Verbose) + 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: + case DW_FORM_strx3: + case DW_FORM_strx4: + case DW_FORM_GNU_str_index: + if (DumpOpts.Verbose) + OS << format("indexed (%8.8x) string = ", (uint32_t)UValue); + dumpString(OS); + break; + case DW_FORM_GNU_strp_alt: + if (DumpOpts.Verbose) + OS << format("alt indirect string, offset: 0x%" PRIx64 "", UValue); + dumpString(OS); + break; + case DW_FORM_ref_addr: + AddrOS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_ref1: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%2.2x", (uint8_t)UValue); + break; + case DW_FORM_ref2: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%4.4x", (uint16_t)UValue); + break; + case DW_FORM_ref4: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%4.4x", (uint32_t)UValue); + break; + case DW_FORM_ref8: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%8.8" PRIx64, UValue); + break; + case DW_FORM_ref_udata: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%" PRIx64, UValue); + break; + case DW_FORM_GNU_ref_alt: + AddrOS << format("<alt 0x%" PRIx64 ">", UValue); + break; + + // All DW_FORM_indirect attributes should be resolved prior to calling + // this function + case DW_FORM_indirect: + 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); + break; + + default: + OS << format("DW_FORM(0x%4.4x)", Form); + break; + } + + if (CURelativeOffset) { + if (DumpOpts.Verbose) + OS << " => {"; + if (DumpOpts.ShowAddresses) + WithColor(OS, HighlightColor::Address).get() + << format("0x%8.8" PRIx64, UValue + (U ? U->getOffset() : 0)); + if (DumpOpts.Verbose) + OS << "}"; + } +} + +void DWARFFormValue::dumpString(raw_ostream &OS) const { + Optional<const char *> DbgStr = getAsCString(); + if (DbgStr.hasValue()) { + auto COS = WithColor(OS, HighlightColor::String); + COS.get() << '"'; + COS.get().write_escaped(DbgStr.getValue()); + COS.get() << '"'; + } +} + +Optional<const char *> DWARFFormValue::getAsCString() const { + if (!isFormClass(FC_String)) + return None; + if (Form == DW_FORM_string) + return Value.cstr; + // FIXME: Add support for DW_FORM_GNU_strp_alt + 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) { + if (!U) + return None; + Optional<uint64_t> StrOffset = U->getStringOffsetSectionItem(Offset); + if (!StrOffset) + return None; + Offset = *StrOffset; + } + // 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; +} + +Optional<uint64_t> DWARFFormValue::getAsAddress() const { + if (auto SA = getAsSectionedAddress()) + return SA->Address; + return None; +} +Optional<SectionedAddress> DWARFFormValue::getAsSectionedAddress() const { + if (!isFormClass(FC_Address)) + return None; + if (Form == DW_FORM_GNU_addr_index || Form == DW_FORM_addrx) { + uint32_t Index = Value.uval; + if (!U) + return None; + Optional<SectionedAddress> SA = U->getAddrOffsetSectionItem(Index); + if (!SA) + return None; + return SA; + } + return {{Value.uval, Value.SectionIndex}}; +} + +Optional<uint64_t> DWARFFormValue::getAsReference() const { + if (!isFormClass(FC_Reference)) + return None; + switch (Form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + if (!U) + return None; + return Value.uval + U->getOffset(); + case DW_FORM_ref_addr: + case DW_FORM_ref_sig8: + case DW_FORM_GNU_ref_alt: + return Value.uval; + default: + return None; + } +} + +Optional<uint64_t> DWARFFormValue::getAsSectionOffset() const { + if (!isFormClass(FC_SectionOffset)) + return None; + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsUnsignedConstant() const { + if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || + Form == DW_FORM_sdata) + return None; + return Value.uval; +} + +Optional<int64_t> DWARFFormValue::getAsSignedConstant() const { + if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || + (Form == DW_FORM_udata && + uint64_t(std::numeric_limits<int64_t>::max()) < Value.uval)) + return None; + switch (Form) { + case DW_FORM_data4: + return int32_t(Value.uval); + case DW_FORM_data2: + return int16_t(Value.uval); + case DW_FORM_data1: + return int8_t(Value.uval); + case DW_FORM_sdata: + case DW_FORM_data8: + default: + return Value.sval; + } +} + +Optional<ArrayRef<uint8_t>> DWARFFormValue::getAsBlock() const { + if (!isFormClass(FC_Block) && !isFormClass(FC_Exprloc) && + Form != DW_FORM_data16) + return None; + return makeArrayRef(Value.data, Value.uval); +} + +Optional<uint64_t> DWARFFormValue::getAsCStringOffset() const { + if (!isFormClass(FC_String) && Form == DW_FORM_string) + return None; + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsReferenceUVal() const { + if (!isFormClass(FC_Reference)) + return None; + return Value.uval; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp new file mode 100644 index 000000000000..1abd931e3b8b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp @@ -0,0 +1,200 @@ +//===- DWARFGdbIndex.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/DWARFGdbIndex.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <utility> + +using namespace llvm; + +// .gdb_index section format reference: +// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html + +void DWARFGdbIndex::dumpCUList(raw_ostream &OS) const { + OS << format("\n CU list offset = 0x%x, has %" PRId64 " entries:", + CuListOffset, (uint64_t)CuList.size()) + << '\n'; + uint32_t I = 0; + for (const CompUnitEntry &CU : CuList) + OS << format(" %d: Offset = 0x%llx, Length = 0x%llx\n", I++, CU.Offset, + CU.Length); +} + +void DWARFGdbIndex::dumpTUList(raw_ostream &OS) const { + OS << formatv("\n Types CU list offset = {0:x}, has {1} entries:\n", + TuListOffset, TuList.size()); + uint32_t I = 0; + for (const TypeUnitEntry &TU : TuList) + OS << formatv(" {0}: offset = {1:x8}, type_offset = {2:x8}, " + "type_signature = {3:x16}\n", + I++, TU.Offset, TU.TypeOffset, TU.TypeSignature); +} + +void DWARFGdbIndex::dumpAddressArea(raw_ostream &OS) const { + OS << format("\n Address area offset = 0x%x, has %" PRId64 " entries:", + AddressAreaOffset, (uint64_t)AddressArea.size()) + << '\n'; + for (const AddressEntry &Addr : AddressArea) + OS << format( + " Low/High address = [0x%llx, 0x%llx) (Size: 0x%llx), CU id = %d\n", + Addr.LowAddress, Addr.HighAddress, Addr.HighAddress - Addr.LowAddress, + Addr.CuIndex); +} + +void DWARFGdbIndex::dumpSymbolTable(raw_ostream &OS) const { + OS << format("\n Symbol table offset = 0x%x, size = %" PRId64 + ", filled slots:", + SymbolTableOffset, (uint64_t)SymbolTable.size()) + << '\n'; + uint32_t I = -1; + for (const SymTableEntry &E : SymbolTable) { + ++I; + if (!E.NameOffset && !E.VecOffset) + continue; + + OS << format(" %d: Name offset = 0x%x, CU vector offset = 0x%x\n", I, + E.NameOffset, E.VecOffset); + + StringRef Name = ConstantPoolStrings.substr( + ConstantPoolOffset - StringPoolOffset + E.NameOffset); + + auto CuVector = std::find_if( + ConstantPoolVectors.begin(), ConstantPoolVectors.end(), + [&](const std::pair<uint32_t, SmallVector<uint32_t, 0>> &V) { + return V.first == E.VecOffset; + }); + assert(CuVector != ConstantPoolVectors.end() && "Invalid symbol table"); + uint32_t CuVectorId = CuVector - ConstantPoolVectors.begin(); + OS << format(" String name: %s, CU vector index: %d\n", Name.data(), + CuVectorId); + } +} + +void DWARFGdbIndex::dumpConstantPool(raw_ostream &OS) const { + OS << format("\n Constant pool offset = 0x%x, has %" PRId64 " CU vectors:", + ConstantPoolOffset, (uint64_t)ConstantPoolVectors.size()); + uint32_t I = 0; + for (const auto &V : ConstantPoolVectors) { + OS << format("\n %d(0x%x): ", I++, V.first); + for (uint32_t Val : V.second) + OS << format("0x%x ", Val); + } + OS << '\n'; +} + +void DWARFGdbIndex::dump(raw_ostream &OS) { + if (HasError) { + OS << "\n<error parsing>\n"; + return; + } + + if (HasContent) { + OS << " Version = " << Version << '\n'; + dumpCUList(OS); + dumpTUList(OS); + dumpAddressArea(OS); + dumpSymbolTable(OS); + dumpConstantPool(OS); + } +} + +bool DWARFGdbIndex::parseImpl(DataExtractor Data) { + uint32_t Offset = 0; + + // Only version 7 is supported at this moment. + Version = Data.getU32(&Offset); + if (Version != 7) + return false; + + CuListOffset = Data.getU32(&Offset); + uint32_t CuTypesOffset = Data.getU32(&Offset); + AddressAreaOffset = Data.getU32(&Offset); + SymbolTableOffset = Data.getU32(&Offset); + ConstantPoolOffset = Data.getU32(&Offset); + + if (Offset != CuListOffset) + return false; + + uint32_t CuListSize = (CuTypesOffset - CuListOffset) / 16; + CuList.reserve(CuListSize); + for (uint32_t i = 0; i < CuListSize; ++i) { + uint64_t CuOffset = Data.getU64(&Offset); + uint64_t CuLength = Data.getU64(&Offset); + CuList.push_back({CuOffset, CuLength}); + } + + // CU Types are no longer needed as DWARF skeleton type units never made it + // into the standard. + uint32_t TuListSize = (AddressAreaOffset - CuTypesOffset) / 24; + TuList.resize(TuListSize); + for (uint32_t I = 0; I < TuListSize; ++I) { + uint64_t CuOffset = Data.getU64(&Offset); + uint64_t TypeOffset = Data.getU64(&Offset); + uint64_t Signature = Data.getU64(&Offset); + TuList[I] = {CuOffset, TypeOffset, Signature}; + } + + uint32_t AddressAreaSize = (SymbolTableOffset - AddressAreaOffset) / 20; + AddressArea.reserve(AddressAreaSize); + for (uint32_t i = 0; i < AddressAreaSize; ++i) { + uint64_t LowAddress = Data.getU64(&Offset); + uint64_t HighAddress = Data.getU64(&Offset); + uint32_t CuIndex = Data.getU32(&Offset); + AddressArea.push_back({LowAddress, HighAddress, CuIndex}); + } + + // The symbol table. This is an open addressed hash table. The size of the + // hash table is always a power of 2. + // Each slot in the hash table consists of a pair of offset_type values. The + // first value is the offset of the symbol's name in the constant pool. The + // second value is the offset of the CU vector in the constant pool. + // If both values are 0, then this slot in the hash table is empty. This is ok + // because while 0 is a valid constant pool index, it cannot be a valid index + // for both a string and a CU vector. + uint32_t SymTableSize = (ConstantPoolOffset - SymbolTableOffset) / 8; + SymbolTable.reserve(SymTableSize); + uint32_t CuVectorsTotal = 0; + for (uint32_t i = 0; i < SymTableSize; ++i) { + uint32_t NameOffset = Data.getU32(&Offset); + uint32_t CuVecOffset = Data.getU32(&Offset); + SymbolTable.push_back({NameOffset, CuVecOffset}); + if (NameOffset || CuVecOffset) + ++CuVectorsTotal; + } + + // The constant pool. CU vectors are stored first, followed by strings. + // The first value is the number of CU indices in the vector. Each subsequent + // value is the index and symbol attributes of a CU in the CU list. + for (uint32_t i = 0; i < CuVectorsTotal; ++i) { + ConstantPoolVectors.emplace_back(0, SmallVector<uint32_t, 0>()); + auto &Vec = ConstantPoolVectors.back(); + Vec.first = Offset - ConstantPoolOffset; + + uint32_t Num = Data.getU32(&Offset); + for (uint32_t j = 0; j < Num; ++j) + Vec.second.push_back(Data.getU32(&Offset)); + } + + ConstantPoolStrings = Data.getData().drop_front(Offset); + StringPoolOffset = Offset; + return true; +} + +void DWARFGdbIndex::parse(DataExtractor Data) { + HasContent = !Data.getData().empty(); + HasError = HasContent && !parseImpl(Data); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp new file mode 100644 index 000000000000..462c036d73ad --- /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/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +Error DWARFListTableHeader::extract(DWARFDataExtractor Data, + uint32_t *OffsetPtr) { + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createStringError(errc::invalid_argument, + "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 createStringError(errc::not_supported, + "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 createStringError(errc::invalid_argument, + "%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 createStringError(errc::invalid_argument, + "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 createStringError(errc::invalid_argument, + "unrecognised %s table version %" PRIu16 + " in table at offset 0x%" PRIx32, + SectionName.data(), HeaderData.Version, HeaderOffset); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createStringError(errc::not_supported, + "%s table at offset 0x%" PRIx32 + " has unsupported address size %" PRIu8, + SectionName.data(), HeaderOffset, HeaderData.AddrSize); + if (HeaderData.SegSize != 0) + return createStringError(errc::not_supported, + "%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 createStringError(errc::invalid_argument, + "%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 new file mode 100644 index 000000000000..00be75e1a94d --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp @@ -0,0 +1,48 @@ +//===- DWARFTypeUnit.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/DWARFTypeUnit.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> + +using namespace llvm; + +void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + DWARFDie TD = getDIEForOffset(getTypeOffset() + getOffset()); + const char *Name = TD.getName(DINameKind::ShortName); + + if (DumpOpts.SummarizeTypes) { + OS << "name = '" << Name << "'" + << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) + << " length = " << format("0x%08x", getLength()) << '\n'; + return; + } + + OS << format("0x%08x", getOffset()) << ": Type Unit:" + << " length = " << format("0x%08x", getLength()) + << " version = " << format("0x%04x", getVersion()); + if (getVersion() >= 5) + OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << " abbr_offset = " << format("0x%04x", getAbbreviations()->getOffset()) + << " addr_size = " << format("0x%02x", getAddressByteSize()) + << " name = '" << Name << "'" + << " 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)) + TU.dump(OS, 0, DumpOpts); + else + OS << "<type unit can't be parsed!>\n\n"; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp new file mode 100644 index 000000000000..80234665bdeb --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -0,0 +1,844 @@ +//===- DWARFUnit.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/DWARFUnit.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.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/DebugInfo/DWARF/DWARFTypeUnit.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace dwarf; + +void DWARFUnitVector::addUnitsForSection(DWARFContext &C, + const DWARFSection &Section, + DWARFSectionKind SectionKind) { + const DWARFObject &D = C.getDWARFObj(); + addUnitsImpl(C, D, Section, C.getDebugAbbrev(), &D.getRangeSection(), + &D.getLocSection(), D.getStringSection(), + D.getStringOffsetSection(), &D.getAddrSection(), + D.getLineSection(), D.isLittleEndian(), false, false, + SectionKind); +} + +void DWARFUnitVector::addUnitsForDWOSection(DWARFContext &C, + const DWARFSection &DWOSection, + DWARFSectionKind SectionKind, + bool Lazy) { + const DWARFObject &D = C.getDWARFObj(); + addUnitsImpl(C, D, DWOSection, C.getDebugAbbrevDWO(), &D.getRangeDWOSection(), + &D.getLocDWOSection(), D.getStringDWOSection(), + D.getStringOffsetDWOSection(), &D.getAddrSection(), + D.getLineDWOSection(), C.isLittleEndian(), true, Lazy, + SectionKind); +} + +void DWARFUnitVector::addUnitsImpl( + DWARFContext &Context, const DWARFObject &Obj, const DWARFSection &Section, + const DWARFDebugAbbrev *DA, const DWARFSection *RS, + const DWARFSection *LocSection, StringRef SS, const DWARFSection &SOS, + const DWARFSection *AOS, const DWARFSection &LS, bool LE, bool IsDWO, + bool Lazy, DWARFSectionKind SectionKind) { + DWARFDataExtractor Data(Obj, Section, LE, 0); + // Lazy initialization of Parser, now that we have all section info. + if (!Parser) { + Parser = [=, &Context, &Obj, &Section, &SOS, + &LS](uint32_t Offset, DWARFSectionKind SectionKind, + const DWARFSection *CurSection, + const DWARFUnitIndex::Entry *IndexEntry) + -> std::unique_ptr<DWARFUnit> { + const DWARFSection &InfoSection = CurSection ? *CurSection : Section; + DWARFDataExtractor Data(Obj, InfoSection, LE, 0); + if (!Data.isValidOffset(Offset)) + return nullptr; + const DWARFUnitIndex *Index = nullptr; + if (IsDWO) + Index = &getDWARFUnitIndex(Context, SectionKind); + DWARFUnitHeader Header; + if (!Header.extract(Context, Data, &Offset, SectionKind, Index, + IndexEntry)) + return nullptr; + std::unique_ptr<DWARFUnit> U; + if (Header.isTypeUnit()) + U = llvm::make_unique<DWARFTypeUnit>(Context, InfoSection, Header, DA, + RS, LocSection, SS, SOS, AOS, LS, + LE, IsDWO, *this); + else + U = llvm::make_unique<DWARFCompileUnit>(Context, InfoSection, Header, + DA, RS, LocSection, SS, SOS, + AOS, LS, LE, IsDWO, *this); + return U; + }; + } + if (Lazy) + return; + // Find a reasonable insertion point within the vector. We skip over + // (a) units from a different section, (b) units from the same section + // but with lower offset-within-section. This keeps units in order + // within a section, although not necessarily within the object file, + // even if we do lazy parsing. + auto I = this->begin(); + uint32_t Offset = 0; + while (Data.isValidOffset(Offset)) { + if (I != this->end() && + (&(*I)->getInfoSection() != &Section || (*I)->getOffset() == Offset)) { + ++I; + continue; + } + auto U = Parser(Offset, SectionKind, &Section, nullptr); + // If parsing failed, we're done with this section. + if (!U) + break; + Offset = U->getNextUnitOffset(); + I = std::next(this->insert(I, std::move(U))); + } +} + +DWARFUnit *DWARFUnitVector::addUnit(std::unique_ptr<DWARFUnit> Unit) { + auto I = std::upper_bound(begin(), end(), Unit, + [](const std::unique_ptr<DWARFUnit> &LHS, + const std::unique_ptr<DWARFUnit> &RHS) { + return LHS->getOffset() < RHS->getOffset(); + }); + return this->insert(I, std::move(Unit))->get(); +} + +DWARFUnit *DWARFUnitVector::getUnitForOffset(uint32_t Offset) const { + auto end = begin() + getNumInfoUnits(); + auto *CU = + std::upper_bound(begin(), end, Offset, + [](uint32_t LHS, const std::unique_ptr<DWARFUnit> &RHS) { + return LHS < RHS->getNextUnitOffset(); + }); + if (CU != end && (*CU)->getOffset() <= Offset) + return CU->get(); + return nullptr; +} + +DWARFUnit * +DWARFUnitVector::getUnitForIndexEntry(const DWARFUnitIndex::Entry &E) { + const auto *CUOff = E.getOffset(DW_SECT_INFO); + if (!CUOff) + return nullptr; + + auto Offset = CUOff->Offset; + auto end = begin() + getNumInfoUnits(); + + auto *CU = + std::upper_bound(begin(), end, CUOff->Offset, + [](uint32_t LHS, const std::unique_ptr<DWARFUnit> &RHS) { + return LHS < RHS->getNextUnitOffset(); + }); + if (CU != end && (*CU)->getOffset() <= Offset) + return CU->get(); + + if (!Parser) + return nullptr; + + auto U = Parser(Offset, DW_SECT_INFO, nullptr, &E); + if (!U) + U = nullptr; + + auto *NewCU = U.get(); + this->insert(CU, std::move(U)); + ++NumInfoUnits; + return NewCU; +} + +DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, + const DWARFUnitHeader &Header, const DWARFDebugAbbrev *DA, + const DWARFSection *RS, const DWARFSection *LocSection, + StringRef SS, const DWARFSection &SOS, + const DWARFSection *AOS, const DWARFSection &LS, bool LE, + bool IsDWO, const DWARFUnitVector &UnitVector) + : Context(DC), InfoSection(Section), Header(Header), Abbrev(DA), + RangeSection(RS), LocSection(LocSection), LineSection(LS), + StringSection(SS), StringOffsetSection(SOS), AddrOffsetSection(AOS), + isLittleEndian(LE), IsDWO(IsDWO), UnitVector(UnitVector) { + clear(); + // For split DWARF we only need to keep track of the location list section's + // data (no relocations), and if we are reading a package file, we need to + // adjust the location list data based on the index entries. + if (IsDWO) { + LocSectionData = LocSection->Data; + if (auto *IndexEntry = Header.getIndexEntry()) + if (const auto *C = IndexEntry->getOffset(DW_SECT_LOC)) + LocSectionData = LocSectionData.substr(C->Offset, C->Length); + } +} + +DWARFUnit::~DWARFUnit() = default; + +DWARFDataExtractor DWARFUnit::getDebugInfoExtractor() const { + return DWARFDataExtractor(Context.getDWARFObj(), InfoSection, isLittleEndian, + getAddressByteSize()); +} + +Optional<SectionedAddress> +DWARFUnit::getAddrOffsetSectionItem(uint32_t Index) const { + if (IsDWO) { + auto R = Context.info_section_units(); + auto I = R.begin(); + // Surprising if a DWO file has more than one skeleton unit in it - this + // probably shouldn't be valid, but if a use case is found, here's where to + // support it (probably have to linearly search for the matching skeleton CU + // here) + if (I != R.end() && std::next(I) == R.end()) + return (*I)->getAddrOffsetSectionItem(Index); + } + uint32_t Offset = AddrOffsetSectionBase + Index * getAddressByteSize(); + if (AddrOffsetSection->Data.size() < Offset + getAddressByteSize()) + return None; + DWARFDataExtractor DA(Context.getDWARFObj(), *AddrOffsetSection, + isLittleEndian, getAddressByteSize()); + uint64_t Section; + uint64_t Address = DA.getRelocatedAddress(&Offset, &Section); + return {{Address, Section}}; +} + +Optional<uint64_t> DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { + if (!StringOffsetsTableContribution) + return None; + unsigned ItemSize = getDwarfStringOffsetsByteSize(); + uint32_t Offset = getStringOffsetsBase() + Index * ItemSize; + if (StringOffsetSection.Data.size() < Offset + ItemSize) + return None; + DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, + isLittleEndian, 0); + return DA.getRelocatedValue(ItemSize, &Offset); +} + +bool DWARFUnitHeader::extract(DWARFContext &Context, + const DWARFDataExtractor &debug_info, + uint32_t *offset_ptr, + DWARFSectionKind SectionKind, + const DWARFUnitIndex *Index, + const DWARFUnitIndex::Entry *Entry) { + Offset = *offset_ptr; + IndexEntry = Entry; + if (!IndexEntry && Index) + IndexEntry = Index->getFromOffset(*offset_ptr); + 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) { + UnitType = debug_info.getU8(offset_ptr); + FormParams.AddrSize = debug_info.getU8(offset_ptr); + AbbrOffset = debug_info.getU32(offset_ptr); + } else { + 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) + return false; + auto *UnitContrib = IndexEntry->getOffset(); + if (!UnitContrib || UnitContrib->Length != (Length + 4)) + return false; + auto *AbbrEntry = IndexEntry->getOffset(DW_SECT_ABBREV); + if (!AbbrEntry) + 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 || !TypeOffsetOK) + return false; + + // Keep track of the highest DWARF version we encounter across all units. + Context.setMaxVersionIfGreater(getVersion()); + return true; +} + +// 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) + return createStringError(errc::invalid_argument, "Did not detect a valid" + " range list table with base = 0x%" PRIu32, + Offset); + Offset -= 12U; + } + llvm::DWARFDebugRnglistTable Table; + if (Error E = Table.extractHeaderAndOffsets(DA, &Offset)) + return std::move(E); + return Table; +} + +Error DWARFUnit::extractRangeList(uint32_t RangeListOffset, + DWARFDebugRangeList &RangeList) const { + // Require that compile unit is extracted. + assert(!DieArray.empty()); + DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, + isLittleEndian, getAddressByteSize()); + uint32_t ActualRangeListOffset = RangeSectionBase + RangeListOffset; + return RangeList.extract(RangesData, &ActualRangeListOffset); +} + +void DWARFUnit::clear() { + Abbrevs = nullptr; + BaseAddr.reset(); + RangeSectionBase = 0; + AddrOffsetSectionBase = 0; + clearDIEs(false); + DWO.reset(); +} + +const char *DWARFUnit::getCompilationDir() { + return dwarf::toString(getUnitDIE().find(DW_AT_comp_dir), nullptr); +} + +void DWARFUnit::extractDIEsToVector( + bool AppendCUDie, bool AppendNonCUDies, + std::vector<DWARFDebugInfoEntry> &Dies) const { + if (!AppendCUDie && !AppendNonCUDies) + return; + + // Set the offset to that of the first DIE and calculate the start of the + // next compilation unit header. + uint32_t DIEOffset = getOffset() + getHeaderSize(); + uint32_t NextCUOffset = getNextUnitOffset(); + DWARFDebugInfoEntry DIE; + DWARFDataExtractor DebugInfoData = getDebugInfoExtractor(); + uint32_t Depth = 0; + bool IsCUDie = true; + + while (DIE.extractFast(*this, &DIEOffset, DebugInfoData, NextCUOffset, + Depth)) { + if (IsCUDie) { + if (AppendCUDie) + Dies.push_back(DIE); + if (!AppendNonCUDies) + break; + // The average bytes per DIE entry has been seen to be + // around 14-20 so let's pre-reserve the needed memory for + // our DIE entries accordingly. + Dies.reserve(Dies.size() + getDebugInfoSize() / 14); + IsCUDie = false; + } else { + Dies.push_back(DIE); + } + + if (const DWARFAbbreviationDeclaration *AbbrDecl = + DIE.getAbbreviationDeclarationPtr()) { + // Normal DIE + if (AbbrDecl->hasChildren()) + ++Depth; + } else { + // NULL DIE. + if (Depth > 0) + --Depth; + if (Depth == 0) + break; // We are done with this compile unit! + } + } + + // Give a little bit of info if we encounter corrupt DWARF (our offset + // should always terminate at or before the start of the next compilation + // unit header). + if (DIEOffset > NextCUOffset) + 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) { + if ((CUDieOnly && !DieArray.empty()) || + DieArray.size() > 1) + return 0; // Already parsed. + + bool HasCUDie = !DieArray.empty(); + extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray); + + if (DieArray.empty()) + return 0; + + // If CU DIE was just parsed, copy several attribute values from it. + if (!HasCUDie) { + DWARFDie UnitDie = getUnitDIE(); + if (Optional<uint64_t> DWOId = toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id))) + Header.setDWOId(*DWOId); + if (!IsDWO) { + assert(AddrOffsetSectionBase == 0); + assert(RangeSectionBase == 0); + AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base), 0); + if (!AddrOffsetSectionBase) + AddrOffsetSectionBase = + toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); + RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); + } + + // In general, in DWARF v5 and beyond we derive the start of the unit's + // contribution to the string offsets table from the unit DIE's + // DW_AT_str_offsets_base attribute. Split DWARF units do not use this + // attribute, so we assume that there is a contribution to the string + // offsets table starting at offset 0 of the debug_str_offsets.dwo section. + // In both cases we need to determine the format of the contribution, + // which may differ from the unit's format. + DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, + isLittleEndian, 0); + if (IsDWO) + StringOffsetsTableContribution = + determineStringOffsetsTableContributionDWO(DA); + else if (getVersion() >= 5) + StringOffsetsTableContribution = + determineStringOffsetsTableContribution(DA); + + // 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. + } + + return DieArray.size(); +} + +bool DWARFUnit::parseDWO() { + if (IsDWO) + return false; + if (DWO.get()) + return false; + DWARFDie UnitDie = getUnitDIE(); + if (!UnitDie) + return false; + auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); + if (!DWOFileName) + return false; + auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); + SmallString<16> AbsolutePath; + if (sys::path::is_relative(*DWOFileName) && CompilationDir && + *CompilationDir) { + sys::path::append(AbsolutePath, *CompilationDir); + } + sys::path::append(AbsolutePath, *DWOFileName); + auto DWOId = getDWOId(); + if (!DWOId) + return false; + auto DWOContext = Context.getDWOContext(AbsolutePath); + if (!DWOContext) + return false; + + DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); + if (!DWOCU) + return false; + 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); + 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; +} + +void DWARFUnit::clearDIEs(bool KeepCUDie) { + if (DieArray.size() > (unsigned)KeepCUDie) { + DieArray.resize((unsigned)KeepCUDie); + DieArray.shrink_to_fit(); + } +} + +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(), *this); + return RangeListOrError.takeError(); + } + + return createStringError(errc::invalid_argument, + "missing or invalid range list table"); +} + +Expected<DWARFAddressRangesVector> +DWARFUnit::findRnglistFromIndex(uint32_t Index) { + if (auto Offset = getRnglistOffset(Index)) + return findRnglistFromOffset(*Offset + RangeSectionBase); + + if (RngListTable) + return createStringError(errc::invalid_argument, + "invalid range list table index %d", Index); + else + return createStringError(errc::invalid_argument, + "missing or invalid range list table"); +} + +Expected<DWARFAddressRangesVector> DWARFUnit::collectAddressRanges() { + DWARFDie UnitDie = getUnitDIE(); + if (!UnitDie) + return createStringError(errc::invalid_argument, "No unit DIE"); + + // First, check if unit DIE describes address ranges for the whole unit. + auto CUDIERangesOrError = UnitDie.getAddressRanges(); + if (!CUDIERangesOrError) + return createStringError(errc::invalid_argument, + "decoding address ranges: %s", + toString(CUDIERangesOrError.takeError()).c_str()); + return *CUDIERangesOrError; +} + +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; + } + AddrDieMap[R.LowPC] = std::make_pair(R.HighPC, Die); + } + } else + llvm::consumeError(DIERangesOrError.takeError()); + } + // 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); + if (AddrDieMap.empty()) + updateAddressDieMap(getUnitDIE()); + auto R = AddrDieMap.upper_bound(Address); + if (R == AddrDieMap.begin()) + return DWARFDie(); + // upper_bound's previous item contains Address. + --R; + if (Address >= R->second.first) + return DWARFDie(); + return R->second.second; +} + +void +DWARFUnit::getInlinedChainForAddress(uint64_t Address, + SmallVectorImpl<DWARFDie> &InlinedChain) { + assert(InlinedChain.empty()); + // Try to look for subprogram DIEs in the DWO file. + parseDWO(); + // First, find the subroutine that contains the given address (the leaf + // of inlined chain). + DWARFDie SubroutineDIE = + (DWO ? DWO.get() : this)->getSubroutineForAddress(Address); + + 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, + DWARFSectionKind Kind) { + if (Kind == DW_SECT_INFO) + return Context.getCUIndex(); + assert(Kind == DW_SECT_TYPES); + return Context.getTUIndex(); +} + +DWARFDie DWARFUnit::getParent(const DWARFDebugInfoEntry *Die) { + if (!Die) + return DWARFDie(); + const uint32_t Depth = Die->getDepth(); + // Unit DIEs always have a depth of zero and never have parents. + if (Depth == 0) + return DWARFDie(); + // Depth of 1 always means parent is the compile/type unit. + if (Depth == 1) + return getUnitDIE(); + // Look for previous DIE with a depth that is one less than the Die's depth. + const uint32_t ParentDepth = Depth - 1; + for (uint32_t I = getDIEIndex(Die) - 1; I > 0; --I) { + if (DieArray[I].getDepth() == ParentDepth) + return DWARFDie(this, &DieArray[I]); + } + return DWARFDie(); +} + +DWARFDie DWARFUnit::getSibling(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(); + // NULL DIEs don't have siblings. + if (Die->getAbbreviationDeclarationPtr() == nullptr) + return DWARFDie(); + + // Find the next DIE whose depth is the same as the Die's depth. + for (size_t I = getDIEIndex(Die) + 1, EndIdx = DieArray.size(); I < EndIdx; + ++I) { + if (DieArray[I].getDepth() == Depth) + return DWARFDie(this, &DieArray[I]); + } + 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(); + + // We do not want access out of bounds when parsing corrupted debug data. + size_t I = getDIEIndex(Die) + 1; + if (I >= DieArray.size()) + return DWARFDie(); + 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(Header.getAbbrOffset()); + return Abbrevs; +} + +llvm::Optional<SectionedAddress> DWARFUnit::getBaseAddress() { + if (BaseAddr) + return BaseAddr; + + DWARFDie UnitDie = getUnitDIE(); + Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); + BaseAddr = toSectionedAddress(PC); + return BaseAddr; +} + +Optional<StrOffsetsContributionDescriptor> +StrOffsetsContributionDescriptor::validateContributionSize( + DWARFDataExtractor &DA) { + uint8_t EntrySize = getDwarfOffsetByteSize(); + // In order to ensure that we don't read a partial record at the end of + // the section we validate for a multiple of the entry size. + uint64_t ValidationSize = alignTo(Size, EntrySize); + // Guard against overflow. + if (ValidationSize >= Size) + if (DA.isValidOffsetForDataOfSize((uint32_t)Base, ValidationSize)) + return *this; + return None; +} + +// Look for a DWARF64-formatted contribution to the string offsets table +// starting at a given offset and record it in a descriptor. +static Optional<StrOffsetsContributionDescriptor> +parseDWARF64StringOffsetsTableHeader(DWARFDataExtractor &DA, uint32_t Offset) { + if (!DA.isValidOffsetForDataOfSize(Offset, 16)) + return None; + + if (DA.getU32(&Offset) != 0xffffffff) + return None; + + uint64_t Size = DA.getU64(&Offset); + uint8_t Version = DA.getU16(&Offset); + (void)DA.getU16(&Offset); // padding + // 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 {{Offset, Size - 4, Version, DWARF64}}; +} + +// Look for a DWARF32-formatted contribution to the string offsets table +// starting at a given offset and record it in a descriptor. +static Optional<StrOffsetsContributionDescriptor> +parseDWARF32StringOffsetsTableHeader(DWARFDataExtractor &DA, uint32_t Offset) { + if (!DA.isValidOffsetForDataOfSize(Offset, 8)) + return None; + uint32_t ContributionSize = DA.getU32(&Offset); + if (ContributionSize >= 0xfffffff0) + return None; + uint8_t Version = DA.getU16(&Offset); + (void)DA.getU16(&Offset); // padding + // 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 {{Offset, ContributionSize - 4, Version, DWARF32}}; +} + +Optional<StrOffsetsContributionDescriptor> +DWARFUnit::determineStringOffsetsTableContribution(DWARFDataExtractor &DA) { + auto Offset = toSectionOffset(getUnitDIE().find(DW_AT_str_offsets_base), 0); + Optional<StrOffsetsContributionDescriptor> Descriptor; + // Attempt to find a DWARF64 contribution 16 bytes before the base. + if (Offset >= 16) + Descriptor = + parseDWARF64StringOffsetsTableHeader(DA, (uint32_t)Offset - 16); + // Try to find a DWARF32 contribution 8 bytes before the base. + if (!Descriptor && Offset >= 8) + Descriptor = parseDWARF32StringOffsetsTableHeader(DA, (uint32_t)Offset - 8); + return Descriptor ? Descriptor->validateContributionSize(DA) : Descriptor; +} + +Optional<StrOffsetsContributionDescriptor> +DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor & DA) { + uint64_t Offset = 0; + auto IndexEntry = Header.getIndexEntry(); + const auto *C = + IndexEntry ? IndexEntry->getOffset(DW_SECT_STR_OFFSETS) : nullptr; + if (C) + Offset = C->Offset; + if (getVersion() >= 5) { + // Look for a valid contribution at the given offset. + auto Descriptor = + parseDWARF64StringOffsetsTableHeader(DA, (uint32_t)Offset); + if (!Descriptor) + Descriptor = parseDWARF32StringOffsetsTableHeader(DA, (uint32_t)Offset); + return Descriptor ? Descriptor->validateContributionSize(DA) : Descriptor; + } + // Prior to DWARF v5, we derive the contribution size from the + // index table (in a package file). In a .dwo file it is simply + // the length of the string offsets section. + if (!IndexEntry) + return {{0, StringOffsetSection.Data.size(), 4, DWARF32}}; + if (C) + return {{C->Offset, C->Length, 4, DWARF32}}; + return None; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp new file mode 100644 index 000000000000..84b6c4b81817 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp @@ -0,0 +1,202 @@ +//===- DWARFUnitIndex.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/DWARFUnitIndex.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +bool DWARFUnitIndex::Header::parse(DataExtractor IndexData, + uint32_t *OffsetPtr) { + if (!IndexData.isValidOffsetForDataOfSize(*OffsetPtr, 16)) + return false; + Version = IndexData.getU32(OffsetPtr); + NumColumns = IndexData.getU32(OffsetPtr); + NumUnits = IndexData.getU32(OffsetPtr); + NumBuckets = IndexData.getU32(OffsetPtr); + return Version <= 2; +} + +void DWARFUnitIndex::Header::dump(raw_ostream &OS) const { + OS << format("version = %u slots = %u\n\n", Version, NumBuckets); +} + +bool DWARFUnitIndex::parse(DataExtractor IndexData) { + bool b = parseImpl(IndexData); + if (!b) { + // Make sure we don't try to dump anything + Header.NumBuckets = 0; + // Release any partially initialized data. + ColumnKinds.reset(); + Rows.reset(); + } + return b; +} + +bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { + uint32_t Offset = 0; + if (!Header.parse(IndexData, &Offset)) + return false; + + if (!IndexData.isValidOffsetForDataOfSize( + Offset, Header.NumBuckets * (8 + 4) + + (2 * Header.NumUnits + 1) * 4 * Header.NumColumns)) + return false; + + Rows = llvm::make_unique<Entry[]>(Header.NumBuckets); + auto Contribs = + llvm::make_unique<Entry::SectionContribution *[]>(Header.NumUnits); + ColumnKinds = llvm::make_unique<DWARFSectionKind[]>(Header.NumColumns); + + // Read Hash Table of Signatures + for (unsigned i = 0; i != Header.NumBuckets; ++i) + Rows[i].Signature = IndexData.getU64(&Offset); + + // Read Parallel Table of Indexes + for (unsigned i = 0; i != Header.NumBuckets; ++i) { + auto Index = IndexData.getU32(&Offset); + if (!Index) + continue; + Rows[i].Index = this; + Rows[i].Contributions = + llvm::make_unique<Entry::SectionContribution[]>(Header.NumColumns); + Contribs[Index - 1] = Rows[i].Contributions.get(); + } + + // Read the Column Headers + for (unsigned i = 0; i != Header.NumColumns; ++i) { + ColumnKinds[i] = static_cast<DWARFSectionKind>(IndexData.getU32(&Offset)); + if (ColumnKinds[i] == InfoColumnKind) { + if (InfoColumn != -1) + return false; + InfoColumn = i; + } + } + + if (InfoColumn == -1) + return false; + + // Read Table of Section Offsets + for (unsigned i = 0; i != Header.NumUnits; ++i) { + auto *Contrib = Contribs[i]; + for (unsigned i = 0; i != Header.NumColumns; ++i) + Contrib[i].Offset = IndexData.getU32(&Offset); + } + + // Read Table of Section Sizes + for (unsigned i = 0; i != Header.NumUnits; ++i) { + auto *Contrib = Contribs[i]; + for (unsigned i = 0; i != Header.NumColumns; ++i) + Contrib[i].Length = IndexData.getU32(&Offset); + } + + return true; +} + +StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { +#define CASE(DS) \ + case DW_SECT_##DS: \ + return #DS; + switch (DS) { + CASE(INFO); + CASE(TYPES); + CASE(ABBREV); + CASE(LINE); + CASE(LOC); + CASE(STR_OFFSETS); + CASE(MACINFO); + CASE(MACRO); + } + llvm_unreachable("unknown DWARFSectionKind"); +} + +void DWARFUnitIndex::dump(raw_ostream &OS) const { + if (!*this) + return; + + Header.dump(OS); + OS << "Index Signature "; + for (unsigned i = 0; i != Header.NumColumns; ++i) + OS << ' ' << left_justify(getColumnHeader(ColumnKinds[i]), 24); + OS << "\n----- ------------------"; + for (unsigned i = 0; i != Header.NumColumns; ++i) + OS << " ------------------------"; + OS << '\n'; + for (unsigned i = 0; i != Header.NumBuckets; ++i) { + auto &Row = Rows[i]; + if (auto *Contribs = Row.Contributions.get()) { + OS << format("%5u 0x%016" PRIx64 " ", i + 1, Row.Signature); + for (unsigned i = 0; i != Header.NumColumns; ++i) { + auto &Contrib = Contribs[i]; + OS << format("[0x%08x, 0x%08x) ", Contrib.Offset, + Contrib.Offset + Contrib.Length); + } + OS << '\n'; + } + } +} + +const DWARFUnitIndex::Entry::SectionContribution * +DWARFUnitIndex::Entry::getOffset(DWARFSectionKind Sec) const { + uint32_t i = 0; + for (; i != Index->Header.NumColumns; ++i) + if (Index->ColumnKinds[i] == Sec) + return &Contributions[i]; + return nullptr; +} + +const DWARFUnitIndex::Entry::SectionContribution * +DWARFUnitIndex::Entry::getOffset() const { + return &Contributions[Index->InfoColumn]; +} + +const DWARFUnitIndex::Entry * +DWARFUnitIndex::getFromOffset(uint32_t Offset) const { + if (OffsetLookup.empty()) { + for (uint32_t i = 0; i != Header.NumBuckets; ++i) + if (Rows[i].Contributions) + OffsetLookup.push_back(&Rows[i]); + llvm::sort(OffsetLookup, [&](Entry *E1, Entry *E2) { + return E1->Contributions[InfoColumn].Offset < + E2->Contributions[InfoColumn].Offset; + }); + } + auto I = + llvm::upper_bound(OffsetLookup, Offset, [&](uint32_t Offset, Entry *E2) { + return Offset < E2->Contributions[InfoColumn].Offset; + }); + if (I == OffsetLookup.begin()) + return nullptr; + --I; + const auto *E = *I; + const auto &InfoContrib = E->Contributions[InfoColumn]; + if ((InfoContrib.Offset + InfoContrib.Length) <= Offset) + return nullptr; + return E; +} + +const DWARFUnitIndex::Entry *DWARFUnitIndex::getFromHash(uint64_t S) const { + uint64_t Mask = Header.NumBuckets - 1; + + auto H = S & Mask; + auto HP = ((S >> 32) & Mask) | 1; + while (Rows[H].getSignature() != S && Rows[H].getSignature() != 0) + H = (H + HP) & Mask; + + if (Rows[H].getSignature() != S) + return nullptr; + + return &Rows[H]; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp new file mode 100644 index 000000000000..f8370178b627 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -0,0 +1,1514 @@ +//===- DWARFVerifier.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/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" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.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> +#include <vector> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +DWARFVerifier::DieRangeInfo::address_range_iterator +DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { + auto Begin = Ranges.begin(); + auto End = Ranges.end(); + auto Pos = std::lower_bound(Begin, End, R); + + if (Pos != End) { + if (Pos->intersects(R)) + return Pos; + if (Pos != Begin) { + auto Iter = Pos - 1; + if (Iter->intersects(R)) + return Iter; + } + } + + Ranges.insert(Pos, R); + return Ranges.end(); +} + +DWARFVerifier::DieRangeInfo::die_range_info_iterator +DWARFVerifier::DieRangeInfo::insert(const DieRangeInfo &RI) { + auto End = Children.end(); + auto Iter = Children.begin(); + while (Iter != End) { + if (Iter->intersects(RI)) + return Iter; + ++Iter; + } + Children.insert(RI); + return Children.end(); +} + +bool DWARFVerifier::DieRangeInfo::contains(const DieRangeInfo &RHS) const { + // Both list of ranges are sorted so we can make this fast. + + if (Ranges.empty() || RHS.Ranges.empty()) + return false; + + // Since the ranges are sorted we can advance where we start searching with + // this object's ranges as we traverse RHS.Ranges. + auto End = Ranges.end(); + auto Iter = findRange(RHS.Ranges.front()); + + // Now linearly walk the ranges in this object and see if they contain each + // ranges from RHS.Ranges. + for (const auto &R : RHS.Ranges) { + while (Iter != End) { + if (Iter->contains(R)) + break; + ++Iter; + } + if (Iter == End) + return false; + } + return true; +} + +bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const { + if (Ranges.empty() || RHS.Ranges.empty()) + return false; + + auto End = Ranges.end(); + auto Iter = findRange(RHS.Ranges.front()); + for (const auto &R : RHS.Ranges) { + if (Iter == End) + return false; + if (R.HighPC <= Iter->LowPC) + continue; + while (Iter != End) { + if (Iter->intersects(R)) + return true; + ++Iter; + } + } + + return false; +} + +bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, + uint32_t *Offset, unsigned UnitIndex, + uint8_t &UnitType, bool &isUnitDWARF64) { + uint32_t AbbrOffset, Length; + uint8_t AddrSize = 0; + uint16_t Version; + bool Success = true; + + bool ValidLength = false; + bool ValidVersion = false; + bool ValidAddrSize = false; + bool ValidType = true; + bool ValidAbbrevOffset = true; + + uint32_t OffsetStart = *Offset; + Length = DebugInfoData.getU32(Offset); + if (Length == UINT32_MAX) { + isUnitDWARF64 = true; + OS << format( + "Unit[%d] is in 64-bit DWARF format; cannot verify from this point.\n", + UnitIndex); + return false; + } + Version = DebugInfoData.getU16(Offset); + + if (Version >= 5) { + UnitType = DebugInfoData.getU8(Offset); + AddrSize = DebugInfoData.getU8(Offset); + AbbrOffset = DebugInfoData.getU32(Offset); + ValidType = dwarf::isUnitType(UnitType); + } else { + UnitType = 0; + AbbrOffset = DebugInfoData.getU32(Offset); + AddrSize = DebugInfoData.getU8(Offset); + } + + if (!DCtx.getDebugAbbrev()->getAbbreviationDeclarationSet(AbbrOffset)) + ValidAbbrevOffset = false; + + ValidLength = DebugInfoData.isValidOffset(OffsetStart + Length + 3); + ValidVersion = DWARFContext::isSupportedVersion(Version); + ValidAddrSize = AddrSize == 4 || AddrSize == 8; + if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset || + !ValidType) { + Success = false; + error() << format("Units[%d] - start offset: 0x%08x \n", UnitIndex, + OffsetStart); + if (!ValidLength) + note() << "The length for this unit is too " + "large for the .debug_info provided.\n"; + if (!ValidVersion) + note() << "The 16 bit unit header version is not valid.\n"; + if (!ValidType) + note() << "The unit type encoding is not valid.\n"; + if (!ValidAbbrevOffset) + note() << "The offset into the .debug_abbrev section is " + "not valid.\n"; + if (!ValidAddrSize) + note() << "The address size is unsupported.\n"; + } + *Offset = OffsetStart + Length + 4; + return Success; +} + +unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit) { + unsigned NumUnitErrors = 0; + unsigned NumDies = Unit.getNumDIEs(); + for (unsigned I = 0; I < NumDies; ++I) { + auto Die = Unit.getDIEAtIndex(I); + + if (Die.getTag() == DW_TAG_null) + continue; + + bool HasTypeAttr = false; + for (auto AttrValue : Die.attributes()) { + NumUnitErrors += verifyDebugInfoAttribute(Die, AttrValue); + NumUnitErrors += verifyDebugInfoForm(Die, AttrValue); + HasTypeAttr |= (AttrValue.Attr == DW_AT_type); + } + + if (!HasTypeAttr && (Die.getTag() == DW_TAG_formal_parameter || + Die.getTag() == DW_TAG_variable || + Die.getTag() == DW_TAG_array_type)) { + error() << "DIE with tag " << TagString(Die.getTag()) + << " is missing type attribute:\n"; + dump(Die) << '\n'; + NumUnitErrors++; + } + NumUnitErrors += verifyDebugInfoCallSite(Die); + } + + DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false); + if (!Die) { + error() << "Compilation unit without DIE.\n"; + NumUnitErrors++; + return NumUnitErrors; + } + + if (!dwarf::isUnitType(Die.getTag())) { + error() << "Compilation unit root DIE is not a unit DIE: " + << dwarf::TagString(Die.getTag()) << ".\n"; + NumUnitErrors++; + } + + uint8_t UnitType = Unit.getUnitType(); + if (!DWARFUnit::isMatchingUnitTypeAndTag(UnitType, Die.getTag())) { + error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType) + << ") and root DIE (" << dwarf::TagString(Die.getTag()) + << ") do not match.\n"; + NumUnitErrors++; + } + + DieRangeInfo RI; + NumUnitErrors += verifyDieRanges(Die, RI); + + return NumUnitErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) { + if (Die.getTag() != DW_TAG_call_site) + return 0; + + DWARFDie Curr = Die.getParent(); + for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) { + if (Curr.getTag() == DW_TAG_inlined_subroutine) { + error() << "Call site entry nested within inlined subroutine:"; + Curr.dump(OS); + return 1; + } + } + + if (!Curr.isValid()) { + error() << "Call site entry not nested within a valid subprogram:"; + Die.dump(OS); + return 1; + } + + Optional<DWARFFormValue> CallAttr = + Curr.find({DW_AT_call_all_calls, DW_AT_call_all_source_calls, + DW_AT_call_all_tail_calls}); + if (!CallAttr) { + error() << "Subprogram with call site entry has no DW_AT_call attribute:"; + Curr.dump(OS); + Die.dump(OS, /*indent*/ 1); + return 1; + } + + return 0; +} + +unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) { + unsigned NumErrors = 0; + if (Abbrev) { + const DWARFAbbreviationDeclarationSet *AbbrDecls = + Abbrev->getAbbreviationDeclarationSet(0); + for (auto AbbrDecl : *AbbrDecls) { + SmallDenseSet<uint16_t> AttributeSet; + for (auto Attribute : AbbrDecl.attributes()) { + auto Result = AttributeSet.insert(Attribute.Attr); + if (!Result.second) { + error() << "Abbreviation declaration contains multiple " + << AttributeString(Attribute.Attr) << " attributes.\n"; + AbbrDecl.dump(OS); + ++NumErrors; + } + } + } + } + return NumErrors; +} + +bool DWARFVerifier::handleDebugAbbrev() { + OS << "Verifying .debug_abbrev...\n"; + + const DWARFObject &DObj = DCtx.getDWARFObj(); + bool noDebugAbbrev = DObj.getAbbrevSection().empty(); + bool noDebugAbbrevDWO = DObj.getAbbrevDWOSection().empty(); + + if (noDebugAbbrev && noDebugAbbrevDWO) { + return true; + } + + unsigned NumErrors = 0; + if (!noDebugAbbrev) + NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrev()); + + if (!noDebugAbbrevDWO) + NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrevDWO()); + return NumErrors == 0; +} + +unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S, + DWARFSectionKind SectionKind) { + const DWARFObject &DObj = DCtx.getDWARFObj(); + DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0); + unsigned NumDebugInfoErrors = 0; + uint32_t OffsetStart = 0, Offset = 0, UnitIdx = 0; + uint8_t UnitType = 0; + bool isUnitDWARF64 = false; + bool isHeaderChainValid = true; + bool hasDIE = DebugInfoData.isValidOffset(Offset); + DWARFUnitVector TypeUnitVector; + DWARFUnitVector CompileUnitVector; + while (hasDIE) { + OffsetStart = Offset; + if (!verifyUnitHeader(DebugInfoData, &Offset, UnitIdx, UnitType, + isUnitDWARF64)) { + isHeaderChainValid = false; + if (isUnitDWARF64) + break; + } else { + DWARFUnitHeader Header; + Header.extract(DCtx, DebugInfoData, &OffsetStart, SectionKind); + DWARFUnit *Unit; + switch (UnitType) { + case dwarf::DW_UT_type: + case dwarf::DW_UT_split_type: { + Unit = TypeUnitVector.addUnit(llvm::make_unique<DWARFTypeUnit>( + DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangeSection(), + &DObj.getLocSection(), DObj.getStringSection(), + DObj.getStringOffsetSection(), &DObj.getAppleObjCSection(), + DObj.getLineSection(), DCtx.isLittleEndian(), false, + TypeUnitVector)); + break; + } + case dwarf::DW_UT_skeleton: + case dwarf::DW_UT_split_compile: + case dwarf::DW_UT_compile: + case dwarf::DW_UT_partial: + // UnitType = 0 means that we are verifying a compile unit in DWARF v4. + case 0: { + Unit = CompileUnitVector.addUnit(llvm::make_unique<DWARFCompileUnit>( + DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangeSection(), + &DObj.getLocSection(), DObj.getStringSection(), + DObj.getStringOffsetSection(), &DObj.getAppleObjCSection(), + DObj.getLineSection(), DCtx.isLittleEndian(), false, + CompileUnitVector)); + break; + } + default: { llvm_unreachable("Invalid UnitType."); } + } + NumDebugInfoErrors += verifyUnitContents(*Unit); + } + hasDIE = DebugInfoData.isValidOffset(Offset); + ++UnitIdx; + } + if (UnitIdx == 0 && !hasDIE) { + warn() << "Section is empty.\n"; + isHeaderChainValid = true; + } + if (!isHeaderChainValid) + ++NumDebugInfoErrors; + NumDebugInfoErrors += verifyDebugInfoReferences(); + return NumDebugInfoErrors; +} + +bool DWARFVerifier::handleDebugInfo() { + const DWARFObject &DObj = DCtx.getDWARFObj(); + unsigned NumErrors = 0; + + OS << "Verifying .debug_info Unit Header Chain...\n"; + DObj.forEachInfoSections([&](const DWARFSection &S) { + NumErrors += verifyUnitSection(S, DW_SECT_INFO); + }); + + OS << "Verifying .debug_types Unit Header Chain...\n"; + DObj.forEachTypesSections([&](const DWARFSection &S) { + NumErrors += verifyUnitSection(S, DW_SECT_TYPES); + }); + return NumErrors == 0; +} + +unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, + DieRangeInfo &ParentRI) { + unsigned NumErrors = 0; + + if (!Die.isValid()) + return NumErrors; + + 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); + + // TODO support object files better + // + // Some object file formats (i.e. non-MachO) support COMDAT. ELF in + // particular does so by placing each function into a section. The DWARF data + // for the function at that point uses a section relative DW_FORM_addrp for + // the DW_AT_low_pc and a DW_FORM_data4 for the offset as the DW_AT_high_pc. + // In such a case, when the Die is the CU, the ranges will overlap, and we + // will flag valid conflicting ranges as invalid. + // + // For such targets, we should read the ranges from the CU and partition them + // by the section id. The ranges within a particular section should be + // disjoint, although the ranges across sections may overlap. We would map + // the child die to the entity that it references and the section with which + // it is associated. The child would then be checked against the range + // information for the associated section. + // + // For now, simply elide the range verification for the CU DIEs if we are + // processing an object file. + + if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) { + for (auto Range : Ranges) { + if (!Range.valid()) { + ++NumErrors; + error() << "Invalid address range " << Range << "\n"; + continue; + } + + // Verify that ranges don't intersect. + const auto IntersectingRange = RI.insert(Range); + if (IntersectingRange != RI.Ranges.end()) { + ++NumErrors; + error() << "DIE has overlapping address ranges: " << Range << " and " + << *IntersectingRange << "\n"; + break; + } + } + } + + // Verify that children don't intersect. + const auto IntersectingChild = ParentRI.insert(RI); + if (IntersectingChild != ParentRI.Children.end()) { + ++NumErrors; + error() << "DIEs have overlapping address ranges:"; + dump(Die); + dump(IntersectingChild->Die) << '\n'; + } + + // Verify that ranges are contained within their parent. + bool ShouldBeContained = !Ranges.empty() && !ParentRI.Ranges.empty() && + !(Die.getTag() == DW_TAG_subprogram && + ParentRI.Die.getTag() == DW_TAG_subprogram); + if (ShouldBeContained && !ParentRI.contains(RI)) { + ++NumErrors; + error() << "DIE address ranges are not contained in its parent's ranges:"; + dump(ParentRI.Die); + dump(Die, 2) << '\n'; + } + + // Recursively check children. + for (DWARFDie Child : Die) + NumErrors += verifyDieRanges(Child, RI); + + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, + DWARFAttribute &AttrValue) { + unsigned NumErrors = 0; + auto ReportError = [&](const Twine &TitleMsg) { + ++NumErrors; + error() << TitleMsg << '\n'; + dump(Die) << '\n'; + }; + + const DWARFObject &DObj = DCtx.getDWARFObj(); + const auto Attr = AttrValue.Attr; + switch (Attr) { + case DW_AT_ranges: + // Make sure the offset in the DW_AT_ranges attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DObj.getRangeSection().Data.size()) + ReportError("DW_AT_ranges offset is beyond .debug_ranges bounds:"); + break; + } + ReportError("DIE has invalid DW_AT_ranges encoding:"); + break; + case DW_AT_stmt_list: + // Make sure the offset in the DW_AT_stmt_list attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DObj.getLineSection().Data.size()) + ReportError("DW_AT_stmt_list offset is beyond .debug_line bounds: " + + llvm::formatv("{0:x8}", *SectionOffset)); + break; + } + ReportError("DIE has invalid DW_AT_stmt_list encoding:"); + break; + case DW_AT_location: { + 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()}); + } + break; + } + case DW_AT_specification: + case DW_AT_abstract_origin: { + if (auto ReferencedDie = Die.getAttributeValueAsReferencedDie(Attr)) { + auto DieTag = Die.getTag(); + auto RefTag = ReferencedDie.getTag(); + if (DieTag == RefTag) + break; + if (DieTag == DW_TAG_inlined_subroutine && RefTag == DW_TAG_subprogram) + break; + if (DieTag == DW_TAG_variable && RefTag == DW_TAG_member) + break; + ReportError("DIE with tag " + TagString(DieTag) + " has " + + AttributeString(Attr) + + " that points to DIE with " + "incompatible tag " + + TagString(RefTag)); + } + break; + } + case DW_AT_type: { + DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type); + if (TypeDie && !isType(TypeDie.getTag())) { + ReportError("DIE has " + AttributeString(Attr) + + " with incompatible tag " + TagString(TypeDie.getTag())); + } + break; + } + default: + break; + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, + DWARFAttribute &AttrValue) { + const DWARFObject &DObj = DCtx.getDWARFObj(); + auto DieCU = Die.getDwarfUnit(); + unsigned NumErrors = 0; + const auto Form = AttrValue.Value.getForm(); + switch (Form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: { + // Verify all CU relative references are valid CU offsets. + Optional<uint64_t> RefVal = AttrValue.Value.getAsReference(); + assert(RefVal); + if (RefVal) { + auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset(); + auto CUOffset = AttrValue.Value.getRawUValue(); + if (CUOffset >= CUSize) { + ++NumErrors; + error() << FormEncodingString(Form) << " CU offset " + << format("0x%08" PRIx64, CUOffset) + << " is invalid (must be less than CU size of " + << format("0x%08" PRIx32, CUSize) << "):\n"; + Die.dump(OS, 0, DumpOpts); + dump(Die) << '\n'; + } else { + // Valid reference, but we will verify it points to an actual + // DIE later. + ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); + } + } + break; + } + case DW_FORM_ref_addr: { + // Verify all absolute DIE references have valid offsets in the + // .debug_info section. + Optional<uint64_t> RefVal = AttrValue.Value.getAsReference(); + assert(RefVal); + if (RefVal) { + if (*RefVal >= DieCU->getInfoSection().Data.size()) { + ++NumErrors; + error() << "DW_FORM_ref_addr offset beyond .debug_info " + "bounds:\n"; + dump(Die) << '\n'; + } else { + // Valid reference, but we will verify it points to an actual + // DIE later. + ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); + } + } + break; + } + case DW_FORM_strp: { + auto SecOffset = AttrValue.Value.getAsSectionOffset(); + assert(SecOffset); // DW_FORM_strp is a section offset. + if (SecOffset && *SecOffset >= DObj.getStringSection().size()) { + ++NumErrors; + error() << "DW_FORM_strp offset beyond .debug_str bounds:\n"; + dump(Die) << '\n'; + } + break; + } + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: { + auto Index = AttrValue.Value.getRawUValue(); + auto DieCU = Die.getDwarfUnit(); + // Check that we have a valid DWARF v5 string offsets table. + if (!DieCU->getStringOffsetsTableContribution()) { + ++NumErrors; + error() << FormEncodingString(Form) + << " used without a valid string offsets table:\n"; + dump(Die) << '\n'; + break; + } + // Check that the index is within the bounds of the section. + unsigned ItemSize = DieCU->getDwarfStringOffsetsByteSize(); + // Use a 64-bit type to calculate the offset to guard against overflow. + uint64_t Offset = + (uint64_t)DieCU->getStringOffsetsBase() + Index * ItemSize; + if (DObj.getStringOffsetSection().Data.size() < Offset + ItemSize) { + ++NumErrors; + error() << FormEncodingString(Form) << " uses index " + << format("%" PRIu64, Index) << ", which is too large:\n"; + dump(Die) << '\n'; + break; + } + // Check that the string offset is valid. + uint64_t StringOffset = *DieCU->getStringOffsetSectionItem(Index); + if (StringOffset >= DObj.getStringSection().size()) { + ++NumErrors; + error() << FormEncodingString(Form) << " uses index " + << format("%" PRIu64, Index) + << ", but the referenced string" + " offset is beyond .debug_str bounds:\n"; + dump(Die) << '\n'; + } + break; + } + default: + break; + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoReferences() { + // Take all references and make sure they point to an actual DIE by + // getting the DIE by offset and emitting an error + OS << "Verifying .debug_info references...\n"; + unsigned NumErrors = 0; + for (auto Pair : ReferenceToDIEOffsets) { + auto Die = DCtx.getDIEForOffset(Pair.first); + if (Die) + continue; + ++NumErrors; + error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first) + << ". Offset is in between DIEs:\n"; + for (auto Offset : Pair.second) + dump(DCtx.getDIEForOffset(Offset)) << '\n'; + OS << "\n"; + } + return NumErrors; +} + +void DWARFVerifier::verifyDebugLineStmtOffsets() { + std::map<uint64_t, DWARFDie> StmtListToDie; + for (const auto &CU : DCtx.compile_units()) { + auto Die = CU->getUnitDIE(); + // Get the attribute value as a section offset. No need to produce an + // error here if the encoding isn't correct because we validate this in + // the .debug_info verifier. + auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list)); + if (!StmtSectionOffset) + continue; + const uint32_t LineTableOffset = *StmtSectionOffset; + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) { + if (!LineTable) { + ++NumDebugLineErrors; + error() << ".debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + dump(Die) << '\n'; + continue; + } + } else { + // Make sure we don't get a valid line table back if the offset is wrong. + assert(LineTable == nullptr); + // Skip this line table as it isn't valid. No need to create an error + // here because we validate this in the .debug_info verifier. + continue; + } + auto Iter = StmtListToDie.find(LineTableOffset); + if (Iter != StmtListToDie.end()) { + ++NumDebugLineErrors; + error() << "two compile unit DIEs, " + << format("0x%08" PRIx32, Iter->second.getOffset()) << " and " + << format("0x%08" PRIx32, Die.getOffset()) + << ", have the same DW_AT_stmt_list section offset:\n"; + dump(Iter->second); + dump(Die) << '\n'; + // Already verified this line table before, no need to do it again. + continue; + } + StmtListToDie[LineTableOffset] = Die; + } +} + +void DWARFVerifier::verifyDebugLineRows() { + for (const auto &CU : DCtx.compile_units()) { + auto Die = CU->getUnitDIE(); + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + // If there is no line table we will have created an error in the + // .debug_info verifier or in verifyDebugLineStmtOffsets(). + if (!LineTable) + continue; + + // Verify prologue. + uint32_t MaxFileIndex = LineTable->Prologue.FileNames.size(); + uint32_t MaxDirIndex = LineTable->Prologue.IncludeDirectories.size(); + uint32_t FileIndex = 1; + StringMap<uint16_t> FullPathMap; + for (const auto &FileName : LineTable->Prologue.FileNames) { + // Verify directory index. + if (FileName.DirIdx > MaxDirIndex) { + ++NumDebugLineErrors; + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "].prologue.file_names[" << FileIndex + << "].dir_idx contains an invalid index: " << FileName.DirIdx + << "\n"; + } + + // Check file paths for duplicates. + std::string FullPath; + const bool HasFullPath = LineTable->getFileNameByIndex( + FileIndex, CU->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FullPath); + assert(HasFullPath && "Invalid index?"); + (void)HasFullPath; + auto It = FullPathMap.find(FullPath); + if (It == FullPathMap.end()) + FullPathMap[FullPath] = FileIndex; + else if (It->second != FileIndex) { + warn() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "].prologue.file_names[" << FileIndex + << "] is a duplicate of file_names[" << It->second << "]\n"; + } + + FileIndex++; + } + + // Verify rows. + uint64_t PrevAddress = 0; + uint32_t RowIndex = 0; + for (const auto &Row : LineTable->Rows) { + // Verify row address. + if (Row.Address < PrevAddress) { + ++NumDebugLineErrors; + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "] row[" << RowIndex + << "] decreases in address from previous row:\n"; + + DWARFDebugLine::Row::dumpTableHeader(OS); + if (RowIndex > 0) + LineTable->Rows[RowIndex - 1].dump(OS); + Row.dump(OS); + OS << '\n'; + } + + // Verify file index. + if (Row.File > MaxFileIndex) { + ++NumDebugLineErrors; + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "][" << RowIndex << "] has invalid file index " << Row.File + << " (valid values are [1," << MaxFileIndex << "]):\n"; + DWARFDebugLine::Row::dumpTableHeader(OS); + Row.dump(OS); + OS << '\n'; + } + if (Row.EndSequence) + PrevAddress = 0; + else + PrevAddress = Row.Address; + ++RowIndex; + } + } +} + +DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D, + DIDumpOptions DumpOpts) + : OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false), + IsMachOObject(false) { + if (const auto *F = DCtx.getDWARFObj().getFile()) { + IsObjectFile = F->isRelocatableObject(); + IsMachOObject = F->isMachO(); + } +} + +bool DWARFVerifier::handleDebugLine() { + NumDebugLineErrors = 0; + OS << "Verifying .debug_line...\n"; + verifyDebugLineStmtOffsets(); + verifyDebugLineRows(); + return NumDebugLineErrors == 0; +} + +unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, + DataExtractor *StrData, + const char *SectionName) { + unsigned NumErrors = 0; + DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection, + DCtx.isLittleEndian(), 0); + AppleAcceleratorTable AccelTable(AccelSectionData, *StrData); + + OS << "Verifying " << SectionName << "...\n"; + + // Verify that the fixed part of the header is not too short. + if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) { + error() << "Section is too small to fit a section header.\n"; + return 1; + } + + // Verify that the section is not too short. + if (Error E = AccelTable.extract()) { + error() << toString(std::move(E)) << '\n'; + return 1; + } + + // Verify that all buckets have a valid hash index or are empty. + uint32_t NumBuckets = AccelTable.getNumBuckets(); + uint32_t NumHashes = AccelTable.getNumHashes(); + + uint32_t BucketsOffset = + AccelTable.getSizeHdr() + AccelTable.getHeaderDataLength(); + uint32_t HashesBase = BucketsOffset + NumBuckets * 4; + uint32_t OffsetsBase = HashesBase + NumHashes * 4; + for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) { + uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset); + if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) { + error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx, + HashIdx); + ++NumErrors; + } + } + uint32_t NumAtoms = AccelTable.getAtomsDesc().size(); + if (NumAtoms == 0) { + error() << "No atoms: failed to read HashData.\n"; + return 1; + } + if (!AccelTable.validateForms()) { + error() << "Unsupported form: failed to read HashData.\n"; + return 1; + } + + for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) { + uint32_t HashOffset = HashesBase + 4 * HashIdx; + uint32_t DataOffset = OffsetsBase + 4 * HashIdx; + uint32_t Hash = AccelSectionData.getU32(&HashOffset); + uint32_t HashDataOffset = AccelSectionData.getU32(&DataOffset); + if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset, + sizeof(uint64_t))) { + error() << format("Hash[%d] has invalid HashData offset: 0x%08x.\n", + HashIdx, HashDataOffset); + ++NumErrors; + } + + uint32_t StrpOffset; + uint32_t StringOffset; + uint32_t StringCount = 0; + unsigned Offset; + unsigned Tag; + while ((StrpOffset = AccelSectionData.getU32(&HashDataOffset)) != 0) { + const uint32_t NumHashDataObjects = + AccelSectionData.getU32(&HashDataOffset); + for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects; + ++HashDataIdx) { + std::tie(Offset, Tag) = AccelTable.readAtoms(HashDataOffset); + auto Die = DCtx.getDIEForOffset(Offset); + if (!Die) { + const uint32_t BucketIdx = + NumBuckets ? (Hash % NumBuckets) : UINT32_MAX; + StringOffset = StrpOffset; + const char *Name = StrData->getCStr(&StringOffset); + if (!Name) + Name = "<NULL>"; + + error() << format( + "%s Bucket[%d] Hash[%d] = 0x%08x " + "Str[%u] = 0x%08x " + "DIE[%d] = 0x%08x is not a valid DIE offset for \"%s\".\n", + SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset, + HashDataIdx, Offset, Name); + + ++NumErrors; + continue; + } + if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) { + error() << "Tag " << dwarf::TagString(Tag) + << " in accelerator table does not match Tag " + << dwarf::TagString(Die.getTag()) << " of DIE[" << HashDataIdx + << "].\n"; + ++NumErrors; + } + } + ++StringCount; + } + } + 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, + bool IncludeLinkageName = true) { + 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 (IncludeLinkageName) { + 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 IncludeLinkageName = Die.getTag() == DW_TAG_subprogram || + Die.getTag() == DW_TAG_inlined_subroutine; + auto EntryNames = getNames(Die, IncludeLinkageName); + 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 units and modules have names but shouldn't be indexed. + case DW_TAG_compile_unit: + case DW_TAG_module: + 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<DWARFUnit> &U : DCtx.compile_units()) { + if (const DWARFDebugNames::NameIndex *NI = + AccelTable.getCUNameIndex(U->getOffset())) { + auto *CU = cast<DWARFCompileUnit>(U.get()); + for (const DWARFDebugInfoEntry &Die : CU->dies()) + NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &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 += verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData, + ".apple_names"); + if (!D.getAppleTypesSection().Data.empty()) + NumErrors += verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData, + ".apple_types"); + if (!D.getAppleNamespacesSection().Data.empty()) + NumErrors += verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData, + ".apple_namespaces"); + if (!D.getAppleObjCSection().Data.empty()) + NumErrors += 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::error(OS); } + +raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); } + +raw_ostream &DWARFVerifier::note() const { return WithColor::note(OS); } + +raw_ostream &DWARFVerifier::dump(const DWARFDie &Die, unsigned indent) const { + Die.dump(OS, indent, DumpOpts); + return OS; +} |
