diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
commit | 044eb2f6afba375a914ac9d8024f8f5142bb912e (patch) | |
tree | 1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /lib/DebugInfo | |
parent | eb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff) |
Notes
Diffstat (limited to 'lib/DebugInfo')
83 files changed, 4479 insertions, 1832 deletions
diff --git a/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp new file mode 100644 index 0000000000000..8828671d9be9d --- /dev/null +++ b/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp @@ -0,0 +1,101 @@ +//===- AppendingTypeTableBuilder.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/CodeView/AppendingTypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +TypeIndex AppendingTypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +AppendingTypeTableBuilder::AppendingTypeTableBuilder(BumpPtrAllocator &Storage) + : RecordStorage(Storage) {} + +AppendingTypeTableBuilder::~AppendingTypeTableBuilder() = default; + +Optional<TypeIndex> AppendingTypeTableBuilder::getFirst() { + if (empty()) + return None; + + return TypeIndex(TypeIndex::FirstNonSimpleIndex); +} + +Optional<TypeIndex> AppendingTypeTableBuilder::getNext(TypeIndex Prev) { + if (++Prev == nextTypeIndex()) + return None; + return Prev; +} + +CVType AppendingTypeTableBuilder::getType(TypeIndex Index) { + CVType Type; + Type.RecordData = SeenRecords[Index.toArrayIndex()]; + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(Type.RecordData.data()); + Type.Type = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + return Type; +} + +StringRef AppendingTypeTableBuilder::getTypeName(TypeIndex Index) { + llvm_unreachable("Method not implemented"); +} + +bool AppendingTypeTableBuilder::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + + return Index.toArrayIndex() < SeenRecords.size(); +} + +uint32_t AppendingTypeTableBuilder::size() { return SeenRecords.size(); } + +uint32_t AppendingTypeTableBuilder::capacity() { return SeenRecords.size(); } + +ArrayRef<ArrayRef<uint8_t>> AppendingTypeTableBuilder::records() const { + return SeenRecords; +} + +void AppendingTypeTableBuilder::reset() { SeenRecords.clear(); } + +TypeIndex +AppendingTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> &Record) { + TypeIndex NewTI = nextTypeIndex(); + uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); + memcpy(Stable, Record.data(), Record.size()); + Record = ArrayRef<uint8_t>(Stable, Record.size()); + SeenRecords.push_back(Record); + return NewTI; +} + +TypeIndex +AppendingTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} diff --git a/lib/DebugInfo/CodeView/CMakeLists.txt b/lib/DebugInfo/CodeView/CMakeLists.txt index b94bb0c80c793..0515788d85ef8 100644 --- a/lib/DebugInfo/CodeView/CMakeLists.txt +++ b/lib/DebugInfo/CodeView/CMakeLists.txt @@ -1,6 +1,8 @@ add_llvm_library(LLVMDebugInfoCodeView + AppendingTypeTableBuilder.cpp CodeViewError.cpp CodeViewRecordIO.cpp + ContinuationRecordBuilder.cpp CVSymbolVisitor.cpp CVTypeVisitor.cpp DebugChecksumsSubsection.cpp @@ -17,9 +19,13 @@ add_llvm_library(LLVMDebugInfoCodeView DebugSymbolsSubsection.cpp EnumTables.cpp Formatters.cpp + GlobalTypeTableBuilder.cpp LazyRandomTypeCollection.cpp Line.cpp + MergingTypeTableBuilder.cpp + RecordName.cpp RecordSerialization.cpp + SimpleTypeSerializer.cpp StringsAndChecksums.cpp SymbolRecordMapping.cpp SymbolDumper.cpp @@ -27,9 +33,8 @@ add_llvm_library(LLVMDebugInfoCodeView TypeDumpVisitor.cpp TypeIndex.cpp TypeIndexDiscovery.cpp - TypeName.cpp + TypeHashing.cpp TypeRecordMapping.cpp - TypeSerializer.cpp TypeStreamMerger.cpp TypeTableCollection.cpp diff --git a/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp b/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp index e0c7ef58c3041..44a67743169ec 100644 --- a/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp +++ b/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp @@ -11,7 +11,6 @@ #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" -#include "llvm/Support/BinaryByteStream.h" using namespace llvm; using namespace llvm::codeview; diff --git a/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/lib/DebugInfo/CodeView/CVTypeVisitor.cpp index 79b9fdefd40e5..a4182a3b2fa15 100644 --- a/lib/DebugInfo/CodeView/CVTypeVisitor.cpp +++ b/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/ADT/TinyPtrVector.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/CodeView/TypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" diff --git a/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp b/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp new file mode 100644 index 0000000000000..f180fc6990fcb --- /dev/null +++ b/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp @@ -0,0 +1,259 @@ +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +struct ContinuationRecord { + ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)}; + ulittle16_t Size{0}; + ulittle32_t IndexRef{0xB0C0B0C0}; +}; + +struct SegmentInjection { + SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; } + + ContinuationRecord Cont; + RecordPrefix Prefix; +}; +} // namespace + +static void addPadding(BinaryStreamWriter &Writer) { + uint32_t Align = Writer.getOffset() % 4; + if (Align == 0) + return; + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); + cantFail(Writer.writeInteger(Pad)); + --PaddingBytes; + } +} + +static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST); +static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST); + +static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord); +static constexpr uint32_t MaxSegmentLength = + MaxRecordLength - ContinuationLength; + +static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) { + return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST + : LF_METHODLIST; +} + +ContinuationRecordBuilder::ContinuationRecordBuilder() + : SegmentWriter(Buffer), Mapping(SegmentWriter) {} + +ContinuationRecordBuilder::~ContinuationRecordBuilder() {} + +void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) { + assert(!Kind.hasValue()); + Kind = RecordKind; + Buffer.clear(); + SegmentWriter.setOffset(0); + SegmentOffsets.clear(); + SegmentOffsets.push_back(0); + assert(SegmentWriter.getOffset() == 0); + assert(SegmentWriter.getLength() == 0); + + const SegmentInjection *FLI = + (RecordKind == ContinuationRecordKind::FieldList) + ? &InjectFieldList + : &InjectMethodOverloadList; + const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI); + InjectedSegmentBytes = + ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection)); + + CVType Type; + Type.Type = getTypeLeafKind(RecordKind); + cantFail(Mapping.visitTypeBegin(Type)); + + // Seed the first trecord with an appropriate record prefix. + RecordPrefix Prefix; + Prefix.RecordLen = 0; + Prefix.RecordKind = Type.Type; + cantFail(SegmentWriter.writeObject(Prefix)); +} + +template <typename RecordType> +void ContinuationRecordBuilder::writeMemberType(RecordType &Record) { + assert(Kind.hasValue()); + + uint32_t OriginalOffset = SegmentWriter.getOffset(); + CVMemberRecord CVMR; + CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind()); + + // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind + // at the beginning. + cantFail(SegmentWriter.writeEnum(CVMR.Kind)); + + // Let the Mapping handle the rest. + cantFail(Mapping.visitMemberBegin(CVMR)); + cantFail(Mapping.visitKnownMember(CVMR, Record)); + cantFail(Mapping.visitMemberEnd(CVMR)); + + // Make sure it's padded to 4 bytes. + addPadding(SegmentWriter); + assert(getCurrentSegmentLength() % 4 == 0); + + // The maximum length of a single segment is 64KB minus the size to insert a + // continuation. So if we are over that, inject a continuation between the + // previous member and the member that was just written, then end the previous + // segment after the continuation and begin a new one with the just-written + // member. + if (getCurrentSegmentLength() > MaxSegmentLength) { + // We need to inject some bytes before the member we just wrote but after + // the previous member. Save off the length of the member we just wrote so + // that we can do some sanity checking on it. + uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset; + (void) MemberLength; + insertSegmentEnd(OriginalOffset); + // Since this member now becomes a new top-level record, it should have + // gotten a RecordPrefix injected, and that RecordPrefix + the member we + // just wrote should now constitute the entirety of the current "new" + // segment. + assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix)); + } + + assert(getCurrentSegmentLength() % 4 == 0); + assert(getCurrentSegmentLength() <= MaxSegmentLength); +} + +uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const { + return SegmentWriter.getOffset() - SegmentOffsets.back(); +} + +void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) { + uint32_t SegmentBegin = SegmentOffsets.back(); + (void)SegmentBegin; + assert(Offset > SegmentBegin); + assert(Offset - SegmentBegin <= MaxSegmentLength); + + // We need to make space for the continuation record. For now we can't fill + // out the length or the TypeIndex of the back-reference, but we need the + // space to at least be there. + Buffer.insert(Offset, InjectedSegmentBytes); + + uint32_t NewSegmentBegin = Offset + ContinuationLength; + uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back(); + (void) SegmentLength; + + assert(SegmentLength % 4 == 0); + assert(SegmentLength <= MaxRecordLength); + SegmentOffsets.push_back(NewSegmentBegin); + + // Seek to the end so that we can keep writing against the new segment. + SegmentWriter.setOffset(SegmentWriter.getLength()); + assert(SegmentWriter.bytesRemaining() == 0); +} + +CVType ContinuationRecordBuilder::createSegmentRecord( + uint32_t OffBegin, uint32_t OffEnd, Optional<TypeIndex> RefersTo) { + assert(OffEnd - OffBegin <= USHRT_MAX); + + MutableArrayRef<uint8_t> Data = Buffer.data(); + Data = Data.slice(OffBegin, OffEnd - OffBegin); + + CVType Type; + Type.Type = getTypeLeafKind(*Kind); + Type.RecordData = Data; + + // Write the length to the RecordPrefix, making sure it does not include + // sizeof(RecordPrefix.Length) + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data()); + assert(Prefix->RecordKind == Type.Type); + Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen); + + if (RefersTo.hasValue()) { + auto Continuation = Data.take_back(ContinuationLength); + ContinuationRecord *CR = + reinterpret_cast<ContinuationRecord *>(Continuation.data()); + assert(CR->Kind == TypeLeafKind::LF_INDEX); + assert(CR->IndexRef == 0xB0C0B0C0); + CR->IndexRef = RefersTo->getIndex(); + } + + return Type; +} + +std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) { + CVType Type; + Type.Type = getTypeLeafKind(*Kind); + cantFail(Mapping.visitTypeEnd(Type)); + + // We're now done, and we have a series of segments each beginning at an + // offset specified in the SegmentOffsets array. We now need to iterate + // over each segment and post-process them in the following two ways: + // 1) Each top-level record has a RecordPrefix whose type is either + // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0. + // Those should all be set to the correct length now. + // 2) Each continuation record has an IndexRef field which we set to the + // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex + // they want this sequence to start from, we can go through and update + // each one. + // + // Logically, the sequence of records we've built up looks like this: + // + // SegmentOffsets[0]: <Length> (Initially: uninitialized) + // SegmentOffsets[0]+2: LF_FIELDLIST + // SegmentOffsets[0]+4: Member[0] + // SegmentOffsets[0]+?: ... + // SegmentOffsets[0]+?: Member[4] + // SegmentOffsets[1]-8: LF_INDEX + // SegmentOffsets[1]-6: 0 + // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0) + // + // SegmentOffsets[1]: <Length> (Initially: uninitialized) + // SegmentOffsets[1]+2: LF_FIELDLIST + // SegmentOffsets[1]+4: Member[0] + // SegmentOffsets[1]+?: ... + // SegmentOffsets[1]+?: Member[s] + // SegmentOffsets[2]-8: LF_INDEX + // SegmentOffsets[2]-6: 0 + // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0) + // + // ... + // + // SegmentOffsets[N]: <Length> (Initially: uninitialized) + // SegmentOffsets[N]+2: LF_FIELDLIST + // SegmentOffsets[N]+4: Member[0] + // SegmentOffsets[N]+?: ... + // SegmentOffsets[N]+?: Member[t] + // + // And this is the way we have laid them out in the serialization buffer. But + // we cannot actually commit them to the underlying stream this way, due to + // the topological sorting requirement of a type stream (specifically, + // TypeIndex references can only point backwards, not forwards). So the + // sequence that we return to the caller contains the records in reverse + // order, which is the proper order for committing the serialized records. + + std::vector<CVType> Types; + Types.reserve(SegmentOffsets.size()); + + auto SO = makeArrayRef(SegmentOffsets); + + uint32_t End = SegmentWriter.getOffset(); + + Optional<TypeIndex> RefersTo; + for (uint32_t Offset : reverse(SO)) { + Types.push_back(createSegmentRecord(Offset, End, RefersTo)); + + End = Offset; + RefersTo = Index++; + } + + Kind.reset(); + return Types; +} + +// Explicitly instantiate the member function for each known type so that we can +// implement this in the cpp file. +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \ + Name##Record &Record); +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" diff --git a/lib/DebugInfo/CodeView/EnumTables.cpp b/lib/DebugInfo/CodeView/EnumTables.cpp index 4cfb55a31b356..d8301cab1657c 100644 --- a/lib/DebugInfo/CodeView/EnumTables.cpp +++ b/lib/DebugInfo/CodeView/EnumTables.cpp @@ -33,55 +33,9 @@ static const EnumEntry<TypeLeafKind> TypeLeafNames[] = { }; static const EnumEntry<uint16_t> RegisterNames[] = { - CV_ENUM_CLASS_ENT(RegisterId, Unknown), - CV_ENUM_CLASS_ENT(RegisterId, VFrame), - CV_ENUM_CLASS_ENT(RegisterId, AL), - CV_ENUM_CLASS_ENT(RegisterId, CL), - CV_ENUM_CLASS_ENT(RegisterId, DL), - CV_ENUM_CLASS_ENT(RegisterId, BL), - CV_ENUM_CLASS_ENT(RegisterId, AH), - CV_ENUM_CLASS_ENT(RegisterId, CH), - CV_ENUM_CLASS_ENT(RegisterId, DH), - CV_ENUM_CLASS_ENT(RegisterId, BH), - CV_ENUM_CLASS_ENT(RegisterId, AX), - CV_ENUM_CLASS_ENT(RegisterId, CX), - CV_ENUM_CLASS_ENT(RegisterId, DX), - CV_ENUM_CLASS_ENT(RegisterId, BX), - CV_ENUM_CLASS_ENT(RegisterId, SP), - CV_ENUM_CLASS_ENT(RegisterId, BP), - CV_ENUM_CLASS_ENT(RegisterId, SI), - CV_ENUM_CLASS_ENT(RegisterId, DI), - CV_ENUM_CLASS_ENT(RegisterId, EAX), - CV_ENUM_CLASS_ENT(RegisterId, ECX), - CV_ENUM_CLASS_ENT(RegisterId, EDX), - CV_ENUM_CLASS_ENT(RegisterId, EBX), - CV_ENUM_CLASS_ENT(RegisterId, ESP), - CV_ENUM_CLASS_ENT(RegisterId, EBP), - CV_ENUM_CLASS_ENT(RegisterId, ESI), - CV_ENUM_CLASS_ENT(RegisterId, EDI), - CV_ENUM_CLASS_ENT(RegisterId, ES), - CV_ENUM_CLASS_ENT(RegisterId, CS), - CV_ENUM_CLASS_ENT(RegisterId, SS), - CV_ENUM_CLASS_ENT(RegisterId, DS), - CV_ENUM_CLASS_ENT(RegisterId, FS), - CV_ENUM_CLASS_ENT(RegisterId, GS), - CV_ENUM_CLASS_ENT(RegisterId, IP), - CV_ENUM_CLASS_ENT(RegisterId, RAX), - CV_ENUM_CLASS_ENT(RegisterId, RBX), - CV_ENUM_CLASS_ENT(RegisterId, RCX), - CV_ENUM_CLASS_ENT(RegisterId, RDX), - CV_ENUM_CLASS_ENT(RegisterId, RSI), - CV_ENUM_CLASS_ENT(RegisterId, RDI), - CV_ENUM_CLASS_ENT(RegisterId, RBP), - CV_ENUM_CLASS_ENT(RegisterId, RSP), - CV_ENUM_CLASS_ENT(RegisterId, R8), - CV_ENUM_CLASS_ENT(RegisterId, R9), - CV_ENUM_CLASS_ENT(RegisterId, R10), - CV_ENUM_CLASS_ENT(RegisterId, R11), - CV_ENUM_CLASS_ENT(RegisterId, R12), - CV_ENUM_CLASS_ENT(RegisterId, R13), - CV_ENUM_CLASS_ENT(RegisterId, R14), - CV_ENUM_CLASS_ENT(RegisterId, R15), +#define CV_REGISTER(name, val) CV_ENUM_CLASS_ENT(RegisterId, name), +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER }; static const EnumEntry<uint32_t> PublicSymFlagNames[] = { @@ -132,7 +86,7 @@ static const EnumEntry<codeview::SourceLanguage> SourceLanguages[] = { CV_ENUM_ENT(SourceLanguage, CSharp), CV_ENUM_ENT(SourceLanguage, VB), CV_ENUM_ENT(SourceLanguage, ILAsm), CV_ENUM_ENT(SourceLanguage, Java), CV_ENUM_ENT(SourceLanguage, JScript), CV_ENUM_ENT(SourceLanguage, MSIL), - CV_ENUM_ENT(SourceLanguage, HLSL), + CV_ENUM_ENT(SourceLanguage, HLSL), CV_ENUM_ENT(SourceLanguage, D), }; static const EnumEntry<uint32_t> CompileSym2FlagNames[] = { diff --git a/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp new file mode 100644 index 0000000000000..3ecd684c1e39f --- /dev/null +++ b/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp @@ -0,0 +1,127 @@ +//===- GlobalTypeTableBuilder.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/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +TypeIndex GlobalTypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +GlobalTypeTableBuilder::GlobalTypeTableBuilder(BumpPtrAllocator &Storage) + : RecordStorage(Storage) { + SeenRecords.reserve(4096); +} + +GlobalTypeTableBuilder::~GlobalTypeTableBuilder() = default; + +Optional<TypeIndex> GlobalTypeTableBuilder::getFirst() { + if (empty()) + return None; + + return TypeIndex(TypeIndex::FirstNonSimpleIndex); +} + +Optional<TypeIndex> GlobalTypeTableBuilder::getNext(TypeIndex Prev) { + if (++Prev == nextTypeIndex()) + return None; + return Prev; +} + +CVType GlobalTypeTableBuilder::getType(TypeIndex Index) { + CVType Type; + Type.RecordData = SeenRecords[Index.toArrayIndex()]; + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(Type.RecordData.data()); + Type.Type = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + return Type; +} + +StringRef GlobalTypeTableBuilder::getTypeName(TypeIndex Index) { + llvm_unreachable("Method not implemented"); +} + +bool GlobalTypeTableBuilder::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + + return Index.toArrayIndex() < SeenRecords.size(); +} + +uint32_t GlobalTypeTableBuilder::size() { return SeenRecords.size(); } + +uint32_t GlobalTypeTableBuilder::capacity() { return SeenRecords.size(); } + +ArrayRef<ArrayRef<uint8_t>> GlobalTypeTableBuilder::records() const { + return SeenRecords; +} + +ArrayRef<GloballyHashedType> GlobalTypeTableBuilder::hashes() const { + return SeenHashes; +} + +void GlobalTypeTableBuilder::reset() { + HashedRecords.clear(); + SeenRecords.clear(); +} + +static inline ArrayRef<uint8_t> stabilize(BumpPtrAllocator &Alloc, + ArrayRef<uint8_t> Data) { + uint8_t *Stable = Alloc.Allocate<uint8_t>(Data.size()); + memcpy(Stable, Data.data(), Data.size()); + return makeArrayRef(Stable, Data.size()); +} + +TypeIndex GlobalTypeTableBuilder::insertRecordAs(GloballyHashedType Hash, + CreateRecord Create) { + auto Result = HashedRecords.try_emplace(Hash, nextTypeIndex()); + + if (Result.second) { + ArrayRef<uint8_t> RecordData = stabilize(RecordStorage, Create()); + SeenRecords.push_back(RecordData); + SeenHashes.push_back(Hash); + } + + // Update the caller's copy of Record to point a stable copy. + return Result.first->second; +} + +TypeIndex GlobalTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> Record) { + GloballyHashedType GHT = + GloballyHashedType::hashType(Record, SeenHashes, SeenHashes); + return insertRecordAs(GHT, [Record]() { return Record; }); +} + +TypeIndex +GlobalTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} diff --git a/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp index 5aaf3f1453a8f..ca8007411cadc 100644 --- a/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp +++ b/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -13,7 +13,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" -#include "llvm/DebugInfo/CodeView/TypeName.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" @@ -58,21 +58,27 @@ LazyRandomTypeCollection::LazyRandomTypeCollection(const CVTypeArray &Types, uint32_t NumRecords) : LazyRandomTypeCollection(Types, NumRecords, PartialOffsetArray()) {} -void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { +void LazyRandomTypeCollection::reset(BinaryStreamReader &Reader, + uint32_t RecordCountHint) { Count = 0; PartialOffsets = PartialOffsetArray(); - BinaryStreamReader Reader(Data, support::little); - error(Reader.readArray(Types, Reader.getLength())); + error(Reader.readArray(Types, Reader.bytesRemaining())); // Clear and then resize, to make sure existing data gets destroyed. Records.clear(); Records.resize(RecordCountHint); } +void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); +} + void LazyRandomTypeCollection::reset(ArrayRef<uint8_t> Data, uint32_t RecordCountHint) { - reset(toStringRef(Data), RecordCountHint); + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); } uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index) { @@ -83,12 +89,23 @@ uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index) { } CVType LazyRandomTypeCollection::getType(TypeIndex Index) { - error(ensureTypeExists(Index)); + auto EC = ensureTypeExists(Index); + error(std::move(EC)); assert(contains(Index)); return Records[Index.toArrayIndex()].Type; } +Optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) { + if (auto EC = ensureTypeExists(Index)) { + consumeError(std::move(EC)); + return None; + } + + assert(contains(Index)); + return Records[Index.toArrayIndex()].Type; +} + StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { if (Index.isNoneType() || Index.isSimple()) return TypeIndex::simpleTypeName(Index); @@ -112,6 +129,9 @@ StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { } bool LazyRandomTypeCollection::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + if (Records.size() <= Index.toArrayIndex()) return false; if (!Records[Index.toArrayIndex()].Type.valid()) diff --git a/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp new file mode 100644 index 0000000000000..8aee4aa2e2aee --- /dev/null +++ b/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp @@ -0,0 +1,128 @@ +//===- MergingTypeTableBuilder.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/CodeView/MergingTypeTableBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +TypeIndex MergingTypeTableBuilder::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +MergingTypeTableBuilder::MergingTypeTableBuilder(BumpPtrAllocator &Storage) + : RecordStorage(Storage) { + SeenRecords.reserve(4096); +} + +MergingTypeTableBuilder::~MergingTypeTableBuilder() = default; + +Optional<TypeIndex> MergingTypeTableBuilder::getFirst() { + if (empty()) + return None; + + return TypeIndex(TypeIndex::FirstNonSimpleIndex); +} + +Optional<TypeIndex> MergingTypeTableBuilder::getNext(TypeIndex Prev) { + if (++Prev == nextTypeIndex()) + return None; + return Prev; +} + +CVType MergingTypeTableBuilder::getType(TypeIndex Index) { + CVType Type; + Type.RecordData = SeenRecords[Index.toArrayIndex()]; + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(Type.RecordData.data()); + Type.Type = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + return Type; +} + +StringRef MergingTypeTableBuilder::getTypeName(TypeIndex Index) { + llvm_unreachable("Method not implemented"); +} + +bool MergingTypeTableBuilder::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + + return Index.toArrayIndex() < SeenRecords.size(); +} + +uint32_t MergingTypeTableBuilder::size() { return SeenRecords.size(); } + +uint32_t MergingTypeTableBuilder::capacity() { return SeenRecords.size(); } + +ArrayRef<ArrayRef<uint8_t>> MergingTypeTableBuilder::records() const { + return SeenRecords; +} + +void MergingTypeTableBuilder::reset() { + HashedRecords.clear(); + SeenRecords.clear(); +} + +static inline ArrayRef<uint8_t> stabilize(BumpPtrAllocator &Alloc, + ArrayRef<uint8_t> Data) { + uint8_t *Stable = Alloc.Allocate<uint8_t>(Data.size()); + memcpy(Stable, Data.data(), Data.size()); + return makeArrayRef(Stable, Data.size()); +} + +TypeIndex MergingTypeTableBuilder::insertRecordAs(hash_code Hash, + ArrayRef<uint8_t> &Record) { + assert(Record.size() < UINT32_MAX && "Record too big"); + assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); + + LocallyHashedType WeakHash{Hash, Record}; + auto Result = HashedRecords.try_emplace(WeakHash, nextTypeIndex()); + + if (Result.second) { + ArrayRef<uint8_t> RecordData = stabilize(RecordStorage, Record); + Result.first->first.RecordData = RecordData; + SeenRecords.push_back(RecordData); + } + + // Update the caller's copy of Record to point a stable copy. + TypeIndex ActualTI = Result.first->second; + Record = SeenRecords[ActualTI.toArrayIndex()]; + return ActualTI; +} + +TypeIndex +MergingTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> &Record) { + return insertRecordAs(hash_value(Record), Record); +} + +TypeIndex +MergingTypeTableBuilder::insertRecord(ContinuationRecordBuilder &Builder) { + TypeIndex TI; + auto Fragments = Builder.end(nextTypeIndex()); + assert(!Fragments.empty()); + for (auto C : Fragments) + TI = insertRecordBytes(C.RecordData); + return TI; +} diff --git a/lib/DebugInfo/CodeView/TypeName.cpp b/lib/DebugInfo/CodeView/RecordName.cpp index 2eb8b81862f3c..15fb1724d23d4 100644 --- a/lib/DebugInfo/CodeView/TypeName.cpp +++ b/lib/DebugInfo/CodeView/RecordName.cpp @@ -1,4 +1,4 @@ -//===- TypeName.cpp ------------------------------------------- *- C++ --*-===// +//===- RecordName.cpp ----------------------------------------- *- C++ --*-===// // // The LLVM Compiler Infrastructure // @@ -7,10 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "llvm/DebugInfo/CodeView/TypeName.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/Support/FormatVariadic.h" @@ -241,3 +243,78 @@ std::string llvm::codeview::computeTypeName(TypeCollection &Types, } return Computer.name(); } + +static int getSymbolNameOffset(CVSymbol Sym) { + switch (Sym.kind()) { + // See ProcSym + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_LPROC32_DPC: + case SymbolKind::S_LPROC32_DPC_ID: + return 35; + // See Thunk32Sym + case SymbolKind::S_THUNK32: + return 21; + // See SectionSym + case SymbolKind::S_SECTION: + return 16; + // See CoffGroupSym + case SymbolKind::S_COFFGROUP: + return 14; + // See PublicSym32, FileStaticSym, RegRelativeSym, DataSym, ThreadLocalDataSym + case SymbolKind::S_PUB32: + case SymbolKind::S_FILESTATIC: + case SymbolKind::S_REGREL32: + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_LMANDATA: + case SymbolKind::S_GMANDATA: + case SymbolKind::S_LTHREAD32: + case SymbolKind::S_GTHREAD32: + return 10; + // See RegisterSym and LocalSym + case SymbolKind::S_REGISTER: + case SymbolKind::S_LOCAL: + return 6; + // See BlockSym + case SymbolKind::S_BLOCK32: + return 18; + // See LabelSym + case SymbolKind::S_LABEL32: + return 7; + // See ObjNameSym, ExportSym, and UDTSym + case SymbolKind::S_OBJNAME: + case SymbolKind::S_EXPORT: + case SymbolKind::S_UDT: + return 4; + // See BPRelativeSym + case SymbolKind::S_BPREL32: + return 8; + default: + return -1; + } +} + +StringRef llvm::codeview::getSymbolName(CVSymbol Sym) { + if (Sym.kind() == SymbolKind::S_CONSTANT) { + // S_CONSTANT is preceded by an APSInt, which has a variable length. So we + // have to do a full deserialization. + BinaryStreamReader Reader(Sym.content(), llvm::support::little); + // The container doesn't matter for single records. + SymbolRecordMapping Mapping(Reader, CodeViewContainer::ObjectFile); + ConstantSym Const(SymbolKind::S_CONSTANT); + cantFail(Mapping.visitSymbolBegin(Sym)); + cantFail(Mapping.visitKnownRecord(Sym, Const)); + cantFail(Mapping.visitSymbolEnd(Sym)); + return Const.Name; + } + + int Offset = getSymbolNameOffset(Sym); + if (Offset == -1) + return StringRef(); + + StringRef StringData = toStringRef(Sym.content()).drop_front(Offset); + return StringData.split('\0').first; +} diff --git a/lib/DebugInfo/CodeView/RecordSerialization.cpp b/lib/DebugInfo/CodeView/RecordSerialization.cpp index 6446670f60d84..bff9a619a8461 100644 --- a/lib/DebugInfo/CodeView/RecordSerialization.cpp +++ b/lib/DebugInfo/CodeView/RecordSerialization.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/BinaryByteStream.h" @@ -147,3 +148,8 @@ Error llvm::codeview::consume(BinaryStreamReader &Reader, StringRef &Item) { return Reader.readCString(Item); } + +Expected<CVSymbol> llvm::codeview::readSymbolFromStream(BinaryStreamRef Stream, + uint32_t Offset) { + return readCVRecordFromStream<SymbolKind>(Stream, Offset); +} diff --git a/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp b/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp new file mode 100644 index 0000000000000..d28b7c3c2d830 --- /dev/null +++ b/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp @@ -0,0 +1,62 @@ +#include "llvm/DebugInfo/CodeView/SimpleTypeSerializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +static void writeRecordPrefix(BinaryStreamWriter &Writer, TypeLeafKind Kind) { + RecordPrefix Prefix; + Prefix.RecordKind = Kind; + Prefix.RecordLen = 0; + cantFail(Writer.writeObject(Prefix)); +} + +static void addPadding(BinaryStreamWriter &Writer) { + uint32_t Align = Writer.getOffset() % 4; + if (Align == 0) + return; + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); + cantFail(Writer.writeInteger(Pad)); + --PaddingBytes; + } +} + +SimpleTypeSerializer::SimpleTypeSerializer() : ScratchBuffer(MaxRecordLength) {} + +SimpleTypeSerializer::~SimpleTypeSerializer() {} + +template <typename T> +ArrayRef<uint8_t> SimpleTypeSerializer::serialize(T &Record) { + BinaryStreamWriter Writer(ScratchBuffer, support::little); + TypeRecordMapping Mapping(Writer); + + CVType CVT; + CVT.Type = static_cast<TypeLeafKind>(Record.getKind()); + + writeRecordPrefix(Writer, CVT.Type); + + cantFail(Mapping.visitTypeBegin(CVT)); + cantFail(Mapping.visitKnownRecord(CVT, Record)); + cantFail(Mapping.visitTypeEnd(CVT)); + + addPadding(Writer); + + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(ScratchBuffer.data()); + + Prefix->RecordKind = CVT.kind(); + Prefix->RecordLen = Writer.getOffset() - sizeof(uint16_t); + + return {ScratchBuffer.data(), Writer.getOffset()}; +} + +// Explicitly instantiate the member function for each known type so that we can +// implement this in the cpp file. +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + template ArrayRef<uint8_t> llvm::codeview::SimpleTypeSerializer::serialize( \ + Name##Record &Record); +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" diff --git a/lib/DebugInfo/CodeView/StringsAndChecksums.cpp b/lib/DebugInfo/CodeView/StringsAndChecksums.cpp index 306af1d1ef6bc..85d9dbb8c7dfe 100644 --- a/lib/DebugInfo/CodeView/StringsAndChecksums.cpp +++ b/lib/DebugInfo/CodeView/StringsAndChecksums.cpp @@ -35,14 +35,36 @@ void StringsAndChecksumsRef::initializeStrings( assert(SR.kind() == DebugSubsectionKind::StringTable); assert(!Strings && "Found a string table even though we already have one!"); - OwnedStrings = llvm::make_unique<DebugStringTableSubsectionRef>(); + OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>(); consumeError(OwnedStrings->initialize(SR.getRecordData())); Strings = OwnedStrings.get(); } +void StringsAndChecksumsRef::reset() { + resetStrings(); + resetChecksums(); +} + +void StringsAndChecksumsRef::resetStrings() { + OwnedStrings.reset(); + Strings = nullptr; +} + +void StringsAndChecksumsRef::resetChecksums() { + OwnedChecksums.reset(); + Checksums = nullptr; +} + +void StringsAndChecksumsRef::setStrings( + const DebugStringTableSubsectionRef &StringsRef) { + OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>(); + *OwnedStrings = StringsRef; + Strings = OwnedStrings.get(); +} + void StringsAndChecksumsRef::setChecksums( const DebugChecksumsSubsectionRef &CS) { - OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>(); + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); *OwnedChecksums = CS; Checksums = OwnedChecksums.get(); } @@ -53,7 +75,7 @@ void StringsAndChecksumsRef::initializeChecksums( if (Checksums) return; - OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>(); + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); consumeError(OwnedChecksums->initialize(FCR.getRecordData())); Checksums = OwnedChecksums.get(); } diff --git a/lib/DebugInfo/CodeView/SymbolDumper.cpp b/lib/DebugInfo/CodeView/SymbolDumper.cpp index 62e73acc72d6d..df75f52661e12 100644 --- a/lib/DebugInfo/CodeView/SymbolDumper.cpp +++ b/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/CodeView/SymbolDumper.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" @@ -317,7 +316,8 @@ Error CVSymbolDumperImpl::visitKnownRecord( Error CVSymbolDumperImpl::visitKnownRecord( CVSymbol &CVR, DefRangeRegisterRelSym &DefRangeRegisterRel) { - W.printNumber("BaseRegister", DefRangeRegisterRel.Hdr.Register); + W.printEnum("BaseRegister", uint16_t(DefRangeRegisterRel.Hdr.Register), + getRegisterNames()); W.printBoolean("HasSpilledUDTMember", DefRangeRegisterRel.hasSpilledUDTMember()); W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); @@ -330,7 +330,8 @@ Error CVSymbolDumperImpl::visitKnownRecord( Error CVSymbolDumperImpl::visitKnownRecord( CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) { - W.printNumber("Register", DefRangeRegister.Hdr.Register); + W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register), + getRegisterNames()); W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); printLocalVariableAddrRange(DefRangeRegister.Range, DefRangeRegister.getRelocationOffset()); @@ -340,7 +341,8 @@ Error CVSymbolDumperImpl::visitKnownRecord( Error CVSymbolDumperImpl::visitKnownRecord( CVSymbol &CVR, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { - W.printNumber("Register", DefRangeSubfieldRegister.Hdr.Register); + W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register), + getRegisterNames()); W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName); W.printNumber("OffsetInParent", DefRangeSubfieldRegister.Hdr.OffsetInParent); printLocalVariableAddrRange(DefRangeSubfieldRegister.Range, @@ -393,7 +395,7 @@ Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, FrameCookie.getRelocationOffset(), FrameCookie.CodeOffset, &LinkageName); } - W.printHex("Register", FrameCookie.Register); + W.printEnum("Register", uint16_t(FrameCookie.Register), getRegisterNames()); W.printEnum("CookieKind", uint16_t(FrameCookie.CookieKind), getFrameCookieKindNames()); W.printHex("Flags", FrameCookie.Flags); diff --git a/lib/DebugInfo/CodeView/SymbolSerializer.cpp b/lib/DebugInfo/CodeView/SymbolSerializer.cpp index 9a2e776feb756..0071ecc856853 100644 --- a/lib/DebugInfo/CodeView/SymbolSerializer.cpp +++ b/lib/DebugInfo/CodeView/SymbolSerializer.cpp @@ -21,8 +21,7 @@ using namespace llvm::codeview; SymbolSerializer::SymbolSerializer(BumpPtrAllocator &Allocator, CodeViewContainer Container) - : Storage(Allocator), RecordBuffer(MaxRecordLength), - Stream(RecordBuffer, support::little), Writer(Stream), + : Storage(Allocator), Stream(RecordBuffer, support::little), Writer(Stream), Mapping(Writer, Container) {} Error SymbolSerializer::visitSymbolBegin(CVSymbol &Record) { diff --git a/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp b/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp index e18a35ca1f389..e7998b8732fed 100644 --- a/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp +++ b/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp @@ -15,7 +15,6 @@ #include "llvm/DebugInfo/CodeView/TypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" diff --git a/lib/DebugInfo/CodeView/TypeHashing.cpp b/lib/DebugInfo/CodeView/TypeHashing.cpp new file mode 100644 index 0000000000000..f5b28b2a2070b --- /dev/null +++ b/lib/DebugInfo/CodeView/TypeHashing.cpp @@ -0,0 +1,74 @@ +//===- TypeHashing.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/CodeView/TypeHashing.h" + +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/Support/SHA1.h" + +using namespace llvm; +using namespace llvm::codeview; + +LocallyHashedType DenseMapInfo<LocallyHashedType>::Empty{0, {}}; +LocallyHashedType DenseMapInfo<LocallyHashedType>::Tombstone{hash_code(-1), {}}; + +static std::array<uint8_t, 20> EmptyHash; +static std::array<uint8_t, 20> TombstoneHash = { + {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +GloballyHashedType DenseMapInfo<GloballyHashedType>::Empty{EmptyHash}; +GloballyHashedType DenseMapInfo<GloballyHashedType>::Tombstone{TombstoneHash}; + +LocallyHashedType LocallyHashedType::hashType(ArrayRef<uint8_t> RecordData) { + return {llvm::hash_value(RecordData), RecordData}; +} + +GloballyHashedType +GloballyHashedType::hashType(ArrayRef<uint8_t> RecordData, + ArrayRef<GloballyHashedType> PreviousTypes, + ArrayRef<GloballyHashedType> PreviousIds) { + SmallVector<TiReference, 4> Refs; + discoverTypeIndices(RecordData, Refs); + SHA1 S; + S.init(); + uint32_t Off = 0; + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + for (const auto &Ref : Refs) { + // Hash any data that comes before this TiRef. + uint32_t PreLen = Ref.Offset - Off; + ArrayRef<uint8_t> PreData = RecordData.slice(Off, PreLen); + S.update(PreData); + auto Prev = (Ref.Kind == TiRefKind::IndexRef) ? PreviousIds : PreviousTypes; + + auto RefData = RecordData.slice(Ref.Offset, Ref.Count * sizeof(TypeIndex)); + // For each type index referenced, add in the previously computed hash + // value of that type. + ArrayRef<TypeIndex> Indices( + reinterpret_cast<const TypeIndex *>(RefData.data()), Ref.Count); + for (TypeIndex TI : Indices) { + ArrayRef<uint8_t> BytesToHash; + if (TI.isSimple() || TI.isNoneType() || TI.toArrayIndex() >= Prev.size()) { + const uint8_t *IndexBytes = reinterpret_cast<const uint8_t *>(&TI); + BytesToHash = makeArrayRef(IndexBytes, sizeof(TypeIndex)); + } else { + BytesToHash = Prev[TI.toArrayIndex()].Hash; + } + S.update(BytesToHash); + } + + Off = Ref.Offset + Ref.Count * sizeof(TypeIndex); + } + + // Don't forget to add in any trailing bytes. + auto TrailingBytes = RecordData.drop_front(Off); + S.update(TrailingBytes); + + return {S.final()}; +} diff --git a/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp index 0d935c4472aef..d283e9e6d2f17 100644 --- a/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ b/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -392,9 +392,13 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, case SymbolKind::S_LOCAL: Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type break; + case SymbolKind::S_REGISTER: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type; + break; case SymbolKind::S_CONSTANT: Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type break; + case SymbolKind::S_BPREL32: case SymbolKind::S_REGREL32: Refs.push_back({TiRefKind::TypeRef, 4, 1}); // Type break; @@ -403,6 +407,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, break; case SymbolKind::S_CALLERS: case SymbolKind::S_CALLEES: + case SymbolKind::S_INLINEES: // The record is a count followed by an array of type indices. Count = *reinterpret_cast<const ulittle32_t *>(Content.data()); Refs.push_back({TiRefKind::IndexRef, 4, Count}); // Callees @@ -411,8 +416,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, Refs.push_back({TiRefKind::IndexRef, 8, 1}); // ID of inlinee break; case SymbolKind::S_HEAPALLOCSITE: - // FIXME: It's not clear if this is a type or item reference. - Refs.push_back({TiRefKind::IndexRef, 8, 1}); // signature + Refs.push_back({TiRefKind::TypeRef, 8, 1}); // UDT allocated break; // Defranges don't have types, just registers and code offsets. @@ -433,6 +437,8 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, case SymbolKind::S_ENVBLOCK: case SymbolKind::S_BLOCK32: case SymbolKind::S_FRAMEPROC: + case SymbolKind::S_THUNK32: + case SymbolKind::S_FRAMECOOKIE: break; // Scope ending symbols. case SymbolKind::S_END: @@ -450,17 +456,17 @@ void llvm::codeview::discoverTypeIndices(const CVType &Type, ::discoverTypeIndices(Type.content(), Type.kind(), Refs); } -void llvm::codeview::discoverTypeIndices(const CVType &Type, - SmallVectorImpl<TypeIndex> &Indices) { - +static void resolveTypeIndexReferences(ArrayRef<uint8_t> RecordData, + ArrayRef<TiReference> Refs, + SmallVectorImpl<TypeIndex> &Indices) { Indices.clear(); - SmallVector<TiReference, 4> Refs; - discoverTypeIndices(Type, Refs); if (Refs.empty()) return; - BinaryStreamReader Reader(Type.content(), support::little); + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + + BinaryStreamReader Reader(RecordData, support::little); for (const auto &Ref : Refs) { Reader.setOffset(Ref.Offset); FixedStreamArray<TypeIndex> Run; @@ -469,6 +475,18 @@ void llvm::codeview::discoverTypeIndices(const CVType &Type, } } +void llvm::codeview::discoverTypeIndices(const CVType &Type, + SmallVectorImpl<TypeIndex> &Indices) { + return discoverTypeIndices(Type.RecordData, Indices); +} + +void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData, + SmallVectorImpl<TypeIndex> &Indices) { + SmallVector<TiReference, 4> Refs; + discoverTypeIndices(RecordData, Refs); + resolveTypeIndexReferences(RecordData, Refs, Indices); +} + void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData, SmallVectorImpl<TiReference> &Refs) { const RecordPrefix *P = @@ -477,8 +495,26 @@ void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData, ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, Refs); } -bool llvm::codeview::discoverTypeIndices(const CVSymbol &Sym, - SmallVectorImpl<TiReference> &Refs) { +bool llvm::codeview::discoverTypeIndicesInSymbol( + const CVSymbol &Sym, SmallVectorImpl<TiReference> &Refs) { SymbolKind K = Sym.kind(); return ::discoverTypeIndices(Sym.content(), K, Refs); } + +bool llvm::codeview::discoverTypeIndicesInSymbol( + ArrayRef<uint8_t> RecordData, SmallVectorImpl<TiReference> &Refs) { + const RecordPrefix *P = + reinterpret_cast<const RecordPrefix *>(RecordData.data()); + SymbolKind K = static_cast<SymbolKind>(uint16_t(P->RecordKind)); + return ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, + Refs); +} + +bool llvm::codeview::discoverTypeIndicesInSymbol( + ArrayRef<uint8_t> RecordData, SmallVectorImpl<TypeIndex> &Indices) { + SmallVector<TiReference, 2> Refs; + if (!discoverTypeIndicesInSymbol(RecordData, Refs)) + return false; + resolveTypeIndexReferences(RecordData, Refs, Indices); + return true; +} diff --git a/lib/DebugInfo/CodeView/TypeRecordMapping.cpp b/lib/DebugInfo/CodeView/TypeRecordMapping.cpp index 114f6fd2897e7..9b8a6053da842 100644 --- a/lib/DebugInfo/CodeView/TypeRecordMapping.cpp +++ b/lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -426,7 +426,8 @@ Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, OneMethodRecord &Record) { - MapOneMethodRecord Mapper(false); + const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); + MapOneMethodRecord Mapper(IsFromOverloadList); return Mapper(IO, Record); } diff --git a/lib/DebugInfo/CodeView/TypeSerializer.cpp b/lib/DebugInfo/CodeView/TypeSerializer.cpp deleted file mode 100644 index 003c13b4a20d0..0000000000000 --- a/lib/DebugInfo/CodeView/TypeSerializer.cpp +++ /dev/null @@ -1,389 +0,0 @@ -//===- TypeSerialzier.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/CodeView/TypeSerializer.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/RecordSerialization.h" -#include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/BinaryByteStream.h" -#include "llvm/Support/BinaryStreamWriter.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/Error.h" -#include <algorithm> -#include <cassert> -#include <cstdint> -#include <cstring> - -using namespace llvm; -using namespace llvm::codeview; - -namespace { - -struct HashedType { - uint64_t Hash; - const uint8_t *Data; - unsigned Size; // FIXME: Go to uint16_t? - TypeIndex Index; -}; - -/// Wrapper around a poitner to a HashedType. Hash and equality operations are -/// based on data in the pointee. -struct HashedTypePtr { - HashedTypePtr() = default; - HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {} - - HashedType *Ptr = nullptr; -}; - -} // end anonymous namespace - -namespace llvm { - -template <> struct DenseMapInfo<HashedTypePtr> { - static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); } - - static inline HashedTypePtr getTombstoneKey() { - return HashedTypePtr(reinterpret_cast<HashedType *>(1)); - } - - static unsigned getHashValue(HashedTypePtr Val) { - assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr); - return Val.Ptr->Hash; - } - - static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) { - HashedType *LHS = LHSP.Ptr; - HashedType *RHS = RHSP.Ptr; - if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr) - return LHS == RHS; - if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size) - return false; - return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0; - } -}; - -} // end namespace llvm - -/// Private implementation so that we don't leak our DenseMap instantiations to -/// users. -class llvm::codeview::TypeHasher { -private: - /// Storage for type record provided by the caller. Records will outlive the - /// hasher object, so they should be allocated here. - BumpPtrAllocator &RecordStorage; - - /// Storage for hash keys. These only need to live as long as the hashing - /// operation. - BumpPtrAllocator KeyStorage; - - /// Hash table. We really want a DenseMap<ArrayRef<uint8_t>, TypeIndex> here, - /// but DenseMap is inefficient when the keys are long (like type records) - /// because it recomputes the hash value of every key when it grows. This - /// value type stores the hash out of line in KeyStorage, so that table - /// entries are small and easy to rehash. - DenseSet<HashedTypePtr> HashedRecords; - -public: - TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {} - - void reset() { HashedRecords.clear(); } - - /// Takes the bytes of type record, inserts them into the hash table, saves - /// them, and returns a pointer to an identical stable type record along with - /// its type index in the destination stream. - TypeIndex getOrCreateRecord(ArrayRef<uint8_t> &Record, TypeIndex TI); -}; - -TypeIndex TypeHasher::getOrCreateRecord(ArrayRef<uint8_t> &Record, - TypeIndex TI) { - assert(Record.size() < UINT32_MAX && "Record too big"); - assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); - - // Compute the hash up front so we can store it in the key. - HashedType TempHashedType = {hash_value(Record), Record.data(), - unsigned(Record.size()), TI}; - auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType)); - HashedType *&Hashed = Result.first->Ptr; - - if (Result.second) { - // This was a new type record. We need stable storage for both the key and - // the record. The record should outlive the hashing operation. - Hashed = KeyStorage.Allocate<HashedType>(); - *Hashed = TempHashedType; - - uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); - memcpy(Stable, Record.data(), Record.size()); - Hashed->Data = Stable; - assert(Hashed->Size == Record.size()); - } - - // Update the caller's copy of Record to point a stable copy. - Record = ArrayRef<uint8_t>(Hashed->Data, Hashed->Size); - return Hashed->Index; -} - -TypeIndex TypeSerializer::nextTypeIndex() const { - return TypeIndex::fromArrayIndex(SeenRecords.size()); -} - -bool TypeSerializer::isInFieldList() const { - return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST; -} - -MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() { - assert(isInFieldList()); - return getCurrentRecordData().drop_front(CurrentSegment.length()); -} - -MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() { - return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset()); -} - -Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) { - RecordPrefix Prefix; - Prefix.RecordKind = Kind; - Prefix.RecordLen = 0; - if (auto EC = Writer.writeObject(Prefix)) - return EC; - return Error::success(); -} - -Expected<MutableArrayRef<uint8_t>> -TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) { - uint32_t Align = Record.size() % 4; - if (Align == 0) - return Record; - - int PaddingBytes = 4 - Align; - int N = PaddingBytes; - while (PaddingBytes > 0) { - uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); - if (auto EC = Writer.writeInteger(Pad)) - return std::move(EC); - --PaddingBytes; - } - return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N); -} - -TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage, bool Hash) - : RecordStorage(Storage), RecordBuffer(MaxRecordLength * 2), - Stream(RecordBuffer, support::little), Writer(Stream), - Mapping(Writer) { - // RecordBuffer needs to be able to hold enough data so that if we are 1 - // byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes, - // we won't overflow. - if (Hash) - Hasher = llvm::make_unique<TypeHasher>(Storage); -} - -TypeSerializer::~TypeSerializer() = default; - -ArrayRef<ArrayRef<uint8_t>> TypeSerializer::records() const { - return SeenRecords; -} - -void TypeSerializer::reset() { - if (Hasher) - Hasher->reset(); - Writer.setOffset(0); - CurrentSegment = RecordSegment(); - FieldListSegments.clear(); - TypeKind.reset(); - MemberKind.reset(); - SeenRecords.clear(); -} - -TypeIndex TypeSerializer::insertRecordBytes(ArrayRef<uint8_t> &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - if (Hasher) { - TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex()); - if (nextTypeIndex() == ActualTI) - SeenRecords.push_back(Record); - return ActualTI; - } - - TypeIndex NewTI = nextTypeIndex(); - uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); - memcpy(Stable, Record.data(), Record.size()); - Record = ArrayRef<uint8_t>(Stable, Record.size()); - SeenRecords.push_back(Record); - return NewTI; -} - -TypeIndex TypeSerializer::insertRecord(const RemappedType &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - TypeIndex TI; - ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData; - if (Record.Mappings.empty()) { - // This record did not remap any type indices. Just write it. - return insertRecordBytes(OriginalData); - } - - // At least one type index was remapped. Before we can hash it we have to - // copy the full record bytes, re-write each type index, then hash the copy. - // We do this in temporary storage since only the DenseMap can decide whether - // this record already exists, and if it does we don't want the memory to - // stick around. - RemapStorage.resize(OriginalData.size()); - ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); - uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); - for (const auto &M : Record.Mappings) { - // First 4 bytes of every record are the record prefix, but the mapping - // offset is relative to the content which starts after. - *(TypeIndex *)(ContentBegin + M.first) = M.second; - } - auto RemapRef = makeArrayRef(RemapStorage); - return insertRecordBytes(RemapRef); -} - -Error TypeSerializer::visitTypeBegin(CVType &Record) { - assert(!TypeKind.hasValue() && "Already in a type mapping!"); - assert(Writer.getOffset() == 0 && "Stream has data already!"); - - if (auto EC = writeRecordPrefix(Record.kind())) - return EC; - - TypeKind = Record.kind(); - if (auto EC = Mapping.visitTypeBegin(Record)) - return EC; - - return Error::success(); -} - -Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) { - assert(TypeKind.hasValue() && "Not in a type mapping!"); - if (auto EC = Mapping.visitTypeEnd(Record)) - return std::move(EC); - - // Update the record's length and fill out the CVType members to point to - // the stable memory holding the record's data. - auto ThisRecordData = getCurrentRecordData(); - auto ExpectedData = addPadding(ThisRecordData); - if (!ExpectedData) - return ExpectedData.takeError(); - ThisRecordData = *ExpectedData; - - RecordPrefix *Prefix = - reinterpret_cast<RecordPrefix *>(ThisRecordData.data()); - Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t); - - Record.Type = *TypeKind; - Record.RecordData = ThisRecordData; - - // insertRecordBytes assumes we're not in a mapping, so do this first. - TypeKind.reset(); - Writer.setOffset(0); - - TypeIndex InsertedTypeIndex = insertRecordBytes(Record.RecordData); - - // Write out each additional segment in reverse order, and update each - // record's continuation index to point to the previous one. - for (auto X : reverse(FieldListSegments)) { - auto CIBytes = X.take_back(sizeof(uint32_t)); - support::ulittle32_t *CI = - reinterpret_cast<support::ulittle32_t *>(CIBytes.data()); - assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder"); - *CI = InsertedTypeIndex.getIndex(); - InsertedTypeIndex = insertRecordBytes(X); - } - - FieldListSegments.clear(); - CurrentSegment.SubRecords.clear(); - - return InsertedTypeIndex; -} - -Error TypeSerializer::visitTypeEnd(CVType &Record) { - auto ExpectedIndex = visitTypeEndGetIndex(Record); - if (!ExpectedIndex) - return ExpectedIndex.takeError(); - return Error::success(); -} - -Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) { - assert(isInFieldList() && "Not in a field list!"); - assert(!MemberKind.hasValue() && "Already in a member record!"); - MemberKind = Record.Kind; - - if (auto EC = Mapping.visitMemberBegin(Record)) - return EC; - - return Error::success(); -} - -Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) { - if (auto EC = Mapping.visitMemberEnd(Record)) - return EC; - - // Check if this subrecord makes the current segment not fit in 64K minus - // the space for a continuation record (8 bytes). If the segment does not - // fit, insert a continuation record. - if (Writer.getOffset() > MaxRecordLength - ContinuationLength) { - MutableArrayRef<uint8_t> Data = getCurrentRecordData(); - SubRecord LastSubRecord = CurrentSegment.SubRecords.back(); - uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size; - auto CopyData = Data.take_front(CopySize); - auto LeftOverData = Data.drop_front(CopySize); - assert(LastSubRecord.Size == LeftOverData.size()); - - // Allocate stable storage for the record and copy the old record plus - // continuation over. - uint16_t LengthWithSize = CopySize + ContinuationLength; - assert(LengthWithSize <= MaxRecordLength); - RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data()); - Prefix->RecordLen = LengthWithSize - sizeof(uint16_t); - - uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize); - auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize); - MutableBinaryByteStream CS(SavedSegment, support::little); - BinaryStreamWriter CW(CS); - if (auto EC = CW.writeBytes(CopyData)) - return EC; - if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX)) - return EC; - if (auto EC = CW.writeInteger<uint16_t>(0)) - return EC; - if (auto EC = CW.writeInteger<uint32_t>(0xB0C0B0C0)) - return EC; - FieldListSegments.push_back(SavedSegment); - - // Write a new placeholder record prefix to mark the start of this new - // top-level record. - Writer.setOffset(0); - if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST)) - return EC; - - // Then move over the subrecord that overflowed the old segment to the - // beginning of this segment. Note that we have to use memmove here - // instead of Writer.writeBytes(), because the new and old locations - // could overlap. - ::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(), - LeftOverData.size()); - // And point the segment writer at the end of that subrecord. - Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix)); - - CurrentSegment.SubRecords.clear(); - CurrentSegment.SubRecords.push_back(LastSubRecord); - } - - // Update the CVMemberRecord since we may have shifted around or gotten - // padded. - Record.Data = getCurrentSubRecordData(); - - MemberKind.reset(); - return Error::success(); -} diff --git a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp index bff3516203a08..6a94952c175b6 100644 --- a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -10,13 +10,12 @@ #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/Support/Error.h" -#include "llvm/Support/ScopedPrinter.h" using namespace llvm; using namespace llvm::codeview; @@ -64,12 +63,27 @@ public: static const TypeIndex Untranslated; - Error mergeTypesAndIds(TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, + // Local hashing entry points + Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds, + MergingTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes); - Error mergeIdRecords(TypeTableBuilder &Dest, + Error mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, const CVTypeArray &Ids); - Error mergeTypeRecords(TypeTableBuilder &Dest, const CVTypeArray &Types); + Error mergeTypeRecords(MergingTypeTableBuilder &Dest, + const CVTypeArray &Types); + + // Global hashing entry points + Error mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, + GlobalTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes); + Error mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes); + Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes); private: Error doit(const CVTypeArray &Types); @@ -83,6 +97,16 @@ private: bool remapTypeIndex(TypeIndex &Idx); bool remapItemIndex(TypeIndex &Idx); + bool hasTypeStream() const { + return (UseGlobalHashes) ? (!!DestGlobalTypeStream) : (!!DestTypeStream); + } + + bool hasIdStream() const { + return (UseGlobalHashes) ? (!!DestGlobalIdStream) : (!!DestIdStream); + } + + ArrayRef<uint8_t> serializeRemapped(const RemappedType &Record); + bool remapIndices(RemappedType &Record, ArrayRef<TiReference> Refs); bool remapIndex(TypeIndex &Idx, ArrayRef<TypeIndex> Map); @@ -96,25 +120,23 @@ private: return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); } - Error writeRecord(TypeTableBuilder &Dest, const RemappedType &Record, - bool RemapSuccess) { - TypeIndex DestIdx = Untranslated; - if (RemapSuccess) - DestIdx = Dest.writeSerializedRecord(Record); - addMapping(DestIdx); - return Error::success(); - } - Optional<Error> LastError; + bool UseGlobalHashes = false; + bool IsSecondPass = false; unsigned NumBadIndices = 0; TypeIndex CurIndex{TypeIndex::FirstNonSimpleIndex}; - TypeTableBuilder *DestIdStream = nullptr; - TypeTableBuilder *DestTypeStream = nullptr; + MergingTypeTableBuilder *DestIdStream = nullptr; + MergingTypeTableBuilder *DestTypeStream = nullptr; + + GlobalTypeTableBuilder *DestGlobalIdStream = nullptr; + GlobalTypeTableBuilder *DestGlobalTypeStream = nullptr; + + ArrayRef<GloballyHashedType> GlobalHashes; // If we're only mapping id records, this array contains the mapping for // type records. @@ -123,10 +145,35 @@ private: /// Map from source type index to destination type index. Indexed by source /// type index minus 0x1000. SmallVectorImpl<TypeIndex> &IndexMap; + + /// Temporary storage that we use to copy a record's data while re-writing + /// its type indices. + SmallVector<uint8_t, 256> RemapStorage; }; } // end anonymous namespace +ArrayRef<uint8_t> +TypeStreamMerger::serializeRemapped(const RemappedType &Record) { + TypeIndex TI; + ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData; + if (Record.Mappings.empty()) + return OriginalData; + + // At least one type index was remapped. We copy the full record bytes, + // re-write each type index, then return that. + RemapStorage.resize(OriginalData.size()); + ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); + uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); + for (const auto &M : Record.Mappings) { + // First 4 bytes of every record are the record prefix, but the mapping + // offset is relative to the content which starts after. + *(TypeIndex *)(ContentBegin + M.first) = M.second; + } + auto RemapRef = makeArrayRef(RemapStorage); + return RemapRef; +} + const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); static bool isIdRecord(TypeLeafKind K) { @@ -191,7 +238,7 @@ bool TypeStreamMerger::remapTypeIndex(TypeIndex &Idx) { // special mapping from OldTypeStream -> NewTypeStream which was computed // externally. Regardless, we use this special map if and only if we are // doing an id-only mapping. - if (DestTypeStream == nullptr) + if (!hasTypeStream()) return remapIndex(Idx, TypeLookup); assert(TypeLookup.empty()); @@ -199,31 +246,69 @@ bool TypeStreamMerger::remapTypeIndex(TypeIndex &Idx) { } bool TypeStreamMerger::remapItemIndex(TypeIndex &Idx) { - assert(DestIdStream); + assert(hasIdStream()); return remapIndex(Idx, IndexMap); } -Error TypeStreamMerger::mergeTypeRecords(TypeTableBuilder &Dest, +// Local hashing entry points +Error TypeStreamMerger::mergeTypeRecords(MergingTypeTableBuilder &Dest, const CVTypeArray &Types) { DestTypeStream = &Dest; + UseGlobalHashes = false; return doit(Types); } -Error TypeStreamMerger::mergeIdRecords(TypeTableBuilder &Dest, +Error TypeStreamMerger::mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, const CVTypeArray &Ids) { DestIdStream = &Dest; TypeLookup = TypeSourceToDest; + UseGlobalHashes = false; return doit(Ids); } -Error TypeStreamMerger::mergeTypesAndIds(TypeTableBuilder &DestIds, - TypeTableBuilder &DestTypes, +Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds, + MergingTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes) { DestIdStream = &DestIds; DestTypeStream = &DestTypes; + UseGlobalHashes = false; + return doit(IdsAndTypes); +} + +// Global hashing entry points +Error TypeStreamMerger::mergeTypeRecords(GlobalTypeTableBuilder &Dest, + const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes) { + DestGlobalTypeStream = &Dest; + UseGlobalHashes = true; + GlobalHashes = Hashes; + + return doit(Types); +} + +Error TypeStreamMerger::mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes) { + DestGlobalIdStream = &Dest; + TypeLookup = TypeSourceToDest; + UseGlobalHashes = true; + GlobalHashes = Hashes; + + return doit(Ids); +} + +Error TypeStreamMerger::mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, + GlobalTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes) { + DestGlobalIdStream = &DestIds; + DestGlobalTypeStream = &DestTypes; + UseGlobalHashes = true; + GlobalHashes = Hashes; return doit(IdsAndTypes); } @@ -268,14 +353,30 @@ Error TypeStreamMerger::remapAllTypes(const CVTypeArray &Types) { } Error TypeStreamMerger::remapType(const CVType &Type) { - RemappedType R(Type); - SmallVector<TiReference, 32> Refs; - discoverTypeIndices(Type.RecordData, Refs); - bool MappedAllIndices = remapIndices(R, Refs); - TypeTableBuilder &Dest = - isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; - if (auto EC = writeRecord(Dest, R, MappedAllIndices)) - return EC; + auto DoSerialize = [this, Type]() -> ArrayRef<uint8_t> { + RemappedType R(Type); + SmallVector<TiReference, 32> Refs; + discoverTypeIndices(Type.RecordData, Refs); + if (!remapIndices(R, Refs)) + return {}; + return serializeRemapped(R); + }; + + TypeIndex DestIdx = Untranslated; + if (UseGlobalHashes) { + GlobalTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestGlobalIdStream : *DestGlobalTypeStream; + GloballyHashedType H = GlobalHashes[CurIndex.toArrayIndex()]; + DestIdx = Dest.insertRecordAs(H, DoSerialize); + } else { + MergingTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; + + auto Data = DoSerialize(); + if (!Data.empty()) + DestIdx = Dest.insertRecordBytes(Data); + } + addMapping(DestIdx); ++CurIndex; assert((IsSecondPass || IndexMap.size() == slotForIndex(CurIndex)) && @@ -306,14 +407,14 @@ bool TypeStreamMerger::remapIndices(RemappedType &Record, return Success; } -Error llvm::codeview::mergeTypeRecords(TypeTableBuilder &Dest, +Error llvm::codeview::mergeTypeRecords(MergingTypeTableBuilder &Dest, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &Types) { TypeStreamMerger M(SourceToDest); return M.mergeTypeRecords(Dest, Types); } -Error llvm::codeview::mergeIdRecords(TypeTableBuilder &Dest, +Error llvm::codeview::mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef<TypeIndex> TypeSourceToDest, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &Ids) { @@ -322,8 +423,33 @@ Error llvm::codeview::mergeIdRecords(TypeTableBuilder &Dest, } Error llvm::codeview::mergeTypeAndIdRecords( - TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, + MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes) { TypeStreamMerger M(SourceToDest); return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes); } + +Error llvm::codeview::mergeTypeAndIdRecords( + GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, + SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes); +} + +Error llvm::codeview::mergeTypeRecords(GlobalTypeTableBuilder &Dest, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypeRecords(Dest, Types, Hashes); +} + +Error llvm::codeview::mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> Types, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes) { + TypeStreamMerger M(SourceToDest); + return M.mergeIdRecords(Dest, Types, Ids, Hashes); +} diff --git a/lib/DebugInfo/CodeView/TypeTableCollection.cpp b/lib/DebugInfo/CodeView/TypeTableCollection.cpp index 4eca5aeaa0ae3..cf951baa5111c 100644 --- a/lib/DebugInfo/CodeView/TypeTableCollection.cpp +++ b/lib/DebugInfo/CodeView/TypeTableCollection.cpp @@ -10,9 +10,7 @@ #include "llvm/DebugInfo/CodeView/TypeTableCollection.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/DebugInfo/CodeView/TypeName.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" -#include "llvm/Support/BinaryByteStream.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/Support/BinaryStreamReader.h" using namespace llvm; diff --git a/lib/DebugInfo/DWARF/CMakeLists.txt b/lib/DebugInfo/DWARF/CMakeLists.txt index 11f94509e8fae..620016d76fb68 100644 --- a/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/lib/DebugInfo/DWARF/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_library(LLVMDebugInfoDWARF DWARFDebugPubTable.cpp DWARFDebugRangeList.cpp DWARFDie.cpp + DWARFExpression.cpp DWARFFormValue.cpp DWARFGdbIndex.cpp DWARFTypeUnit.cpp diff --git a/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp b/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp index bb475a669efb2..f593953c62ffc 100644 --- a/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp +++ b/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp @@ -63,13 +63,13 @@ DWARFAbbreviationDeclaration::extract(DataExtractor Data, auto A = static_cast<Attribute>(Data.getULEB128(OffsetPtr)); auto F = static_cast<Form>(Data.getULEB128(OffsetPtr)); if (A && F) { - Optional<int64_t> V; bool IsImplicitConst = (F == DW_FORM_implicit_const); if (IsImplicitConst) { - V = Data.getSLEB128(OffsetPtr); + 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) { @@ -96,11 +96,10 @@ DWARFAbbreviationDeclaration::extract(DataExtractor Data, default: // The form has a byte size that doesn't depend on Params. // If it's a fixed size, keep track of it. - if (auto Size = - DWARFFormValue::getFixedByteSize(F, DWARFFormParams())) { - V = *Size; + if ((ByteSize = + DWARFFormValue::getFixedByteSize(F, DWARFFormParams()))) { if (FixedAttributeSize) - FixedAttributeSize->NumBytes += *V; + FixedAttributeSize->NumBytes += *ByteSize; break; } // Indicate we no longer have a fixed byte size for this @@ -110,7 +109,7 @@ DWARFAbbreviationDeclaration::extract(DataExtractor Data, break; } // Record this attribute and its fixed size if it has one. - AttributeSpecs.push_back(AttributeSpec(A, F, V)); + 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. @@ -149,7 +148,7 @@ void DWARFAbbreviationDeclaration::dump(raw_ostream &OS) const { else OS << format("DW_FORM_Unknown_%x", Spec.Form); if (Spec.isImplicitConst()) - OS << '\t' << *Spec.ByteSizeOrValue; + OS << '\t' << Spec.getImplicitConstValue(); OS << '\n'; } OS << '\n'; @@ -182,10 +181,10 @@ Optional<DWARFFormValue> DWARFAbbreviationDeclaration::getAttributeValue( // We have arrived at the attribute to extract, extract if from Offset. DWARFFormValue FormValue(Spec.Form); if (Spec.isImplicitConst()) { - FormValue.setSValue(*Spec.ByteSizeOrValue); + FormValue.setSValue(Spec.getImplicitConstValue()); return FormValue; } - if (FormValue.extractValue(DebugInfoData, &Offset, &U)) + if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U)) return FormValue; } // March Offset along until we get to the attribute we want. @@ -215,8 +214,8 @@ Optional<int64_t> DWARFAbbreviationDeclaration::AttributeSpec::getByteSize( const DWARFUnit &U) const { if (isImplicitConst()) return 0; - if (ByteSizeOrValue) - return ByteSizeOrValue; + if (ByteSize.HasByteSize) + return ByteSize.ByteSize; Optional<int64_t> S; auto FixedByteSize = DWARFFormValue::getFixedByteSize(Form, U.getFormParams()); diff --git a/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index 9ae7c9a07f76d..17f29737bf93b 100644 --- a/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -11,8 +11,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" @@ -23,12 +21,13 @@ using namespace llvm; -bool DWARFAcceleratorTable::extract() { +llvm::Error DWARFAcceleratorTable::extract() { uint32_t Offset = 0; // Check that we can at least read the header. if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength)+4)) - return false; + return make_error<StringError>("Section too small: cannot read header.", + inconvertibleErrorCode()); Hdr.Magic = AccelSection.getU32(&Offset); Hdr.Version = AccelSection.getU16(&Offset); @@ -39,9 +38,13 @@ bool DWARFAcceleratorTable::extract() { // 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.NumBuckets*4 + Hdr.NumHashes*8)) - return false; + Hdr.NumBuckets * 4 + Hdr.NumHashes * 8 - 1)) + return make_error<StringError>( + "Section too small: cannot read buckets and hashes.", + inconvertibleErrorCode()); HdrData.DIEOffsetBase = AccelSection.getU32(&Offset); uint32_t NumAtoms = AccelSection.getU32(&Offset); @@ -52,7 +55,8 @@ bool DWARFAcceleratorTable::extract() { HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm)); } - return true; + IsValid = true; + return Error::success(); } uint32_t DWARFAcceleratorTable::getNumBuckets() { return Hdr.NumBuckets; } @@ -73,6 +77,8 @@ bool DWARFAcceleratorTable::validateForms() { 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) @@ -84,24 +90,33 @@ bool DWARFAcceleratorTable::validateForms() { return true; } -uint32_t DWARFAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { +std::pair<uint32_t, dwarf::Tag> +DWARFAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { uint32_t DieOffset = dwarf::DW_INVALID_OFFSET; + dwarf::Tag DieTag = dwarf::DW_TAG_null; + DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (auto Atom : getAtomsDesc()) { DWARFFormValue FormValue(Atom.second); - FormValue.extractValue(AccelSection, &HashDataOffset, NULL); + 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; + return {DieOffset, DieTag}; } LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { + if (!IsValid) + return; + // Dump the header. OS << "Magic = " << format("0x%08x", Hdr.Magic) << '\n' << "Version = " << format("0x%04x", Hdr.Version) << '\n' @@ -135,6 +150,7 @@ LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength; unsigned HashesBase = Offset + Hdr.NumBuckets * 4; unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4; + DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (unsigned Bucket = 0; Bucket < Hdr.NumBuckets; ++Bucket) { unsigned Index = AccelSection.getU32(&Offset); @@ -171,7 +187,7 @@ LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { unsigned i = 0; for (auto &Atom : AtomForms) { OS << format("{Atom[%d]: ", i++); - if (Atom.extractValue(AccelSection, &DataOffset, nullptr)) + if (Atom.extractValue(AccelSection, &DataOffset, FormParams)) Atom.dump(OS); else OS << "Error extracting the value"; @@ -183,3 +199,69 @@ LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { } } } + +DWARFAcceleratorTable::ValueIterator::ValueIterator( + const DWARFAcceleratorTable &AccelTable, unsigned Offset) + : AccelTable(&AccelTable), DataOffset(Offset) { + if (!AccelTable.AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) + return; + + for (const auto &Atom : AccelTable.HdrData.Atoms) + AtomForms.push_back(DWARFFormValue(Atom.second)); + + // Read the first entry. + NumData = AccelTable.AccelSection.getU32(&DataOffset); + Next(); +} + +void DWARFAcceleratorTable::ValueIterator::Next() { + assert(NumData > 0 && "attempted to increment iterator past the end"); + auto &AccelSection = AccelTable->AccelSection; + if (Data >= NumData || + !AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) { + NumData = 0; + return; + } + DWARFFormParams FormParams = {AccelTable->Hdr.Version, 0, + dwarf::DwarfFormat::DWARF32}; + for (auto &Atom : AtomForms) + Atom.extractValue(AccelSection, &DataOffset, FormParams); + ++Data; +} + +iterator_range<DWARFAcceleratorTable::ValueIterator> +DWARFAcceleratorTable::equal_range(StringRef Key) const { + if (!IsValid) + return make_range(ValueIterator(), ValueIterator()); + + // Find the bucket. + unsigned HashValue = dwarf::djbHash(Key); + unsigned Bucket = HashValue % Hdr.NumBuckets; + unsigned BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength; + unsigned HashesBase = BucketBase + Hdr.NumBuckets * 4; + unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4; + + unsigned BucketOffset = BucketBase + Bucket * 4; + unsigned Index = AccelSection.getU32(&BucketOffset); + + // Search through all hashes in the bucket. + for (unsigned HashIdx = Index; HashIdx < Hdr.NumHashes; ++HashIdx) { + unsigned HashOffset = HashesBase + HashIdx * 4; + unsigned OffsetsOffset = OffsetsBase + HashIdx * 4; + uint32_t Hash = AccelSection.getU32(&HashOffset); + + if (Hash % Hdr.NumBuckets != 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()); +} diff --git a/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp b/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp index 358e9bf43d003..43b235621d18f 100644 --- a/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp +++ b/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp @@ -27,7 +27,7 @@ void DWARFCompileUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { << ")\n"; if (DWARFDie CUDie = getUnitDIE(false)) - CUDie.dump(OS, -1U, 0, DumpOpts); + CUDie.dump(OS, 0, DumpOpts); else OS << "<compile unit can't be parsed!>\n\n"; } diff --git a/lib/DebugInfo/DWARF/DWARFContext.cpp b/lib/DebugInfo/DWARF/DWARFContext.cpp index dd3235244e243..a5defa90eb35f 100644 --- a/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -31,6 +31,7 @@ #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" @@ -40,12 +41,13 @@ #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/raw_ostream.h" #include <algorithm> #include <cstdint> #include <map> #include <string> -#include <tuple> #include <utility> #include <vector> @@ -59,23 +61,39 @@ using DWARFLineTable = DWARFDebugLine::LineTable; using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; -static void dumpAccelSection(raw_ostream &OS, StringRef Name, - const DWARFSection& Section, StringRef StringSection, - bool LittleEndian) { - DWARFDataExtractor AccelSection(Section, LittleEndian, 0); - DataExtractor StrData(StringSection, LittleEndian, 0); - OS << "\n." << Name << " contents:\n"; - DWARFAcceleratorTable Accel(AccelSection, StrData); - if (!Accel.extract()) +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; - Accel.dump(OS); + 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); + OS << ' ' << MachO->getFileFormatName(); + OS << ' ' << MachO->getFileName() << '\n'; + } + } } static void dumpDWARFv5StringOffsetsSection(raw_ostream &OS, StringRef SectionName, + const DWARFObject &Obj, const DWARFSection &StringOffsetsSection, StringRef StringSection, bool LittleEndian) { - DWARFDataExtractor StrOffsetExt(StringOffsetsSection, LittleEndian, 0); + DWARFDataExtractor StrOffsetExt(Obj, StringOffsetsSection, LittleEndian, 0); uint32_t Offset = 0; uint64_t SectionSize = StringOffsetsSection.Data.size(); @@ -153,17 +171,15 @@ dumpDWARFv5StringOffsetsSection(raw_ostream &OS, StringRef SectionName, // 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, bool LittleEndian, unsigned MaxVersion) { - if (StringOffsetsSection.Data.empty()) - return; - OS << "\n." << SectionName << " contents:\n"; // 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, StringOffsetsSection, + dumpDWARFv5StringOffsetsSection(OS, SectionName, Obj, StringOffsetsSection, StringSection, LittleEndian); else { DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0); @@ -188,140 +204,217 @@ static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, } } -void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { - DIDumpType DumpType = DumpOpts.DumpType; - bool DumpEH = DumpOpts.DumpEH; - bool SummarizeTypes = DumpOpts.SummarizeTypes; +// We want to supply the Unit associated with a .debug_line[.dwo] table when +// we dump it, if possible, but still dump the table even if there isn't a Unit. +// Therefore, collect up handles on all the Units that point into the +// line-table section. +typedef std::map<uint64_t, DWARFUnit *> LineToUnitMap; + +static LineToUnitMap +buildLineToUnitMap(DWARFContext::cu_iterator_range CUs, + DWARFContext::tu_section_iterator_range TUSections) { + LineToUnitMap LineToUnit; + for (const auto &CU : CUs) + if (auto CUDIE = CU->getUnitDIE()) + if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) + LineToUnit.insert(std::make_pair(*StmtOffset, &*CU)); + for (const auto &TUS : TUSections) + for (const auto &TU : TUS) + if (auto TUDIE = TU->getUnitDIE()) + if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list))) + LineToUnit.insert(std::make_pair(*StmtOffset, &*TU)); + return LineToUnit; +} - if (DumpType == DIDT_All || DumpType == DIDT_Abbrev) { - OS << ".debug_abbrev contents:\n"; +void DWARFContext::dump( + raw_ostream &OS, DIDumpOptions DumpOpts, + std::array<Optional<uint64_t>, DIDT_ID_Count> DumpOffsets) { + + Optional<uint64_t> DumpOffset; + 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) { + DumpOffset = DumpOffsets[ID]; + unsigned Mask = 1U << ID; + bool Should = (DumpType & Mask) && (Explicit || !Section.empty()); + if (Should) + OS << "\n" << Name << " contents:\n"; + return Should; + }; + + // Dump individual sections. + if (shouldDump(Explicit, ".debug_abbrev", DIDT_ID_DebugAbbrev, + DObj->getAbbrevSection())) getDebugAbbrev()->dump(OS); - } - - if (DumpType == DIDT_All || DumpType == DIDT_AbbrevDwo) - if (const DWARFDebugAbbrev *D = getDebugAbbrevDWO()) { - OS << "\n.debug_abbrev.dwo contents:\n"; - D->dump(OS); + if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_ID_DebugAbbrev, + DObj->getAbbrevDWOSection())) + getDebugAbbrevDWO()->dump(OS); + + auto dumpDebugInfo = [&](bool IsExplicit, const char *Name, + DWARFSection Section, cu_iterator_range CUs) { + if (shouldDump(IsExplicit, Name, DIDT_ID_DebugInfo, Section.Data)) { + if (DumpOffset) + getDIEForOffset(DumpOffset.getValue()) + .dump(OS, 0, DumpOpts.noImplicitRecursion()); + else + for (const auto &CU : CUs) + CU->dump(OS, DumpOpts); } - - if (DumpType == DIDT_All || DumpType == DIDT_Info) { - OS << "\n.debug_info contents:\n"; - for (const auto &CU : compile_units()) - CU->dump(OS, DumpOpts); - } - - if ((DumpType == DIDT_All || DumpType == DIDT_InfoDwo) && - getNumDWOCompileUnits()) { - OS << "\n.debug_info.dwo contents:\n"; - for (const auto &DWOCU : dwo_compile_units()) - DWOCU->dump(OS, DumpOpts); - } - - if ((DumpType == DIDT_All || DumpType == DIDT_Types) && getNumTypeUnits()) { - OS << "\n.debug_types contents:\n"; - for (const auto &TUS : type_unit_sections()) + }; + dumpDebugInfo(Explicit, ".debug_info", DObj->getInfoSection(), + compile_units()); + dumpDebugInfo(ExplicitDWO, ".debug_info.dwo", DObj->getInfoDWOSection(), + dwo_compile_units()); + + auto dumpDebugType = [&](const char *Name, + tu_section_iterator_range TUSections) { + OS << '\n' << Name << " contents:\n"; + DumpOffset = DumpOffsets[DIDT_ID_DebugTypes]; + for (const auto &TUS : TUSections) for (const auto &TU : TUS) - TU->dump(OS, SummarizeTypes); + if (DumpOffset) + TU->getDIEForOffset(*DumpOffset) + .dump(OS, 0, DumpOpts.noImplicitRecursion()); + else + TU->dump(OS, DumpOpts); + }; + if ((DumpType & DIDT_DebugTypes)) { + if (Explicit || getNumTypeUnits()) + dumpDebugType(".debug_types", type_unit_sections()); + if (ExplicitDWO || getNumDWOTypeUnits()) + dumpDebugType(".debug_types.dwo", dwo_type_unit_sections()); } - if ((DumpType == DIDT_All || DumpType == DIDT_TypesDwo) && - getNumDWOTypeUnits()) { - OS << "\n.debug_types.dwo contents:\n"; - for (const auto &DWOTUS : dwo_type_unit_sections()) - for (const auto &DWOTU : DWOTUS) - DWOTU->dump(OS, SummarizeTypes); + if (shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, + DObj->getLocSection().Data)) { + getDebugLoc()->dump(OS, getRegisterInfo(), DumpOffset); } - - if (DumpType == DIDT_All || DumpType == DIDT_Loc) { - OS << "\n.debug_loc contents:\n"; - getDebugLoc()->dump(OS); + if (shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, + DObj->getLocDWOSection().Data)) { + getDebugLocDWO()->dump(OS, getRegisterInfo(), DumpOffset); } - if (DumpType == DIDT_All || DumpType == DIDT_LocDwo) { - OS << "\n.debug_loc.dwo contents:\n"; - getDebugLocDWO()->dump(OS); - } + if (shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, + DObj->getDebugFrameSection())) + getDebugFrame()->dump(OS, DumpOffset); - if (DumpType == DIDT_All || DumpType == DIDT_Frames) { - OS << "\n.debug_frame contents:\n"; - getDebugFrame()->dump(OS); - if (DumpEH) { - OS << "\n.eh_frame contents:\n"; - getEHFrame()->dump(OS); - } - } + if (shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, + DObj->getEHFrameSection())) + getEHFrame()->dump(OS, DumpOffset); - if (DumpType == DIDT_All || DumpType == DIDT_Macro) { - OS << "\n.debug_macinfo contents:\n"; - getDebugMacro()->dump(OS); + if (DumpType & DIDT_DebugMacro) { + if (Explicit || !getDebugMacro()->empty()) { + OS << "\n.debug_macinfo contents:\n"; + getDebugMacro()->dump(OS); + } } - uint32_t offset = 0; - if (DumpType == DIDT_All || DumpType == DIDT_Aranges) { - OS << "\n.debug_aranges contents:\n"; - DataExtractor arangesData(getARangeSection(), isLittleEndian(), 0); + 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); } - uint8_t savedAddressByteSize = 0; - if (DumpType == DIDT_All || DumpType == DIDT_Line) { - OS << "\n.debug_line contents:\n"; - for (const auto &CU : compile_units()) { - savedAddressByteSize = CU->getAddressByteSize(); - auto CUDIE = CU->getUnitDIE(); - if (!CUDIE) + if (shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, + DObj->getLineSection().Data)) { + LineToUnitMap LineToUnit = + buildLineToUnitMap(compile_units(), type_unit_sections()); + unsigned Offset = 0; + DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(), + 0); + while (Offset < LineData.getData().size()) { + DWARFUnit *U = nullptr; + auto It = LineToUnit.find(Offset); + if (It != LineToUnit.end()) + U = It->second; + LineData.setAddressSize(U ? U->getAddressByteSize() : 0); + DWARFDebugLine::LineTable LineTable; + if (DumpOffset && Offset != *DumpOffset) { + // Find the size of this part of the line table section and skip it. + unsigned OldOffset = Offset; + LineTable.Prologue.parse(LineData, &Offset, U); + Offset = OldOffset + LineTable.Prologue.TotalLength + + LineTable.Prologue.sizeofTotalLength(); continue; - if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) { - DWARFDataExtractor lineData(getLineSection(), isLittleEndian(), - savedAddressByteSize); - DWARFDebugLine::LineTable LineTable; - uint32_t Offset = *StmtOffset; - LineTable.parse(lineData, &Offset); + } + // Verbose dumping is done during parsing and not on the intermediate + // representation. + OS << "debug_line[" << format("0x%8.8x", Offset) << "]\n"; + unsigned OldOffset = Offset; + if (DumpOpts.Verbose) { + LineTable.parse(LineData, &Offset, U, &OS); + } else { + LineTable.parse(LineData, &Offset, U); LineTable.dump(OS); } + // Check for unparseable prologue, to avoid infinite loops. + if (OldOffset == Offset) + break; } } - if (DumpType == DIDT_All || DumpType == DIDT_CUIndex) { - OS << "\n.debug_cu_index contents:\n"; - getCUIndex().dump(OS); + if (shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, + DObj->getLineDWOSection().Data)) { + LineToUnitMap LineToUnit = + buildLineToUnitMap(dwo_compile_units(), dwo_type_unit_sections()); + unsigned Offset = 0; + DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(), + isLittleEndian(), 0); + while (Offset < LineData.getData().size()) { + DWARFUnit *U = nullptr; + auto It = LineToUnit.find(Offset); + if (It != LineToUnit.end()) + U = It->second; + DWARFDebugLine::LineTable LineTable; + unsigned OldOffset = Offset; + if (!LineTable.Prologue.parse(LineData, &Offset, U)) + break; + if (!DumpOffset || OldOffset == *DumpOffset) + LineTable.dump(OS); + } } - if (DumpType == DIDT_All || DumpType == DIDT_TUIndex) { - OS << "\n.debug_tu_index contents:\n"; - getTUIndex().dump(OS); + if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, + DObj->getCUIndexSection())) { + getCUIndex().dump(OS); } - if (DumpType == DIDT_All || DumpType == DIDT_LineDwo) { - OS << "\n.debug_line.dwo contents:\n"; - unsigned stmtOffset = 0; - DWARFDataExtractor lineData(getLineDWOSection(), isLittleEndian(), - savedAddressByteSize); - DWARFDebugLine::LineTable LineTable; - while (LineTable.Prologue.parse(lineData, &stmtOffset)) { - LineTable.dump(OS); - LineTable.clear(); - } + if (shouldDump(Explicit, ".debug_tu_index", DIDT_ID_DebugTUIndex, + DObj->getTUIndexSection())) { + getTUIndex().dump(OS); } - if (DumpType == DIDT_All || DumpType == DIDT_Str) { - OS << "\n.debug_str contents:\n"; - DataExtractor strData(getStringSection(), isLittleEndian(), 0); - offset = 0; + 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 ((DumpType == DIDT_All || DumpType == DIDT_StrDwo) && - !getStringDWOSection().empty()) { - OS << "\n.debug_str.dwo contents:\n"; - DataExtractor strDWOData(getStringDWOSection(), isLittleEndian(), 0); - offset = 0; + 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); @@ -329,75 +422,94 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { } } - if (DumpType == DIDT_All || DumpType == DIDT_Ranges) { - OS << "\n.debug_ranges contents:\n"; + if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, + DObj->getRangeSection().Data)) { // In fact, different compile units may have different address byte - // sizes, but for simplicity we just use the address byte size of the last - // compile unit (there is no easy and fast way to associate address range - // list and the compile unit it describes). - DWARFDataExtractor rangesData(getRangeSection(), isLittleEndian(), - savedAddressByteSize); - offset = 0; + // sizes, but for simplicity we just use the address byte size of the + // last compile unit (there is no easy and fast way to associate address + // range list and the compile unit it describes). + // FIXME: savedAddressByteSize seems sketchy. + uint8_t savedAddressByteSize = 0; + for (const auto &CU : compile_units()) { + savedAddressByteSize = CU->getAddressByteSize(); + break; + } + DWARFDataExtractor rangesData(*DObj, DObj->getRangeSection(), + isLittleEndian(), savedAddressByteSize); + uint32_t offset = 0; DWARFDebugRangeList rangeList; while (rangeList.extract(rangesData, &offset)) rangeList.dump(OS); } - if (DumpType == DIDT_All || DumpType == DIDT_Pubnames) - DWARFDebugPubTable(getPubNamesSection(), isLittleEndian(), false) - .dump("debug_pubnames", OS); + if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, + DObj->getPubNamesSection())) + DWARFDebugPubTable(DObj->getPubNamesSection(), isLittleEndian(), false) + .dump(OS); - if (DumpType == DIDT_All || DumpType == DIDT_Pubtypes) - DWARFDebugPubTable(getPubTypesSection(), isLittleEndian(), false) - .dump("debug_pubtypes", OS); + if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, + DObj->getPubTypesSection())) + DWARFDebugPubTable(DObj->getPubTypesSection(), isLittleEndian(), false) + .dump(OS); - if (DumpType == DIDT_All || DumpType == DIDT_GnuPubnames) - DWARFDebugPubTable(getGnuPubNamesSection(), isLittleEndian(), + if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, + DObj->getGnuPubNamesSection())) + DWARFDebugPubTable(DObj->getGnuPubNamesSection(), isLittleEndian(), true /* GnuStyle */) - .dump("debug_gnu_pubnames", OS); + .dump(OS); - if (DumpType == DIDT_All || DumpType == DIDT_GnuPubtypes) - DWARFDebugPubTable(getGnuPubTypesSection(), isLittleEndian(), + if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, + DObj->getGnuPubTypesSection())) + DWARFDebugPubTable(DObj->getGnuPubTypesSection(), isLittleEndian(), true /* GnuStyle */) - .dump("debug_gnu_pubtypes", OS); - - if (DumpType == DIDT_All || DumpType == DIDT_StrOffsets) - dumpStringOffsetsSection(OS, "debug_str_offsets", getStringOffsetSection(), - getStringSection(), isLittleEndian(), - getMaxVersion()); - - if (DumpType == DIDT_All || DumpType == DIDT_StrOffsetsDwo) { - dumpStringOffsetsSection(OS, "debug_str_offsets.dwo", - getStringOffsetDWOSection(), getStringDWOSection(), - isLittleEndian(), getMaxVersion()); - } - - if ((DumpType == DIDT_All || DumpType == DIDT_GdbIndex) && - !getGdbIndexSection().empty()) { - OS << "\n.gnu_index contents:\n"; + .dump(OS); + + if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, + DObj->getStringOffsetSection().Data)) + dumpStringOffsetsSection( + OS, "debug_str_offsets", *DObj, DObj->getStringOffsetSection(), + DObj->getStringSection(), 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(), isLittleEndian(), getMaxVersion()); + + if (shouldDump(Explicit, ".gnu_index", DIDT_ID_GdbIndex, + DObj->getGdbIndexSection())) { getGdbIndex().dump(OS); } - if (DumpType == DIDT_All || DumpType == DIDT_AppleNames) - dumpAccelSection(OS, "apple_names", getAppleNamesSection(), - getStringSection(), isLittleEndian()); + if (shouldDump(Explicit, ".apple_names", DIDT_ID_AppleNames, + DObj->getAppleNamesSection().Data)) + getAppleNames().dump(OS); - if (DumpType == DIDT_All || DumpType == DIDT_AppleTypes) - dumpAccelSection(OS, "apple_types", getAppleTypesSection(), - getStringSection(), isLittleEndian()); + if (shouldDump(Explicit, ".apple_types", DIDT_ID_AppleTypes, + DObj->getAppleTypesSection().Data)) + getAppleTypes().dump(OS); - if (DumpType == DIDT_All || DumpType == DIDT_AppleNamespaces) - dumpAccelSection(OS, "apple_namespaces", getAppleNamespacesSection(), - getStringSection(), isLittleEndian()); + if (shouldDump(Explicit, ".apple_namespaces", DIDT_ID_AppleNamespaces, + DObj->getAppleNamespacesSection().Data)) + getAppleNamespaces().dump(OS); - if (DumpType == DIDT_All || DumpType == DIDT_AppleObjC) - dumpAccelSection(OS, "apple_objc", getAppleObjCSection(), - getStringSection(), isLittleEndian()); + if (shouldDump(Explicit, ".apple_objc", DIDT_ID_AppleObjC, + DObj->getAppleObjCSection().Data)) + getAppleObjC().dump(OS); } DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { - // FIXME: Improve this for the case where this DWO file is really a DWP file - // with an index - use the index for lookup instead of a linear search. + DWOCUs.parseDWO(*this, DObj->getInfoDWOSection(), true); + + if (const auto &CUI = getCUIndex()) { + if (const auto *R = CUI.getFromHash(Hash)) + return DWOCUs.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()) if (DWOCU->getDWOId() == Hash) return DWOCU.get(); @@ -411,21 +523,16 @@ DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) { return DWARFDie(); } -bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) { +bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { bool Success = true; - DWARFVerifier verifier(OS, *this); - if (DumpType == DIDT_All || DumpType == DIDT_Info) { - if (!verifier.handleDebugInfo()) - Success = false; - } - if (DumpType == DIDT_All || DumpType == DIDT_Line) { - if (!verifier.handleDebugLine()) - Success = false; - } - if (DumpType == DIDT_All || DumpType == DIDT_AppleNames) { - if (!verifier.handleAppleNames()) - Success = false; - } + 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; } @@ -433,7 +540,7 @@ const DWARFUnitIndex &DWARFContext::getCUIndex() { if (CUIndex) return *CUIndex; - DataExtractor CUIndexData(getCUIndexSection(), isLittleEndian(), 0); + DataExtractor CUIndexData(DObj->getCUIndexSection(), isLittleEndian(), 0); CUIndex = llvm::make_unique<DWARFUnitIndex>(DW_SECT_INFO); CUIndex->parse(CUIndexData); @@ -444,7 +551,7 @@ const DWARFUnitIndex &DWARFContext::getTUIndex() { if (TUIndex) return *TUIndex; - DataExtractor TUIndexData(getTUIndexSection(), isLittleEndian(), 0); + DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); TUIndex = llvm::make_unique<DWARFUnitIndex>(DW_SECT_TYPES); TUIndex->parse(TUIndexData); @@ -455,7 +562,7 @@ DWARFGdbIndex &DWARFContext::getGdbIndex() { if (GdbIndex) return *GdbIndex; - DataExtractor GdbIndexData(getGdbIndexSection(), true /*LE*/, 0); + DataExtractor GdbIndexData(DObj->getGdbIndexSection(), true /*LE*/, 0); GdbIndex = llvm::make_unique<DWARFGdbIndex>(); GdbIndex->parse(GdbIndexData); return *GdbIndex; @@ -465,7 +572,7 @@ const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { if (Abbrev) return Abbrev.get(); - DataExtractor abbrData(getAbbrevSection(), isLittleEndian(), 0); + DataExtractor abbrData(DObj->getAbbrevSection(), isLittleEndian(), 0); Abbrev.reset(new DWARFDebugAbbrev()); Abbrev->extract(abbrData); @@ -476,7 +583,7 @@ const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { if (AbbrevDWO) return AbbrevDWO.get(); - DataExtractor abbrData(getAbbrevDWOSection(), isLittleEndian(), 0); + DataExtractor abbrData(DObj->getAbbrevDWOSection(), isLittleEndian(), 0); AbbrevDWO.reset(new DWARFDebugAbbrev()); AbbrevDWO->extract(abbrData); return AbbrevDWO.get(); @@ -489,7 +596,7 @@ const DWARFDebugLoc *DWARFContext::getDebugLoc() { Loc.reset(new DWARFDebugLoc); // assume all compile units have the same address byte size if (getNumCompileUnits()) { - DWARFDataExtractor LocData(getLocSection(), isLittleEndian(), + DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), getCompileUnitAtIndex(0)->getAddressByteSize()); Loc->parse(LocData); } @@ -500,7 +607,7 @@ const DWARFDebugLocDWO *DWARFContext::getDebugLocDWO() { if (LocDWO) return LocDWO.get(); - DataExtractor LocData(getLocDWOSection().Data, isLittleEndian(), 0); + DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), 0); LocDWO.reset(new DWARFDebugLocDWO()); LocDWO->parse(LocData); return LocDWO.get(); @@ -528,8 +635,8 @@ const DWARFDebugFrame *DWARFContext::getDebugFrame() { // provides this information). This problem is fixed in DWARFv4 // See this dwarf-discuss discussion for more details: // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html - DataExtractor debugFrameData(getDebugFrameSection(), isLittleEndian(), - getAddressSize()); + DataExtractor debugFrameData(DObj->getDebugFrameSection(), isLittleEndian(), + DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(false /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); @@ -539,8 +646,8 @@ const DWARFDebugFrame *DWARFContext::getEHFrame() { if (EHFrame) return EHFrame.get(); - DataExtractor debugFrameData(getEHFrameSection(), isLittleEndian(), - getAddressSize()); + DataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(), + DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(true /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); @@ -550,12 +657,47 @@ const DWARFDebugMacro *DWARFContext::getDebugMacro() { if (Macro) return Macro.get(); - DataExtractor MacinfoData(getMacinfoSection(), isLittleEndian(), 0); + DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); Macro.reset(new DWARFDebugMacro()); Macro->parse(MacinfoData); return Macro.get(); } +static DWARFAcceleratorTable & +getAccelTable(std::unique_ptr<DWARFAcceleratorTable> &Cache, + const DWARFObject &Obj, const DWARFSection &Section, + StringRef StringSection, bool IsLittleEndian) { + if (Cache) + return *Cache; + DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); + DataExtractor StrData(StringSection, IsLittleEndian, 0); + Cache.reset(new DWARFAcceleratorTable(AccelSection, StrData)); + if (Error E = Cache->extract()) + llvm::consumeError(std::move(E)); + return *Cache; +} + +const DWARFAcceleratorTable &DWARFContext::getAppleNames() { + return getAccelTable(AppleNames, *DObj, DObj->getAppleNamesSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const DWARFAcceleratorTable &DWARFContext::getAppleTypes() { + return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const DWARFAcceleratorTable &DWARFContext::getAppleNamespaces() { + return getAccelTable(AppleNamespaces, *DObj, + DObj->getAppleNamespacesSection(), + DObj->getStringSection(), isLittleEndian()); +} + +const DWARFAcceleratorTable &DWARFContext::getAppleObjC() { + return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), + DObj->getStringSection(), isLittleEndian()); +} + const DWARFLineTable * DWARFContext::getLineTableForUnit(DWARFUnit *U) { if (!Line) @@ -576,35 +718,35 @@ DWARFContext::getLineTableForUnit(DWARFUnit *U) { // Make sure the offset is good before we try to parse. if (stmtOffset >= U->getLineSection().Data.size()) - return nullptr; + return nullptr; // We have to parse it first. - DWARFDataExtractor lineData(U->getLineSection(), isLittleEndian(), + DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), U->getAddressByteSize()); - return Line->getOrParseLineTable(lineData, stmtOffset); + return Line->getOrParseLineTable(lineData, stmtOffset, U); } void DWARFContext::parseCompileUnits() { - CUs.parse(*this, getInfoSection()); + CUs.parse(*this, DObj->getInfoSection()); } void DWARFContext::parseTypeUnits() { if (!TUs.empty()) return; - forEachTypesSections([&](const DWARFSection &S) { + DObj->forEachTypesSections([&](const DWARFSection &S) { TUs.emplace_back(); TUs.back().parse(*this, S); }); } void DWARFContext::parseDWOCompileUnits() { - DWOCUs.parseDWO(*this, getInfoDWOSection()); + DWOCUs.parseDWO(*this, DObj->getInfoDWOSection()); } void DWARFContext::parseDWOTypeUnits() { if (!DWOTUs.empty()) return; - forEachTypesDWOSections([&](const DWARFSection &S) { + DObj->forEachTypesDWOSections([&](const DWARFSection &S) { DWOTUs.emplace_back(); DWOTUs.back().parseDWO(*this, S); }); @@ -622,6 +764,35 @@ DWARFCompileUnit *DWARFContext::getCompileUnitForAddress(uint64_t Address) { 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, @@ -793,11 +964,13 @@ DWARFContext::getDWOContext(StringRef AbsolutePath) { return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); } - SmallString<128> DWPName; Expected<OwningBinary<ObjectFile>> Obj = [&] { if (!CheckedForDWP) { - (getFileName() + ".dwp").toVector(DWPName); - auto Obj = object::ObjectFile::createObjectFile(DWPName); + 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; @@ -820,7 +993,7 @@ DWARFContext::getDWOContext(StringRef AbsolutePath) { auto S = std::make_shared<DWOFile>(); S->File = std::move(Obj.get()); - S->Context = llvm::make_unique<DWARFContextInMemory>(*S->File.getBinary()); + S->Context = DWARFContext::create(*S->File.getBinary()); *Entry = S; auto *Ctxt = S->Context.get(); return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); @@ -906,208 +1079,400 @@ static bool isRelocScattered(const object::ObjectFile &Obj, return MachObj->isRelocationScattered(RelocInfo); } -Error DWARFContextInMemory::maybeDecompress(const SectionRef &Sec, - StringRef Name, StringRef &Data) { - if (!Decompressor::isCompressed(Sec)) - return Error::success(); +ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { + errs() << "error: " + toString(std::move(E)) << '\n'; + return ErrorPolicy::Continue; +} - Expected<Decompressor> Decompressor = - Decompressor::create(Name, Data, IsLittleEndian, AddressSize == 8); - if (!Decompressor) - return Decompressor.takeError(); +namespace { +struct DWARFSectionMap final : public DWARFSection { + RelocAddrMap Relocs; +}; - SmallString<32> Out; - if (auto Err = Decompressor->resizeAndDecompress(Out)) - return Err; +class DWARFObjInMemory final : public DWARFObject { + bool IsLittleEndian; + uint8_t AddressSize; + StringRef FileName; + const object::ObjectFile *Obj = nullptr; + std::vector<SectionName> SectionNames; + + using TypeSectionMap = MapVector<object::SectionRef, DWARFSectionMap, + std::map<object::SectionRef, unsigned>>; + + TypeSectionMap TypesSections; + TypeSectionMap TypesDWOSections; + + DWARFSectionMap InfoSection; + DWARFSectionMap LocSection; + DWARFSectionMap LineSection; + DWARFSectionMap RangeSection; + DWARFSectionMap StringOffsetSection; + DWARFSectionMap InfoDWOSection; + DWARFSectionMap LineDWOSection; + DWARFSectionMap LocDWOSection; + DWARFSectionMap StringOffsetDWOSection; + DWARFSectionMap RangeDWOSection; + DWARFSectionMap AddrSection; + DWARFSectionMap AppleNamesSection; + DWARFSectionMap AppleTypesSection; + DWARFSectionMap AppleNamespacesSection; + DWARFSectionMap AppleObjCSection; + + DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { + return StringSwitch<DWARFSectionMap *>(Name) + .Case("debug_info", &InfoSection) + .Case("debug_loc", &LocSection) + .Case("debug_line", &LineSection) + .Case("debug_str_offsets", &StringOffsetSection) + .Case("debug_ranges", &RangeSection) + .Case("debug_info.dwo", &InfoDWOSection) + .Case("debug_loc.dwo", &LocDWOSection) + .Case("debug_line.dwo", &LineDWOSection) + .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) + .Case("debug_addr", &AddrSection) + .Case("apple_names", &AppleNamesSection) + .Case("apple_types", &AppleTypesSection) + .Case("apple_namespaces", &AppleNamespacesSection) + .Case("apple_namespac", &AppleNamespacesSection) + .Case("apple_objc", &AppleObjCSection) + .Default(nullptr); + } - UncompressedSections.emplace_back(std::move(Out)); - Data = UncompressedSections.back(); + StringRef AbbrevSection; + StringRef ARangeSection; + StringRef DebugFrameSection; + StringRef EHFrameSection; + StringRef StringSection; + StringRef MacinfoSection; + StringRef PubNamesSection; + StringRef PubTypesSection; + StringRef GnuPubNamesSection; + StringRef AbbrevDWOSection; + StringRef StringDWOSection; + StringRef GnuPubTypesSection; + StringRef CUIndexSection; + StringRef GdbIndexSection; + StringRef TUIndexSection; + + SmallVector<SmallString<32>, 4> 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_pubnames", &PubNamesSection) + .Case("debug_pubtypes", &PubTypesSection) + .Case("debug_gnu_pubnames", &GnuPubNamesSection) + .Case("debug_gnu_pubtypes", &GnuPubTypesSection) + .Case("debug_abbrev.dwo", &AbbrevDWOSection) + .Case("debug_str.dwo", &StringDWOSection) + .Case("debug_cu_index", &CUIndexSection) + .Case("debug_tu_index", &TUIndexSection) + .Case("gdb_index", &GdbIndexSection) + // Any more debug info sections go here. + .Default(nullptr); + } - return Error::success(); -} + /// 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(); -ErrorPolicy DWARFContextInMemory::defaultErrorHandler(Error E) { - errs() << "error: " + toString(std::move(E)) << '\n'; - return ErrorPolicy::Continue; -} + Expected<Decompressor> Decompressor = + Decompressor::create(Name, Data, IsLittleEndian, AddressSize == 8); + if (!Decompressor) + return Decompressor.takeError(); -DWARFContextInMemory::DWARFContextInMemory( - const object::ObjectFile &Obj, const LoadedObjectInfo *L, - function_ref<ErrorPolicy(Error)> HandleError) - : FileName(Obj.getFileName()), IsLittleEndian(Obj.isLittleEndian()), - AddressSize(Obj.getBytesInAddress()) { - for (const SectionRef &Section : Obj.sections()) { - StringRef Name; - Section.getName(Name); - // Skip BSS and Virtual sections, they aren't interesting. - if (Section.isBSS() || Section.isVirtual()) - 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; + SmallString<32> Out; + if (auto Err = Decompressor->resizeAndDecompress(Out)) + return Err; + + UncompressedSections.emplace_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(); } + } + 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; - // 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. + // Skip sections stripped by dsymutil. + if (Section.isStripped()) + continue; - // Map platform specific debug section names to DWARF standard section - // names. - Name = Obj.mapDebugSectionName(Name); + 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; + } - 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; + // 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_types") { + // Find debug_types data by section rather than name as there are + // multiple, comdat grouped, debug_types sections. + TypesSections[Section].Data = Data; + } else if (Name == "debug_types.dwo") { + TypesDWOSections[Section].Data = Data; } - } else if (Name == "debug_types") { - // Find debug_types data by section rather than name as there are - // multiple, comdat grouped, debug_types sections. - 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. - DWARFSection *Sec = mapNameToDWARFSection(RelSecName); - RelocAddrMap *Map = Sec ? &Sec->Relocs : nullptr; - if (!Map) { - // Find debug_types relocs by section rather than name as there are - // multiple, comdat grouped, debug_types sections. - if (RelSecName == "debug_types") - Map = &TypesSections[*RelocatedSection].Relocs; - else if (RelSecName == "debug_types.dwo") - Map = &TypesDWOSections[*RelocatedSection].Relocs; - else + if (RelocatedSection == Obj.section_end()) continue; - } - if (Section.relocation_begin() == Section.relocation_end()) - continue; + StringRef RelSecName; + StringRef RelSecData; + RelocatedSection->getName(RelSecName); - // 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)) + // 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; - Expected<SymInfo> SymInfoOrErr = getSymbolInfo(Obj, Reloc, L, AddrCache); - if (!SymInfoOrErr) { - if (HandleError(SymInfoOrErr.takeError()) == ErrorPolicy::Halt) - return; + // 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_types relocs by section rather than name as there are + // multiple, comdat grouped, debug_types sections. + 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; } - 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; + 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}); } - RelocAddrEntry Rel = {SymInfoOrErr->SectionIndex, Val}; - Map->insert({Reloc.getOffset(), Rel}); } + + for (SectionName &S : SectionNames) + if (SectionAmountMap[S.Name] > 1) + S.IsNameUnique = false; } -} -DWARFContextInMemory::DWARFContextInMemory( - const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, uint8_t AddrSize, - bool isLittleEndian) - : IsLittleEndian(isLittleEndian), AddressSize(AddrSize) { - for (const auto &SecIt : Sections) { - if (StringRef *SectionData = mapSectionToMember(SecIt.first())) - *SectionData = SecIt.second->getBuffer(); + 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; } -} -DWARFSection *DWARFContextInMemory::mapNameToDWARFSection(StringRef Name) { - return StringSwitch<DWARFSection *>(Name) - .Case("debug_info", &InfoSection) - .Case("debug_loc", &LocSection) - .Case("debug_line", &LineSection) - .Case("debug_str_offsets", &StringOffsetSection) - .Case("debug_ranges", &RangeSection) - .Case("debug_info.dwo", &InfoDWOSection) - .Case("debug_loc.dwo", &LocDWOSection) - .Case("debug_line.dwo", &LineDWOSection) - .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) - .Case("debug_addr", &AddrSection) - .Case("apple_names", &AppleNamesSection) - .Case("apple_types", &AppleTypesSection) - .Case("apple_namespaces", &AppleNamespacesSection) - .Case("apple_namespac", &AppleNamespacesSection) - .Case("apple_objc", &AppleObjCSection) - .Default(nullptr); + 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 &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; + } + + // Sections for DWARF5 split dwarf proposal. + const DWARFSection &getInfoDWOSection() const override { + return InfoDWOSection; + } + 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; } + 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; } + StringRef getMacinfoSection() const override { return MacinfoSection; } + StringRef getPubNamesSection() const override { return PubNamesSection; } + StringRef getPubTypesSection() const override { return PubTypesSection; } + StringRef getGnuPubNamesSection() const override { + return GnuPubNamesSection; + } + StringRef 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; + } + + StringRef getFileName() const override { return FileName; } + uint8_t getAddressSize() const override { return AddressSize; } + const DWARFSection &getInfoSection() const override { return InfoSection; } + 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)); } -StringRef *DWARFContextInMemory::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_pubnames", &PubNamesSection) - .Case("debug_pubtypes", &PubTypesSection) - .Case("debug_gnu_pubnames", &GnuPubNamesSection) - .Case("debug_gnu_pubtypes", &GnuPubTypesSection) - .Case("debug_abbrev.dwo", &AbbrevDWOSection) - .Case("debug_str.dwo", &StringDWOSection) - .Case("debug_cu_index", &CUIndexSection) - .Case("debug_tu_index", &TUIndexSection) - .Case("gdb_index", &GdbIndexSection) - // Any more debug info sections go here. - .Default(nullptr); +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), ""); } -void DWARFContextInMemory::anchor() {} +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 make_error<StringError>(TargetLookupError, inconvertibleErrorCode()); + RegInfo.reset(TheTarget->createMCRegInfo(TT.str())); + return Error::success(); +} diff --git a/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp index 001097e56c716..861dd313fb095 100644 --- a/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp +++ b/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -8,17 +8,20 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.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 (!RelocMap) + if (SecNdx) + *SecNdx = -1ULL; + if (!Section) return getUnsigned(Off, Size); - RelocAddrMap::const_iterator AI = RelocMap->find(*Off); - if (AI == RelocMap->end()) + Optional<RelocAddrEntry> Rel = Obj->find(*Section, *Off); + if (!Rel) return getUnsigned(Off, Size); if (SecNdx) - *SecNdx = AI->second.SectionIndex; - return getUnsigned(Off, Size) + AI->second.Value; + *SecNdx = Rel->SectionIndex; + return getUnsigned(Off, Size) + Rel->Value; } diff --git a/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp b/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp index 76dd2e4c21bcc..4830c36a8ee79 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp @@ -68,9 +68,7 @@ DWARFAbbreviationDeclarationSet::getAbbreviationDeclaration( return &Decls[AbbrCode - FirstAbbrCode]; } -DWARFDebugAbbrev::DWARFDebugAbbrev() { - clear(); -} +DWARFDebugAbbrev::DWARFDebugAbbrev() { clear(); } void DWARFDebugAbbrev::clear() { AbbrDeclSets.clear(); @@ -79,18 +77,29 @@ void DWARFDebugAbbrev::clear() { void DWARFDebugAbbrev::extract(DataExtractor Data) { clear(); + this->Data = Data; +} +void DWARFDebugAbbrev::parse() const { + if (!Data) + return; uint32_t Offset = 0; DWARFAbbreviationDeclarationSet AbbrDecls; - while (Data.isValidOffset(Offset)) { + 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)) + if (!AbbrDecls.extract(*Data, &Offset)) break; - AbbrDeclSets[CUAbbrOffset] = std::move(AbbrDecls); + 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; @@ -115,5 +124,16 @@ DWARFDebugAbbrev::getAbbreviationDeclarationSet(uint64_t CUAbbrOffset) const { 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/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp index 6601393d7459b..a3ecb15e3661b 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -43,7 +43,8 @@ void DWARFDebugAranges::generate(DWARFContext *CTX) { return; // Extract aranges from .debug_aranges section. - DataExtractor ArangesData(CTX->getARangeSection(), CTX->isLittleEndian(), 0); + DataExtractor ArangesData(CTX->getDWARFObj().getARangeSection(), + CTX->isLittleEndian(), 0); extract(ArangesData); // Generate aranges from DIEs: even if .debug_aranges section is present, @@ -106,8 +107,8 @@ void DWARFDebugAranges::construct() { assert(ValidCUs.empty()); // Endpoints are not needed now. - std::vector<RangeEndpoint> EmptyEndpoints; - EmptyEndpoints.swap(Endpoints); + Endpoints.clear(); + Endpoints.shrink_to_fit(); } uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { diff --git a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 475cf25b781b4..3312da67804b5 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -11,7 +11,6 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -46,19 +45,26 @@ public: FrameKind getKind() const { return Kind; } virtual uint64_t getOffset() const { return Offset; } - /// \brief Parse and store a sequence of CFI instructions from Data, + /// Parse and store a sequence of CFI instructions from Data, /// starting at *Offset and ending at EndOffset. If everything /// goes well, *Offset should be equal to EndOffset when this method /// returns. Otherwise, an error occurred. virtual void parseInstructions(DataExtractor Data, uint32_t *Offset, uint32_t EndOffset); - /// \brief Dump the entry header to the given output stream. + /// Dump the entry header to the given output stream. virtual void dumpHeader(raw_ostream &OS) const = 0; - /// \brief Dump the entry's instructions to the given output stream. + /// Dump the entry's instructions to the given output stream. virtual void dumpInstructions(raw_ostream &OS) const; + /// Dump the entire entry to the given output stream. + void dump(raw_ostream &OS) const { + dumpHeader(OS); + dumpInstructions(OS); + OS << "\n"; + } + protected: const FrameKind Kind; @@ -157,6 +163,7 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, 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; @@ -188,10 +195,16 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, break; } case DW_CFA_def_cfa_expression: + // FIXME: Parse the actual instruction. + *Offset += Data.getULEB128(Offset); + break; case DW_CFA_expression: - case DW_CFA_val_expression: - // TODO: implement this - report_fatal_error("Values with expressions not implemented yet!"); + case DW_CFA_val_expression: { + // FIXME: Parse the actual instruction. + Data.getULEB128(Offset); + *Offset += Data.getULEB128(Offset); + break; + } } } } @@ -686,11 +699,24 @@ void DWARFDebugFrame::parse(DataExtractor Data) { } } -void DWARFDebugFrame::dump(raw_ostream &OS) const { - OS << "\n"; - for (const auto &Entry : Entries) { - Entry->dumpHeader(OS); - Entry->dumpInstructions(OS); - OS << "\n"; +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, Optional<uint64_t> Offset) const { + if (Offset) { + if (auto *Entry = getEntryAtOffset(*Offset)) + Entry->dump(OS); + return; } + + OS << "\n"; + for (const auto &Entry : Entries) + Entry->dump(OS); } diff --git a/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index 7d180564e9f7a..e5ef4eaceebef 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -12,7 +12,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Format.h" @@ -49,6 +48,7 @@ void DWARFDebugLine::Prologue::clear() { MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0; OpcodeBase = 0; FormParams = DWARFFormParams({0, 0, DWARF32}); + HasMD5 = false; StandardOpcodeLengths.clear(); IncludeDirectories.clear(); FileNames.clear(); @@ -79,15 +79,23 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { << IncludeDirectories[I] << "'\n"; if (!FileNames.empty()) { - OS << " Dir Mod Time File Len File Name\n" - << " ---- ---------- ---------- -----------" - "----------------\n"; + if (HasMD5) + OS << " Dir MD5 Checksum File Name\n" + << " ---- -------------------------------- -----------" + "---------------\n"; + else + OS << " Dir Mod Time File Len File Name\n" + << " ---- ---------- ---------- -----------" + "----------------\n"; for (uint32_t I = 0; I != FileNames.size(); ++I) { const FileNameEntry &FileEntry = FileNames[I]; - OS << format("file_names[%3u] %4" PRIu64 " ", I + 1, FileEntry.DirIdx) - << format("0x%8.8" PRIx64 " 0x%8.8" PRIx64 " ", FileEntry.ModTime, - FileEntry.Length) - << FileEntry.Name << '\n'; + OS << format("file_names[%3u] %4" PRIu64 " ", I + 1, FileEntry.DirIdx); + if (HasMD5) + OS << FileEntry.Checksum.digest(); + else + OS << format("0x%8.8" PRIx64 " 0x%8.8" PRIx64, FileEntry.ModTime, + FileEntry.Length); + OS << ' ' << FileEntry.Name << '\n'; } } } @@ -123,7 +131,7 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, // ran off the end of the prologue. static ContentDescriptors parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, - uint64_t EndPrologueOffset) { + uint64_t EndPrologueOffset, bool *HasMD5) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; @@ -136,6 +144,8 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); if (Descriptor.Type == dwarf::DW_LNCT_path) HasPath = true; + else if (Descriptor.Type == dwarf::DW_LNCT_MD5 && HasMD5) + *HasMD5 = true; Descriptors.push_back(Descriptor); } return HasPath ? Descriptors : ContentDescriptors(); @@ -144,12 +154,12 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, static bool parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr, uint64_t EndPrologueOffset, - const DWARFFormParams &FormParams, - std::vector<StringRef> &IncludeDirectories, + const DWARFFormParams &FormParams, const DWARFUnit *U, + bool &HasMD5, std::vector<StringRef> &IncludeDirectories, std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { // Get the directory entry description. ContentDescriptors DirDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset); + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); if (DirDescriptors.empty()) return false; @@ -162,7 +172,7 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { case DW_LNCT_path: - if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr)) + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, U)) return false; IncludeDirectories.push_back(Value.getAsCString().getValue()); break; @@ -175,7 +185,7 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, // Get the file entry description. ContentDescriptors FileDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset); + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, &HasMD5); if (FileDescriptors.empty()) return false; @@ -187,7 +197,7 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : FileDescriptors) { DWARFFormValue Value(Descriptor.Form); - if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr)) + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, U)) return false; switch (Descriptor.Type) { case DW_LNCT_path: @@ -202,7 +212,11 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, case DW_LNCT_size: FileEntry.Length = Value.getAsUnsignedConstant().getValue(); break; - // FIXME: Add MD5 + 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; } @@ -213,7 +227,7 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, } bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, - uint32_t *OffsetPtr) { + uint32_t *OffsetPtr, const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; clear(); @@ -230,7 +244,8 @@ bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, if (getVersion() >= 5) { FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); - assert(getAddressSize() == DebugLineData.getAddressSize() && + assert((DebugLineData.getAddressSize() == 0 || + DebugLineData.getAddressSize() == getAddressSize()) && "Line table header and data extractor disagree"); SegSelectorSize = DebugLineData.getU8(OffsetPtr); } @@ -253,7 +268,8 @@ bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, if (getVersion() >= 5) { if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - getFormParams(), IncludeDirectories, FileNames)) { + getFormParams(), U, HasMD5, IncludeDirectories, + FileNames)) { fprintf(stderr, "warning: parsing line table prologue at 0x%8.8" PRIx64 " found an invalid directory or file table description at" @@ -381,46 +397,71 @@ DWARFDebugLine::getLineTable(uint32_t Offset) const { } const DWARFDebugLine::LineTable * -DWARFDebugLine::getOrParseLineTable(const DWARFDataExtractor &DebugLineData, - uint32_t Offset) { +DWARFDebugLine::getOrParseLineTable(DWARFDataExtractor &DebugLineData, + uint32_t Offset, const DWARFUnit *U) { std::pair<LineTableIter, bool> Pos = LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable())); LineTable *LT = &Pos.first->second; if (Pos.second) { - if (!LT->parse(DebugLineData, &Offset)) + if (!LT->parse(DebugLineData, &Offset, U)) return nullptr; } return LT; } -bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, - uint32_t *OffsetPtr) { +bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData, + uint32_t *OffsetPtr, const DWARFUnit *U, + raw_ostream *OS) { const uint32_t DebugLineOffset = *OffsetPtr; clear(); - if (!Prologue.parse(DebugLineData, OffsetPtr)) { + if (!Prologue.parse(DebugLineData, OffsetPtr, U)) { // Restore our offset and return false to indicate failure! *OffsetPtr = DebugLineOffset; return false; } + if (OS) + Prologue.dump(*OS); + 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 - uint32_t ExtOffset = *OffsetPtr; uint64_t Len = DebugLineData.getULEB128(OffsetPtr); - uint32_t ArgSize = Len - (*OffsetPtr - ExtOffset); + 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 @@ -432,6 +473,11 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, // of the sequence. State.Row.EndSequence = true; State.appendRowToMatrix(*OffsetPtr); + if (OS) { + *OS << "\n"; + OS->indent(12); + State.Row.dump(*OS); + } State.resetRowAndSequence(); break; @@ -442,7 +488,16 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, // 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 + assert(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: @@ -473,20 +528,42 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); Prologue.FileNames.push_back(FileEntry); + if (OS) + *OS << " (" << FileEntry.Name.str() + << ", dir=" << FileEntry.DirIdx << ", mod_time=" + << 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: - // Length 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 below - (*OffsetPtr) += ArgSize; + 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) { + fprintf(stderr, "Unexpected line op length at offset 0x%8.8" PRIx32 + " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx32 "\n", + ExtOffset, Len, *OffsetPtr - ExtOffset); + // Skip the rest of the line-number program. + *OffsetPtr = EndOffset; + return false; + } } else if (Opcode < Prologue.OpcodeBase) { + if (OS) + *OS << LNStandardString(Opcode); switch (Opcode) { // Standard Opcodes case DW_LNS_copy: @@ -494,32 +571,49 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, // 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. - State.Row.Address += - DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; + { + 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: @@ -551,6 +645,9 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, uint64_t AddrOffset = (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; State.Row.Address += AddrOffset; + if (OS) + *OS + << format(" (0x%16.16" PRIx64 ")", AddrOffset); } break; @@ -564,7 +661,13 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, // 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. - State.Row.Address += DebugLineData.getU16(OffsetPtr); + { + 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: @@ -583,6 +686,8 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, // 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: @@ -592,8 +697,12 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, { assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; - for (uint8_t I = 0; I < OpcodeLength; ++I) - DebugLineData.getULEB128(OffsetPtr); + 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; } @@ -638,10 +747,20 @@ bool DWARFDebugLine::LineTable::parse(const DWARFDataExtractor &DebugLineData, 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) { diff --git a/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp index c240dd7406d9f..58f88536f3171 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -11,7 +11,10 @@ #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/raw_ostream.h" #include <algorithm> @@ -20,104 +23,202 @@ using namespace llvm; -void DWARFDebugLoc::dump(raw_ostream &OS) const { - for (const LocationList &L : Locations) { +// 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, AddressSize, dwarf::DWARF_VERSION).print(OS, MRI); +} + +void DWARFDebugLoc::LocationList::dump(raw_ostream &OS, bool IsLittleEndian, + unsigned AddressSize, + const MCRegisterInfo *MRI, + unsigned Indent) const { + for (const Entry &E : Entries) { + OS << '\n'; + OS.indent(Indent); + OS << format("0x%016" PRIx64, E.Begin) << " - " + << format("0x%016" PRIx64, E.End) << ": "; + + 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); - const unsigned Indent = 12; - for (const Entry &E : L.Entries) { - if (&E != L.Entries.begin()) - OS.indent(Indent); - OS << "Beginning address offset: " << format("0x%016" PRIx64, E.Begin) - << '\n'; - OS.indent(Indent) << " Ending address offset: " - << format("0x%016" PRIx64, E.End) << '\n'; - OS.indent(Indent) << " Location description: "; - for (unsigned char Loc : E.Loc) { - OS << format("%2.2x ", Loc); - } - OS << "\n\n"; + L.dump(OS, IsLittleEndian, AddressSize, MRI, 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())) { + llvm::errs() << "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)) { + llvm::errs() << "Location list overflows the debug_loc section.\n"; + return None; + } + + unsigned Bytes = Data.getU16(Offset); + if (!Data.isValidOffsetForDataOfSize(*Offset, Bytes)) { + llvm::errs() << "Location list overflows the debug_loc section.\n"; + 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()); + std::copy(str.begin(), str.end(), 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)) { - Locations.resize(Locations.size() + 1); - LocationList &Loc = Locations.back(); - Loc.Offset = Offset; - // 2.6.2 Location Lists - // A location list entry consists of: - while (true) { - // A beginning and ending address offsets. - Entry E; - E.Begin = data.getRelocatedAddress(&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) - break; - - unsigned Bytes = data.getU16(&Offset); - // A single location description describing the location of the object... - StringRef str = data.getData().substr(Offset, Bytes); - Offset += Bytes; - E.Loc.append(str.begin(), str.end()); - Loc.Entries.push_back(std::move(E)); - } + 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)) errs() << "error: failed to consume entire .debug_loc section\n"; } +Optional<DWARFDebugLocDWO::LocationList> +DWARFDebugLocDWO::parseOneLocationList(DataExtractor Data, unsigned *Offset) { + 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))) { + if (Kind != dwarf::DW_LLE_startx_length) { + llvm::errs() << "error: dumping support for LLE of kind " << (int)Kind + << " not implemented\n"; + return None; + } + + Entry E; + E.Start = Data.getULEB128(Offset); + E.Length = Data.getU32(Offset); + + unsigned Bytes = 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()); + std::copy(str.begin(), str.end(), E.Loc.begin()); + + LL.Entries.push_back(std::move(E)); + } + return LL; +} + void DWARFDebugLocDWO::parse(DataExtractor data) { + IsLittleEndian = data.isLittleEndian(); + AddressSize = data.getAddressSize(); + uint32_t Offset = 0; while (data.isValidOffset(Offset)) { - Locations.resize(Locations.size() + 1); - LocationList &Loc = Locations.back(); - Loc.Offset = Offset; - dwarf::LocationListEntry Kind; - while ((Kind = static_cast<dwarf::LocationListEntry>( - data.getU8(&Offset))) != dwarf::DW_LLE_end_of_list) { - - if (Kind != dwarf::DW_LLE_startx_length) { - errs() << "error: dumping support for LLE of kind " << (int)Kind - << " not implemented\n"; - return; - } - - Entry E; - - E.Start = data.getULEB128(&Offset); - E.Length = data.getU32(&Offset); - - unsigned Bytes = 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()); - std::copy(str.begin(), str.end(), E.Loc.begin()); - - Loc.Entries.push_back(std::move(E)); - } + if (auto LL = parseOneLocationList(data, &Offset)) + Locations.push_back(std::move(*LL)); + else + return; } } -void DWARFDebugLocDWO::dump(raw_ostream &OS) const { - for (const LocationList &L : Locations) { +DWARFDebugLocDWO::LocationList const * +DWARFDebugLocDWO::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 DWARFDebugLocDWO::LocationList::dump(raw_ostream &OS, bool IsLittleEndian, + unsigned AddressSize, + const MCRegisterInfo *MRI, + unsigned Indent) const { + for (const Entry &E : Entries) { + OS << '\n'; + OS.indent(Indent); + OS << "Addr idx " << E.Start << " (w/ length " << E.Length << "): "; + dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI); + } +} + +void DWARFDebugLocDWO::dump(raw_ostream &OS, const MCRegisterInfo *MRI, + Optional<uint64_t> Offset) const { + auto DumpLocationList = [&](const LocationList &L) { OS << format("0x%8.8x: ", L.Offset); - const unsigned Indent = 12; - for (const Entry &E : L.Entries) { - if (&E != L.Entries.begin()) - OS.indent(Indent); - OS << "Beginning address index: " << E.Start << '\n'; - OS.indent(Indent) << " Length: " << E.Length << '\n'; - OS.indent(Indent) << " Location description: "; - for (unsigned char Loc : E.Loc) - OS << format("%2.2x ", Loc); - OS << "\n\n"; - } + L.dump(OS, 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/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp index 5a4e39f3c2af8..956a91e9c4d63 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -44,8 +44,7 @@ DWARFDebugPubTable::DWARFDebugPubTable(StringRef Data, bool LittleEndian, } } -void DWARFDebugPubTable::dump(StringRef Name, raw_ostream &OS) const { - OS << "\n." << Name << " contents:\n"; +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); diff --git a/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp b/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp index 0b6ae86fd94b2..f0b7ec2751de0 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp @@ -17,6 +17,11 @@ using namespace llvm; +raw_ostream &llvm::operator<<(raw_ostream &OS, const DWARFAddressRange &R) { + return OS << format("[0x%16.16" PRIx64 ", 0x%16.16" PRIx64 ")", R.LowPC, + R.HighPC); +} + void DWARFDebugRangeList::clear() { Offset = -1U; AddressSize = 0; @@ -33,20 +38,22 @@ bool DWARFDebugRangeList::extract(const DWARFDataExtractor &data, return false; Offset = *offset_ptr; while (true) { - RangeListEntry entry; + RangeListEntry Entry; + Entry.SectionIndex = -1ULL; + uint32_t prev_offset = *offset_ptr; - entry.StartAddress = - data.getRelocatedAddress(offset_ptr, &entry.SectionIndex); - entry.EndAddress = data.getRelocatedAddress(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 false; } - if (entry.isEndOfListEntry()) + if (Entry.isEndOfListEntry()) break; - Entries.push_back(entry); + Entries.push_back(Entry); } return true; } @@ -61,16 +68,29 @@ void DWARFDebugRangeList::dump(raw_ostream &OS) const { OS << format("%08x <End of list>\n", Offset); } -DWARFAddressRangesVector -DWARFDebugRangeList::getAbsoluteRanges(uint64_t BaseAddress) const { +DWARFAddressRangesVector DWARFDebugRangeList::getAbsoluteRanges( + llvm::Optional<BaseAddress> BaseAddr) const { DWARFAddressRangesVector Res; for (const RangeListEntry &RLE : Entries) { if (RLE.isBaseAddressSelectionEntry(AddressSize)) { - BaseAddress = RLE.EndAddress; - } else { - Res.push_back({BaseAddress + RLE.StartAddress, - BaseAddress + RLE.EndAddress, RLE.SectionIndex}); + 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/lib/DebugInfo/DWARF/DWARFDie.cpp b/lib/DebugInfo/DWARF/DWARFDie.cpp index 111f0bbd44448..91f0f8501f0c1 100644 --- a/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -16,8 +16,10 @@ #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/MathExtras.h" @@ -31,6 +33,7 @@ using namespace llvm; using namespace dwarf; +using namespace object; using namespace syntax; static void dumpApplePropertyAttribute(raw_ostream &OS, uint64_t Val) { @@ -51,17 +54,131 @@ static void dumpApplePropertyAttribute(raw_ostream &OS, uint64_t Val) { OS << ")"; } -static void dumpRanges(raw_ostream &OS, const DWARFAddressRangesVector& Ranges, - unsigned AddressSize, unsigned Indent) { - if (Ranges.empty()) - return; - - for (const auto &Range: Ranges) { +static void dumpRanges(const DWARFObject &Obj, raw_ostream &OS, + const DWARFAddressRangesVector &Ranges, + unsigned AddressSize, unsigned Indent, + const DIDumpOptions &DumpOpts) { + ArrayRef<SectionName> SectionNames; + if (DumpOpts.Verbose) + SectionNames = Obj.getSectionNames(); + + for (size_t I = 0; I < Ranges.size(); ++I) { + const DWARFAddressRange &R = Ranges[I]; + OS << '\n'; OS.indent(Indent); - OS << format("[0x%0*" PRIx64 " - 0x%0*" PRIx64 ")", - AddressSize*2, Range.LowPC, - AddressSize*2, Range.HighPC); + OS << format("[0x%0*" PRIx64 " - 0x%0*" PRIx64 ")", AddressSize * 2, + R.LowPC, AddressSize * 2, R.HighPC); + + if (SectionNames.empty() || R.SectionIndex == -1ULL) + continue; + + StringRef Name = SectionNames[R.SectionIndex].Name; + OS << " \"" << Name << '\"'; + + // Print section index if name is not unique. + if (!SectionNames[R.SectionIndex].IsNameUnique) + OS << format(" [%" PRIu64 "]", 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)) { + const DWARFSection &LocSection = Obj.getLocSection(); + const DWARFSection &LocDWOSection = Obj.getLocDWOSection(); + uint32_t Offset = *FormValue.getAsSectionOffset(); + + if (!LocSection.Data.empty()) { + DWARFDebugLoc DebugLoc; + DWARFDataExtractor Data(Obj, LocSection, Ctx.isLittleEndian(), + Obj.getAddressSize()); + auto LL = DebugLoc.parseOneLocationList(Data, &Offset); + if (LL) + LL->dump(OS, Ctx.isLittleEndian(), Obj.getAddressSize(), MRI, Indent); + else + OS << "error extracting location list."; + } else if (!LocDWOSection.Data.empty()) { + DataExtractor Data(LocDWOSection.Data, Ctx.isLittleEndian(), 0); + auto LL = DWARFDebugLocDWO::parseOneLocationList(Data, &Offset); + if (LL) + LL->dump(OS, 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) << " "; +} + +/// Recursively dump the DIE type name when applicable. +static void dumpTypeName(raw_ostream &OS, const DWARFDie &Die) { + DWARFDie D = Die.getAttributeValueAsReferencedDie(DW_AT_type); + + 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: + break; + default: + dumpTypeTagName(OS, T); + } + + // Follow the DW_AT_type if possible. + dumpTypeName(OS, D); + + switch (T) { + case DW_TAG_array_type: + OS << "[]"; + break; + case DW_TAG_pointer_type: + OS << '*'; + break; + case DW_TAG_ptr_to_member_type: + OS << '*'; + break; + case DW_TAG_reference_type: + OS << '&'; + break; + case DW_TAG_rvalue_reference_type: + OS << "&&"; + break; + default: + break; } } @@ -73,14 +190,14 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, return; const char BaseIndent[] = " "; OS << BaseIndent; - OS.indent(Indent+2); + OS.indent(Indent + 2); auto attrString = AttributeString(Attr); if (!attrString.empty()) WithColor(OS, syntax::Attribute) << attrString; else WithColor(OS, syntax::Attribute).get() << format("DW_AT_Unknown_%x", Attr); - if (!DumpOpts.Brief) { + if (DumpOpts.Verbose || DumpOpts.ShowForm) { auto formString = FormEncodingString(Form); if (!formString.empty()) OS << " [" << formString << ']'; @@ -90,60 +207,81 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, DWARFUnit *U = Die.getDwarfUnit(); DWARFFormValue formValue(Form); - - if (!formValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, U)) + + if (!formValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, + U->getFormParams(), U)) return; - + OS << "\t("; - + StringRef Name; std::string File; auto Color = syntax::Enumerator; if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { Color = syntax::String; if (const auto *LT = U->getContext().getLineTableForUnit(U)) - if (LT->getFileNameByIndex(formValue.getAsUnsignedConstant().getValue(), U->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { + 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); - + formValue.dump(OS, DumpOpts); + // 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(Attr).getName(DINameKind::LinkageName)) - OS << " \"" << Name << '\"'; + if (const char *Name = Die.getAttributeValueAsReferencedDie(Attr).getName( + DINameKind::LinkageName)) + OS << " \"" << Name << '\"'; + } else if (Attr == DW_AT_type) { + OS << " \""; + dumpTypeName(OS, Die); + 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) { - dumpRanges(OS, Die.getAddressRanges(), U->getAddressByteSize(), - sizeof(BaseIndent)+Indent+4); + const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); + dumpRanges(Obj, OS, Die.getAddressRanges(), U->getAddressByteSize(), + sizeof(BaseIndent) + Indent + 4, DumpOpts); } - + OS << ")\n"; } -bool DWARFDie::isSubprogramDIE() const { - return getTag() == DW_TAG_subprogram; -} +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 { +Optional<DWARFFormValue> DWARFDie::find(dwarf::Attribute Attr) const { if (!isValid()) return None; auto AbbrevDecl = getAbbreviationDeclarationPtr(); @@ -170,33 +308,29 @@ Optional<DWARFFormValue> DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const { if (!isValid()) return None; - auto Die = *this; - if (auto Value = Die.find(Attrs)) - return Value; - if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) - Die = D; - if (auto Value = Die.find(Attrs)) - return Value; - if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_specification)) - Die = D; - if (auto Value = Die.find(Attrs)) + if (auto Value = find(Attrs)) return Value; + if (auto Die = getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) { + if (auto Value = Die.findRecursively(Attrs)) + return Value; + } + if (auto Die = getAttributeValueAsReferencedDie(DW_AT_specification)) { + if (auto Value = Die.findRecursively(Attrs)) + return Value; + } return None; } DWARFDie DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const { - auto SpecRef = toReference(find(Attr)); - if (SpecRef) { - auto SpecUnit = U->getUnitSection().getUnitForOffset(*SpecRef); - if (SpecUnit) + if (auto SpecRef = toReference(find(Attr))) { + if (auto SpecUnit = U->getUnitSection().getUnitForOffset(*SpecRef)) return SpecUnit->getDIEForOffset(*SpecRef); } return DWARFDie(); } -Optional<uint64_t> -DWARFDie::getRangesBaseAttribute() const { +Optional<uint64_t> DWARFDie::getRangesBaseAttribute() const { return toSectionOffset(find({DW_AT_rnglists_base, DW_AT_GNU_ranges_base})); } @@ -229,8 +363,7 @@ bool DWARFDie::getLowAndHighPC(uint64_t &LowPC, uint64_t &HighPC, return false; } -DWARFAddressRangesVector -DWARFDie::getAddressRanges() const { +DWARFAddressRangesVector DWARFDie::getAddressRanges() const { if (isNULL()) return DWARFAddressRangesVector(); // Single range specified by low/high PC. @@ -248,8 +381,8 @@ DWARFDie::getAddressRanges() const { return DWARFAddressRangesVector(); } -void -DWARFDie::collectChildrenAddressRanges(DWARFAddressRangesVector& Ranges) const { +void DWARFDie::collectChildrenAddressRanges( + DWARFAddressRangesVector &Ranges) const { if (isNULL()) return; if (isSubprogramDIE()) { @@ -257,33 +390,32 @@ DWARFDie::collectChildrenAddressRanges(DWARFAddressRangesVector& Ranges) const { Ranges.insert(Ranges.end(), DIERanges.begin(), DIERanges.end()); } - for (auto Child: children()) + for (auto Child : children()) Child.collectChildrenAddressRanges(Ranges); } bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { - for (const auto& R : getAddressRanges()) { + for (const auto &R : getAddressRanges()) { if (R.LowPC <= Address && Address < R.HighPC) return true; } return false; } -const char * -DWARFDie::getSubroutineName(DINameKind Kind) const { +const char *DWARFDie::getSubroutineName(DINameKind Kind) const { if (!isSubroutineDIE()) return nullptr; return getName(Kind); } -const char * -DWARFDie::getName(DINameKind Kind) const { +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)) + 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)) @@ -304,18 +436,33 @@ void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, CallDiscriminator = toUnsigned(find(DW_AT_GNU_discriminator), 0); } -void DWARFDie::dump(raw_ostream &OS, unsigned RecurseDepth, unsigned Indent, +/// 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) { + DumpOpts.ShowParents = false; + Indent = dumpParentChain(getParent(), OS, Indent, DumpOpts); + } + if (debug_info_data.isValidOffset(offset)) { uint32_t abbrCode = debug_info_data.getULEB128(&offset); - WithColor(OS, syntax::Address).get() << format("\n0x%8.8x: ", Offset); - + if (DumpOpts.ShowAddresses) + WithColor(OS, syntax::Address).get() << format("\n0x%8.8x: ", Offset); + if (abbrCode) { auto AbbrevDecl = getAbbreviationDeclarationPtr(); if (AbbrevDecl) { @@ -324,9 +471,9 @@ void DWARFDie::dump(raw_ostream &OS, unsigned RecurseDepth, unsigned Indent, WithColor(OS, syntax::Tag).get().indent(Indent) << tagString; else WithColor(OS, syntax::Tag).get().indent(Indent) - << format("DW_TAG_Unknown_%x", getTag()); + << format("DW_TAG_Unknown_%x", getTag()); - if (!DumpOpts.Brief) + if (DumpOpts.Verbose) OS << format(" [%u] %c", abbrCode, AbbrevDecl->hasChildren() ? '*' : ' '); OS << '\n'; @@ -342,17 +489,18 @@ void DWARFDie::dump(raw_ostream &OS, unsigned RecurseDepth, unsigned Indent, dumpAttribute(OS, *this, &offset, AttrSpec.Attr, AttrSpec.Form, Indent, DumpOpts); } - + DWARFDie child = getFirstChild(); - if (RecurseDepth > 0 && child) { + if (DumpOpts.ShowChildren && DumpOpts.RecurseDepth > 0 && child) { + DumpOpts.RecurseDepth--; while (child) { - child.dump(OS, RecurseDepth-1, Indent+2, DumpOpts); + child.dump(OS, Indent + 2, DumpOpts); child = child.getSibling(); } } } else { OS << "Abbreviation code not found in 'debug_abbrev' class for code: " - << abbrCode << '\n'; + << abbrCode << '\n'; } } else { OS.indent(Indent) << "NULL\n"; @@ -360,6 +508,8 @@ void DWARFDie::dump(raw_ostream &OS, unsigned RecurseDepth, unsigned Indent, } } +LLVM_DUMP_METHOD void DWARFDie::dump() const { dump(llvm::errs(), 0); } + DWARFDie DWARFDie::getParent() const { if (isValid()) return U->getParent(Die); @@ -372,14 +522,19 @@ DWARFDie DWARFDie::getSibling() const { return DWARFDie(); } -iterator_range<DWARFDie::attribute_iterator> -DWARFDie::attributes() const { +DWARFDie DWARFDie::getFirstChild() const { + if (isValid()) + return U->getFirstChild(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) { +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) { @@ -406,7 +561,7 @@ void DWARFDie::attribute_iterator::updateForIndex( auto U = Die.getDwarfUnit(); assert(U && "Die must have valid DWARF unit"); bool b = AttrValue.Value.extractValue(U->getDebugInfoExtractor(), - &ParseOffset, U); + &ParseOffset, U->getFormParams(), U); (void)b; assert(b && "extractValue cannot fail on fully parsed DWARF"); AttrValue.ByteSize = ParseOffset - AttrValue.Offset; diff --git a/lib/DebugInfo/DWARF/DWARFExpression.cpp b/lib/DebugInfo/DWARF/DWARFExpression.cpp new file mode 100644 index 0000000000000..c704c2901aef7 --- /dev/null +++ b/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -0,0 +1,274 @@ +//===-- 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_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) { + for (auto &Op : *this) { + if (!Op.print(OS, this, RegInfo, /* isEH */ false)) { + 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/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/lib/DebugInfo/DWARF/DWARFFormValue.cpp index 83a7792e12447..44886de2e3d58 100644 --- a/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -186,6 +186,7 @@ bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, 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: @@ -276,7 +277,8 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { } bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, - uint32_t *OffsetPtr, const DWARFUnit *CU) { + uint32_t *OffsetPtr, DWARFFormParams FP, + const DWARFUnit *CU) { U = CU; bool Indirect = false; bool IsBlock = false; @@ -288,10 +290,8 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, switch (Form) { case DW_FORM_addr: case DW_FORM_ref_addr: { - if (!U) - return false; - uint16_t Size = (Form == DW_FORM_addr) ? U->getAddressByteSize() - : U->getRefAddrByteSize(); + uint16_t Size = + (Form == DW_FORM_addr) ? FP.AddrSize : FP.getRefAddrByteSize(); Value.uval = Data.getRelocatedValue(Size, OffsetPtr, &Value.SectionIndex); break; } @@ -340,6 +340,11 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, 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; @@ -360,10 +365,8 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, case DW_FORM_GNU_strp_alt: case DW_FORM_line_strp: case DW_FORM_strp_sup: { - if (!U) - return false; Value.uval = - Data.getRelocatedValue(U->getDwarfOffsetByteSize(), OffsetPtr); + Data.getRelocatedValue(FP.getDwarfOffsetByteSize(), OffsetPtr); break; } case DW_FORM_flag_present: @@ -396,21 +399,22 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, return true; } -void DWARFFormValue::dump(raw_ostream &OS) const { +void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { uint64_t UValue = Value.uval; bool CURelativeOffset = false; - + raw_ostream &AddrOS = + DumpOpts.ShowAddresses ? WithColor(OS, syntax::Address).get() : nulls(); switch (Form) { case DW_FORM_addr: - OS << format("0x%016" PRIx64, UValue); + AddrOS << format("0x%016" PRIx64, UValue); break; case DW_FORM_GNU_addr_index: { - OS << format(" indexed (%8.8x) address = ", (uint32_t)UValue); + AddrOS << format(" indexed (%8.8x) address = ", (uint32_t)UValue); uint64_t Address; if (U == nullptr) OS << "<invalid dwarf unit>"; else if (U->getAddrOffsetSectionItem(UValue, Address)) - OS << format("0x%016" PRIx64, Address); + AddrOS << format("0x%016" PRIx64, Address); else OS << "<no .debug_addr section>"; break; @@ -429,9 +433,14 @@ void DWARFFormValue::dump(raw_ostream &OS) const { 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); @@ -481,7 +490,8 @@ void DWARFFormValue::dump(raw_ostream &OS) const { OS << Value.uval; break; case DW_FORM_strp: - OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); + if (DumpOpts.Verbose) + OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); dumpString(OS); break; case DW_FORM_strx: @@ -490,38 +500,40 @@ void DWARFFormValue::dump(raw_ostream &OS) const { case DW_FORM_strx3: case DW_FORM_strx4: case DW_FORM_GNU_str_index: - OS << format(" indexed (%8.8x) string = ", (uint32_t)UValue); + if (DumpOpts.Verbose) + OS << format(" indexed (%8.8x) string = ", (uint32_t)UValue); dumpString(OS); break; case DW_FORM_GNU_strp_alt: - OS << format("alt indirect string, offset: 0x%" PRIx64 "", UValue); + if (DumpOpts.Verbose) + OS << format("alt indirect string, offset: 0x%" PRIx64 "", UValue); dumpString(OS); break; case DW_FORM_ref_addr: - OS << format("0x%016" PRIx64, UValue); + AddrOS << format("0x%016" PRIx64, UValue); break; case DW_FORM_ref1: CURelativeOffset = true; - OS << format("cu + 0x%2.2x", (uint8_t)UValue); + AddrOS << format("cu + 0x%2.2x", (uint8_t)UValue); break; case DW_FORM_ref2: CURelativeOffset = true; - OS << format("cu + 0x%4.4x", (uint16_t)UValue); + AddrOS << format("cu + 0x%4.4x", (uint16_t)UValue); break; case DW_FORM_ref4: CURelativeOffset = true; - OS << format("cu + 0x%4.4x", (uint32_t)UValue); + AddrOS << format("cu + 0x%4.4x", (uint32_t)UValue); break; case DW_FORM_ref8: CURelativeOffset = true; - OS << format("cu + 0x%8.8" PRIx64, UValue); + AddrOS << format("cu + 0x%8.8" PRIx64, UValue); break; case DW_FORM_ref_udata: CURelativeOffset = true; - OS << format("cu + 0x%" PRIx64, UValue); + AddrOS << format("cu + 0x%" PRIx64, UValue); break; case DW_FORM_GNU_ref_alt: - OS << format("<alt 0x%" PRIx64 ">", UValue); + AddrOS << format("<alt 0x%" PRIx64 ">", UValue); break; // All DW_FORM_indirect attributes should be resolved prior to calling @@ -532,7 +544,7 @@ void DWARFFormValue::dump(raw_ostream &OS) const { // Should be formatted to 64-bit for DWARF64. case DW_FORM_sec_offset: - OS << format("0x%08x", (uint32_t)UValue); + AddrOS << format("0x%08x", (uint32_t)UValue); break; default: @@ -540,7 +552,7 @@ void DWARFFormValue::dump(raw_ostream &OS) const { break; } - if (CURelativeOffset) { + if (CURelativeOffset && DumpOpts.Verbose) { OS << " => {"; WithColor(OS, syntax::Address).get() << format("0x%8.8" PRIx64, UValue + (U ? U->getOffset() : 0)); @@ -648,7 +660,8 @@ Optional<int64_t> DWARFFormValue::getAsSignedConstant() const { } Optional<ArrayRef<uint8_t>> DWARFFormValue::getAsBlock() const { - if (!isFormClass(FC_Block) && !isFormClass(FC_Exprloc)) + if (!isFormClass(FC_Block) && !isFormClass(FC_Exprloc) && + Form != DW_FORM_data16) return None; return makeArrayRef(Value.data, Value.uval); } diff --git a/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp b/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp index fd1684d33a16b..206c12fa403f3 100644 --- a/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp +++ b/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp @@ -31,11 +31,11 @@ bool DWARFTypeUnit::extractImpl(DataExtractor debug_info, return TypeOffset < getLength() + SizeOfLength; } -void DWARFTypeUnit::dump(raw_ostream &OS, bool SummarizeTypes) { +void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { DWARFDie TD = getDIEForOffset(TypeOffset + getOffset()); const char *Name = TD.getName(DINameKind::ShortName); - if (SummarizeTypes) { + if (DumpOpts.SummarizeTypes) { OS << "name = '" << Name << "'" << " type_signature = " << format("0x%016" PRIx64, TypeHash) << " length = " << format("0x%08x", getLength()) << '\n'; @@ -55,7 +55,7 @@ void DWARFTypeUnit::dump(raw_ostream &OS, bool SummarizeTypes) { << " (next unit at " << format("0x%08x", getNextUnitOffset()) << ")\n"; if (DWARFDie TU = getUnitDIE(false)) - TU.dump(OS, -1U); + TU.dump(OS, 0, DumpOpts); else OS << "<type unit can't be parsed!>\n\n"; } diff --git a/lib/DebugInfo/DWARF/DWARFUnit.cpp b/lib/DebugInfo/DWARF/DWARFUnit.cpp index 043bdb874f431..c3d8ff2cbc294 100644 --- a/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -30,18 +30,20 @@ using namespace llvm; using namespace dwarf; void DWARFUnitSectionBase::parse(DWARFContext &C, const DWARFSection &Section) { - parseImpl(C, Section, C.getDebugAbbrev(), &C.getRangeSection(), - C.getStringSection(), C.getStringOffsetSection(), - &C.getAddrSection(), C.getLineSection(), C.isLittleEndian(), false); + const DWARFObject &D = C.getDWARFObj(); + parseImpl(C, Section, C.getDebugAbbrev(), &D.getRangeSection(), + D.getStringSection(), D.getStringOffsetSection(), + &D.getAddrSection(), D.getLineSection(), D.isLittleEndian(), false, + false); } void DWARFUnitSectionBase::parseDWO(DWARFContext &C, - const DWARFSection &DWOSection, - DWARFUnitIndex *Index) { - parseImpl(C, DWOSection, C.getDebugAbbrevDWO(), &C.getRangeDWOSection(), - C.getStringDWOSection(), C.getStringOffsetDWOSection(), - &C.getAddrSection(), C.getLineDWOSection(), C.isLittleEndian(), - true); + const DWARFSection &DWOSection, bool Lazy) { + const DWARFObject &D = C.getDWARFObj(); + parseImpl(C, DWOSection, C.getDebugAbbrevDWO(), &D.getRangeDWOSection(), + D.getStringDWOSection(), D.getStringOffsetDWOSection(), + &D.getAddrSection(), D.getLineDWOSection(), C.isLittleEndian(), + true, Lazy); } DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, @@ -59,13 +61,18 @@ DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, DWARFUnit::~DWARFUnit() = default; +DWARFDataExtractor DWARFUnit::getDebugInfoExtractor() const { + return DWARFDataExtractor(Context.getDWARFObj(), InfoSection, isLittleEndian, + getAddressByteSize()); +} + bool DWARFUnit::getAddrOffsetSectionItem(uint32_t Index, uint64_t &Result) const { uint32_t Offset = AddrOffsetSectionBase + Index * getAddressByteSize(); if (AddrOffsetSection->Data.size() < Offset + getAddressByteSize()) return false; - DWARFDataExtractor DA(*AddrOffsetSection, isLittleEndian, - getAddressByteSize()); + DWARFDataExtractor DA(Context.getDWARFObj(), *AddrOffsetSection, + isLittleEndian, getAddressByteSize()); Result = DA.getRelocatedAddress(&Offset); return true; } @@ -76,7 +83,8 @@ bool DWARFUnit::getStringOffsetSectionItem(uint32_t Index, uint32_t Offset = StringOffsetSectionBase + Index * ItemSize; if (StringOffsetSection.Data.size() < Offset + ItemSize) return false; - DWARFDataExtractor DA(StringOffsetSection, isLittleEndian, 0); + DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, + isLittleEndian, 0); Result = DA.getRelocatedValue(ItemSize, &Offset); return true; } @@ -86,7 +94,6 @@ bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { // FIXME: Support DWARF64. FormParams.Format = DWARF32; FormParams.Version = debug_info.getU16(offset_ptr); - uint64_t AbbrOffset; if (FormParams.Version >= 5) { UnitType = debug_info.getU8(offset_ptr); FormParams.AddrSize = debug_info.getU8(offset_ptr); @@ -116,9 +123,7 @@ bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { // Keep track of the highest DWARF version we encounter across all units. Context.setMaxVersionIfGreater(getVersion()); - - Abbrevs = Abbrev->getAbbreviationDeclarationSet(AbbrOffset); - return Abbrevs != nullptr; + return true; } bool DWARFUnit::extract(DataExtractor debug_info, uint32_t *offset_ptr) { @@ -141,8 +146,8 @@ bool DWARFUnit::extractRangeList(uint32_t RangeListOffset, DWARFDebugRangeList &RangeList) const { // Require that compile unit is extracted. assert(!DieArray.empty()); - DWARFDataExtractor RangesData(*RangeSection, isLittleEndian, - getAddressByteSize()); + DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, + isLittleEndian, getAddressByteSize()); uint32_t ActualRangeListOffset = RangeSectionBase + RangeListOffset; return RangeList.extract(RangesData, &ActualRangeListOffset); } @@ -152,7 +157,7 @@ void DWARFUnit::clear() { Length = 0; Abbrevs = nullptr; FormParams = DWARFFormParams({0, 0, DWARF32}); - BaseAddr = 0; + BaseAddr.reset(); RangeSectionBase = 0; AddrOffsetSectionBase = 0; clearDIEs(false); @@ -234,11 +239,17 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { // If CU DIE was just parsed, copy several attribute values from it. if (!HasCUDie) { DWARFDie UnitDie = getUnitDIE(); - auto BaseAddr = toAddress(UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc})); - if (BaseAddr) - setBaseAddress(*BaseAddr); - AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); - RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); + Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); + if (Optional<uint64_t> Addr = toAddress(PC)) + setBaseAddress({*Addr, PC->getSectionIndex()}); + + if (!isDWO) { + assert(AddrOffsetSectionBase == 0); + assert(RangeSectionBase == 0); + AddrOffsetSectionBase = + toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); + RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); + } // In general, we derive the offset of the unit's contibution to the // debug_str_offsets{.dwo} section from the unit DIE's @@ -295,18 +306,8 @@ bool DWARFUnit::parseDWO() { void DWARFUnit::clearDIEs(bool KeepCUDie) { if (DieArray.size() > (unsigned)KeepCUDie) { - // std::vectors never get any smaller when resized to a smaller size, - // or when clear() or erase() are called, the size will report that it - // is smaller, but the memory allocated remains intact (call capacity() - // to see this). So we need to create a temporary vector and swap the - // contents which will cause just the internal pointers to be swapped - // so that when temporary vector goes out of scope, it will destroy the - // contents. - std::vector<DWARFDebugInfoEntry> TmpArray; - DieArray.swap(TmpArray); - // Save at least the compile unit DIE - if (KeepCUDie) - DieArray.push_back(TmpArray.front()); + DieArray.resize((unsigned)KeepCUDie); + DieArray.shrink_to_fit(); } } @@ -439,7 +440,7 @@ DWARFDie DWARFUnit::getSibling(const DWARFDebugInfoEntry *Die) { // 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) { @@ -448,3 +449,20 @@ DWARFDie DWARFUnit::getSibling(const DWARFDebugInfoEntry *Die) { } 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]); +} + +const DWARFAbbreviationDeclarationSet *DWARFUnit::getAbbreviations() const { + if (!Abbrevs) + Abbrevs = Abbrev->getAbbreviationDeclarationSet(AbbrOffset); + return Abbrevs; +} diff --git a/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp index 59b3d0ca55a63..17f17572a3092 100644 --- a/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp +++ b/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp @@ -123,7 +123,7 @@ StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { } void DWARFUnitIndex::dump(raw_ostream &OS) const { - if (!Header.NumBuckets) + if (!*this) return; Header.dump(OS); @@ -165,8 +165,25 @@ DWARFUnitIndex::Entry::getOffset() const { const DWARFUnitIndex::Entry * DWARFUnitIndex::getFromOffset(uint32_t Offset) const { for (uint32_t i = 0; i != Header.NumBuckets; ++i) - if (const auto &Contribs = Rows[i].Contributions) - if (Contribs[InfoColumn].Offset == Offset) + if (const auto &Contribs = Rows[i].Contributions) { + const auto &InfoContrib = Contribs[InfoColumn]; + if (InfoContrib.Offset <= Offset && + Offset < (InfoContrib.Offset + InfoContrib.Length)) return &Rows[i]; + } return nullptr; } + +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/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/lib/DebugInfo/DWARF/DWARFVerifier.cpp index 4de46bea301e9..3d473698b4639 100644 --- a/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -7,14 +7,17 @@ // //===----------------------------------------------------------------------===// +#include "SyntaxHighlighting.h" #include "llvm/DebugInfo/DWARF/DWARFVerifier.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/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include <map> #include <set> @@ -23,6 +26,86 @@ using namespace llvm; using namespace dwarf; using namespace object; +using namespace syntax; + +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, @@ -53,7 +136,7 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, UnitType = DebugInfoData.getU8(Offset); AddrSize = DebugInfoData.getU8(Offset); AbbrOffset = DebugInfoData.getU32(Offset); - ValidType = DWARFUnit::isValidUnitType(UnitType); + ValidType = dwarf::isUnitType(UnitType); } else { UnitType = 0; AbbrOffset = DebugInfoData.getU32(Offset); @@ -69,25 +152,26 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset || !ValidType) { Success = false; - OS << format("Units[%d] - start offset: 0x%08x \n", UnitIndex, OffsetStart); + error() << format("Units[%d] - start offset: 0x%08x \n", UnitIndex, + OffsetStart); if (!ValidLength) - OS << "\tError: The length for this unit is too " + note() << "The length for this unit is too " "large for the .debug_info provided.\n"; if (!ValidVersion) - OS << "\tError: The 16 bit unit header version is not valid.\n"; + note() << "The 16 bit unit header version is not valid.\n"; if (!ValidType) - OS << "\tError: The unit type encoding is not valid.\n"; + note() << "The unit type encoding is not valid.\n"; if (!ValidAbbrevOffset) - OS << "\tError: The offset into the .debug_abbrev section is " + note() << "The offset into the .debug_abbrev section is " "not valid.\n"; if (!ValidAddrSize) - OS << "\tError: The address size is unsupported.\n"; + note() << "The address size is unsupported.\n"; } *Offset = OffsetStart + Length + 4; return Success; } -bool DWARFVerifier::verifyUnitContents(DWARFUnit Unit) { +bool DWARFVerifier::verifyUnitContents(DWARFUnit Unit, uint8_t UnitType) { uint32_t NumUnitErrors = 0; unsigned NumDies = Unit.getNumDIEs(); for (unsigned I = 0; I < NumDies; ++I) { @@ -99,20 +183,89 @@ bool DWARFVerifier::verifyUnitContents(DWARFUnit Unit) { NumUnitErrors += verifyDebugInfoForm(Die, AttrValue); } } + + DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false); + if (!Die) { + error() << "Compilation unit without DIE.\n"; + NumUnitErrors++; + return NumUnitErrors == 0; + } + + if (!dwarf::isUnitType(Die.getTag())) { + error() << "Compilation unit root DIE is not a unit DIE: " + << dwarf::TagString(Die.getTag()) << ".\n"; + NumUnitErrors++; + } + + if (UnitType != 0 && + !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 == 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; +} + bool DWARFVerifier::handleDebugInfo() { OS << "Verifying .debug_info Unit Header Chain...\n"; - DWARFDataExtractor DebugInfoData(DCtx.getInfoSection(), DCtx.isLittleEndian(), - 0); + const DWARFObject &DObj = DCtx.getDWARFObj(); + DWARFDataExtractor DebugInfoData(DObj, DObj.getInfoSection(), + DCtx.isLittleEndian(), 0); uint32_t 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); + DWARFUnitSection<DWARFTypeUnit> TUSection{}; + DWARFUnitSection<DWARFCompileUnit> CUSection{}; while (hasDIE) { OffsetStart = Offset; if (!verifyUnitHeader(DebugInfoData, &Offset, UnitIdx, UnitType, @@ -125,12 +278,11 @@ bool DWARFVerifier::handleDebugInfo() { switch (UnitType) { case dwarf::DW_UT_type: case dwarf::DW_UT_split_type: { - DWARFUnitSection<DWARFTypeUnit> TUSection{}; Unit.reset(new DWARFTypeUnit( - DCtx, DCtx.getInfoSection(), DCtx.getDebugAbbrev(), - &DCtx.getRangeSection(), DCtx.getStringSection(), - DCtx.getStringOffsetSection(), &DCtx.getAppleObjCSection(), - DCtx.getLineSection(), DCtx.isLittleEndian(), false, TUSection, + DCtx, DObj.getInfoSection(), DCtx.getDebugAbbrev(), + &DObj.getRangeSection(), DObj.getStringSection(), + DObj.getStringOffsetSection(), &DObj.getAppleObjCSection(), + DObj.getLineSection(), DCtx.isLittleEndian(), false, TUSection, nullptr)); break; } @@ -141,72 +293,141 @@ bool DWARFVerifier::handleDebugInfo() { // UnitType = 0 means that we are // verifying a compile unit in DWARF v4. case 0: { - DWARFUnitSection<DWARFCompileUnit> CUSection{}; Unit.reset(new DWARFCompileUnit( - DCtx, DCtx.getInfoSection(), DCtx.getDebugAbbrev(), - &DCtx.getRangeSection(), DCtx.getStringSection(), - DCtx.getStringOffsetSection(), &DCtx.getAppleObjCSection(), - DCtx.getLineSection(), DCtx.isLittleEndian(), false, CUSection, + DCtx, DObj.getInfoSection(), DCtx.getDebugAbbrev(), + &DObj.getRangeSection(), DObj.getStringSection(), + DObj.getStringOffsetSection(), &DObj.getAppleObjCSection(), + DObj.getLineSection(), DCtx.isLittleEndian(), false, CUSection, nullptr)); break; } default: { llvm_unreachable("Invalid UnitType."); } } Unit->extract(DebugInfoData, &OffsetStart); - if (!verifyUnitContents(*Unit)) + if (!verifyUnitContents(*Unit, UnitType)) ++NumDebugInfoErrors; } hasDIE = DebugInfoData.isValidOffset(Offset); ++UnitIdx; } if (UnitIdx == 0 && !hasDIE) { - OS << "Warning: .debug_info is empty.\n"; + warn() << ".debug_info is empty.\n"; isHeaderChainValid = true; } NumDebugInfoErrors += verifyDebugInfoReferences(); return (isHeaderChainValid && NumDebugInfoErrors == 0); } +unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, + DieRangeInfo &ParentRI) { + unsigned NumErrors = 0; + + if (!Die.isValid()) + return NumErrors; + + DWARFAddressRangesVector Ranges = Die.getAddressRanges(); + + // Build RI for this DIE and check that ranges within this DIE do not + // overlap. + DieRangeInfo RI(Die); + 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:"; + Die.dump(OS, 0); + IntersectingChild->Die.dump(OS, 0); + OS << "\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:"; + Die.dump(OS, 0); + ParentRI.Die.dump(OS, 0); + OS << "\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'; + Die.dump(OS, 0, DumpOpts); + OS << "\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 >= DCtx.getRangeSection().Data.size()) { - ++NumErrors; - OS << "error: DW_AT_ranges offset is beyond .debug_ranges " - "bounds:\n"; - Die.dump(OS, 0); - OS << "\n"; - } - } else { - ++NumErrors; - OS << "error: DIE has invalid DW_AT_ranges encoding:\n"; - Die.dump(OS, 0); - OS << "\n"; + 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 >= DCtx.getLineSection().Data.size()) { - ++NumErrors; - OS << "error: DW_AT_stmt_list offset is beyond .debug_line " - "bounds: " - << format("0x%08" PRIx64, *SectionOffset) << "\n"; - Die.dump(OS, 0); - OS << "\n"; - } - } else { - ++NumErrors; - OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n"; - Die.dump(OS, 0); - OS << "\n"; + 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: { + Optional<ArrayRef<uint8_t>> Expr = AttrValue.Value.getAsBlock(); + if (!Expr) { + ReportError("DIE has invalid DW_AT_location encoding:"); + break; + } + + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data( + StringRef(reinterpret_cast<const char *>(Expr->data()), Expr->size()), + DCtx.isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.isError(); + }); + if (Error) + ReportError("DIE contains invalid DWARF expression:"); + break; + } default: break; @@ -216,6 +437,7 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, DWARFAttribute &AttrValue) { + const DWARFObject &DObj = DCtx.getDWARFObj(); unsigned NumErrors = 0; const auto Form = AttrValue.Value.getForm(); switch (Form) { @@ -233,11 +455,11 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, auto CUOffset = AttrValue.Value.getRawUValue(); if (CUOffset >= CUSize) { ++NumErrors; - OS << "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); + 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); OS << "\n"; } else { // Valid reference, but we will verify it points to an actual @@ -253,11 +475,11 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, Optional<uint64_t> RefVal = AttrValue.Value.getAsReference(); assert(RefVal); if (RefVal) { - if (*RefVal >= DCtx.getInfoSection().Data.size()) { + if (*RefVal >= DObj.getInfoSection().Data.size()) { ++NumErrors; - OS << "error: DW_FORM_ref_addr offset beyond .debug_info " - "bounds:\n"; - Die.dump(OS, 0); + error() << "DW_FORM_ref_addr offset beyond .debug_info " + "bounds:\n"; + Die.dump(OS, 0, DumpOpts); OS << "\n"; } else { // Valid reference, but we will verify it points to an actual @@ -270,10 +492,10 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, case DW_FORM_strp: { auto SecOffset = AttrValue.Value.getAsSectionOffset(); assert(SecOffset); // DW_FORM_strp is a section offset. - if (SecOffset && *SecOffset >= DCtx.getStringSection().size()) { + if (SecOffset && *SecOffset >= DObj.getStringSection().size()) { ++NumErrors; - OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n"; - Die.dump(OS, 0); + error() << "DW_FORM_strp offset beyond .debug_str bounds:\n"; + Die.dump(OS, 0, DumpOpts); OS << "\n"; } break; @@ -294,11 +516,11 @@ unsigned DWARFVerifier::verifyDebugInfoReferences() { if (Die) continue; ++NumErrors; - OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first) - << ". Offset is in between DIEs:\n"; + error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first) + << ". Offset is in between DIEs:\n"; for (auto Offset : Pair.second) { auto ReferencingDie = DCtx.getDIEForOffset(Offset); - ReferencingDie.dump(OS, 0); + ReferencingDie.dump(OS, 0, DumpOpts); OS << "\n"; } OS << "\n"; @@ -318,12 +540,12 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() { continue; const uint32_t LineTableOffset = *StmtSectionOffset; auto LineTable = DCtx.getLineTableForUnit(CU.get()); - if (LineTableOffset < DCtx.getLineSection().Data.size()) { + if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) { if (!LineTable) { ++NumDebugLineErrors; - OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) - << "] was not able to be parsed for CU:\n"; - Die.dump(OS, 0); + error() << ".debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + Die.dump(OS, 0, DumpOpts); OS << '\n'; continue; } @@ -337,12 +559,12 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() { auto Iter = StmtListToDie.find(LineTableOffset); if (Iter != StmtListToDie.end()) { ++NumDebugLineErrors; - OS << "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"; - Iter->second.dump(OS, 0); - Die.dump(OS, 0); + 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"; + Iter->second.dump(OS, 0, DumpOpts); + Die.dump(OS, 0, DumpOpts); OS << '\n'; // Already verified this line table before, no need to do it again. continue; @@ -359,17 +581,57 @@ void DWARFVerifier::verifyDebugLineRows() { // .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; - OS << "error: .debug_line[" - << format("0x%08" PRIx64, - *toSectionOffset(Die.find(DW_AT_stmt_list))) - << "] row[" << RowIndex - << "] decreases in address from previous row:\n"; + 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) @@ -378,13 +640,14 @@ void DWARFVerifier::verifyDebugLineRows() { OS << '\n'; } + // Verify file index. if (Row.File > MaxFileIndex) { ++NumDebugLineErrors; - OS << "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"; + 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'; @@ -406,94 +669,137 @@ bool DWARFVerifier::handleDebugLine() { return NumDebugLineErrors == 0; } -bool DWARFVerifier::handleAppleNames() { - NumAppleNamesErrors = 0; +unsigned DWARFVerifier::verifyAccelTable(const DWARFSection *AccelSection, + DataExtractor *StrData, + const char *SectionName) { + unsigned NumErrors = 0; + DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection, + DCtx.isLittleEndian(), 0); + DWARFAcceleratorTable AccelTable(AccelSectionData, *StrData); - DWARFDataExtractor AppleNamesSection(DCtx.getAppleNamesSection(), - DCtx.isLittleEndian(), 0); - DataExtractor StrData(DCtx.getStringSection(), DCtx.isLittleEndian(), 0); - DWARFAcceleratorTable AppleNames(AppleNamesSection, StrData); + OS << "Verifying " << SectionName << "...\n"; - if (!AppleNames.extract()) { - return true; + // 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; } - OS << "Verifying .apple_names...\n"; + // 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 = AppleNames.getNumBuckets(); - uint32_t NumHashes = AppleNames.getNumHashes(); + uint32_t NumBuckets = AccelTable.getNumBuckets(); + uint32_t NumHashes = AccelTable.getNumHashes(); uint32_t BucketsOffset = - AppleNames.getSizeHdr() + AppleNames.getHeaderDataLength(); + 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 = AppleNamesSection.getU32(&BucketsOffset); + uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset); if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) { - OS << format("error: Bucket[%d] has invalid hash index: %u\n", BucketIdx, - HashIdx); - ++NumAppleNamesErrors; + error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx, + HashIdx); + ++NumErrors; } } - - uint32_t NumAtoms = AppleNames.getAtomsDesc().size(); + uint32_t NumAtoms = AccelTable.getAtomsDesc().size(); if (NumAtoms == 0) { - OS << "error: no atoms; failed to read HashData\n"; - ++NumAppleNamesErrors; - return false; + error() << "No atoms: failed to read HashData.\n"; + return 1; } - - if (!AppleNames.validateForms()) { - OS << "error: unsupported form; failed to read HashData\n"; - ++NumAppleNamesErrors; - return false; + 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 = AppleNamesSection.getU32(&HashOffset); - uint32_t HashDataOffset = AppleNamesSection.getU32(&DataOffset); - if (!AppleNamesSection.isValidOffsetForDataOfSize(HashDataOffset, - sizeof(uint64_t))) { - OS << format("error: Hash[%d] has invalid HashData offset: 0x%08x\n", - HashIdx, HashDataOffset); - ++NumAppleNamesErrors; + 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; - uint32_t DieOffset = dwarf::DW_INVALID_OFFSET; - - while ((StrpOffset = AppleNamesSection.getU32(&HashDataOffset)) != 0) { + unsigned Offset; + unsigned Tag; + while ((StrpOffset = AccelSectionData.getU32(&HashDataOffset)) != 0) { const uint32_t NumHashDataObjects = - AppleNamesSection.getU32(&HashDataOffset); + AccelSectionData.getU32(&HashDataOffset); for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects; ++HashDataIdx) { - DieOffset = AppleNames.readAtoms(HashDataOffset); - if (!DCtx.getDIEForOffset(DieOffset)) { + 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); + const char *Name = StrData->getCStr(&StringOffset); if (!Name) Name = "<NULL>"; - OS << format( - "error: .apple_names Bucket[%d] Hash[%d] = 0x%08x " + 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", - BucketIdx, HashIdx, Hash, StringCount, StrpOffset, HashDataIdx, - DieOffset, Name); + SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset, + HashDataIdx, Offset, Name); - ++NumAppleNamesErrors; + ++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 NumAppleNamesErrors == 0; + return NumErrors; +} + +bool DWARFVerifier::handleAccelTables() { + const DWARFObject &D = DCtx.getDWARFObj(); + DataExtractor StrData(D.getStringSection(), DCtx.isLittleEndian(), 0); + unsigned NumErrors = 0; + if (!D.getAppleNamesSection().Data.empty()) + NumErrors += + verifyAccelTable(&D.getAppleNamesSection(), &StrData, ".apple_names"); + if (!D.getAppleTypesSection().Data.empty()) + NumErrors += + verifyAccelTable(&D.getAppleTypesSection(), &StrData, ".apple_types"); + if (!D.getAppleNamespacesSection().Data.empty()) + NumErrors += verifyAccelTable(&D.getAppleNamespacesSection(), &StrData, + ".apple_namespaces"); + if (!D.getAppleObjCSection().Data.empty()) + NumErrors += + verifyAccelTable(&D.getAppleObjCSection(), &StrData, ".apple_objc"); + return NumErrors == 0; +} + +raw_ostream &DWARFVerifier::error() const { + return WithColor(OS, syntax::Error).get() << "error: "; +} + +raw_ostream &DWARFVerifier::warn() const { + return WithColor(OS, syntax::Warning).get() << "warning: "; +} + +raw_ostream &DWARFVerifier::note() const { + return WithColor(OS, syntax::Note).get() << "note: "; } diff --git a/lib/DebugInfo/DWARF/LLVMBuild.txt b/lib/DebugInfo/DWARF/LLVMBuild.txt index 8242a7f2e7f77..a21276244ea75 100644 --- a/lib/DebugInfo/DWARF/LLVMBuild.txt +++ b/lib/DebugInfo/DWARF/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Library name = DebugInfoDWARF parent = DebugInfo -required_libraries = BinaryFormat Object Support +required_libraries = BinaryFormat Object MC Support diff --git a/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp b/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp index d4f44e446954f..65d66fc8f5148 100644 --- a/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp +++ b/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp @@ -24,12 +24,15 @@ WithColor::WithColor(raw_ostream &OS, enum HighlightColor Type) : OS(OS) { // Detect color from terminal type unless the user passed the --color option. if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE) { switch (Type) { - case Address: OS.changeColor(raw_ostream::YELLOW); break; - case String: OS.changeColor(raw_ostream::GREEN); break; - case Tag: OS.changeColor(raw_ostream::BLUE); break; - case Attribute: OS.changeColor(raw_ostream::CYAN); break; - case Enumerator: OS.changeColor(raw_ostream::MAGENTA); break; - case Macro: OS.changeColor(raw_ostream::RED); break; + case Address: OS.changeColor(raw_ostream::YELLOW); break; + case String: OS.changeColor(raw_ostream::GREEN); break; + case Tag: OS.changeColor(raw_ostream::BLUE); break; + case Attribute: OS.changeColor(raw_ostream::CYAN); break; + case Enumerator: OS.changeColor(raw_ostream::MAGENTA); break; + case Macro: OS.changeColor(raw_ostream::RED); break; + case Error: OS.changeColor(raw_ostream::RED, true); break; + case Warning: OS.changeColor(raw_ostream::MAGENTA, true); break; + case Note: OS.changeColor(raw_ostream::BLACK, true); break; } } } diff --git a/lib/DebugInfo/DWARF/SyntaxHighlighting.h b/lib/DebugInfo/DWARF/SyntaxHighlighting.h index 277de973dbf0e..686cf2c77608d 100644 --- a/lib/DebugInfo/DWARF/SyntaxHighlighting.h +++ b/lib/DebugInfo/DWARF/SyntaxHighlighting.h @@ -18,7 +18,17 @@ namespace dwarf { namespace syntax { // Symbolic names for various syntax elements. -enum HighlightColor { Address, String, Tag, Attribute, Enumerator, Macro }; +enum HighlightColor { + Address, + String, + Tag, + Attribute, + Enumerator, + Macro, + Error, + Warning, + Note +}; /// An RAII object that temporarily switches an output stream to a /// specific color. @@ -30,8 +40,8 @@ public: WithColor(raw_ostream &OS, enum HighlightColor Type); ~WithColor(); - raw_ostream& get() { return OS; } - operator raw_ostream& () { return OS; } + raw_ostream &get() { return OS; } + operator raw_ostream &() { return OS; } }; } // end namespace syntax diff --git a/lib/DebugInfo/MSF/MSFBuilder.cpp b/lib/DebugInfo/MSF/MSFBuilder.cpp index 0f4f785abf55a..9cd22ab7d8870 100644 --- a/lib/DebugInfo/MSF/MSFBuilder.cpp +++ b/lib/DebugInfo/MSF/MSFBuilder.cpp @@ -36,8 +36,7 @@ MSFBuilder::MSFBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow, BumpPtrAllocator &Allocator) : Allocator(Allocator), IsGrowable(CanGrow), FreePageMap(kDefaultFreePageMap), BlockSize(BlockSize), - MininumBlocks(MinBlockCount), BlockMapAddr(kDefaultBlockMapAddr), - FreeBlocks(MinBlockCount, true) { + BlockMapAddr(kDefaultBlockMapAddr), FreeBlocks(MinBlockCount, true) { FreeBlocks[kSuperBlockBlock] = false; FreeBlocks[kFreePageMap0Block] = false; FreeBlocks[kFreePageMap1Block] = false; @@ -107,7 +106,23 @@ Error MSFBuilder::allocateBlocks(uint32_t NumBlocks, return make_error<MSFError>(msf_error_code::insufficient_buffer, "There are no free Blocks in the file"); uint32_t AllocBlocks = NumBlocks - NumFreeBlocks; - FreeBlocks.resize(AllocBlocks + FreeBlocks.size(), true); + uint32_t OldBlockCount = FreeBlocks.size(); + uint32_t NewBlockCount = AllocBlocks + OldBlockCount; + uint32_t NextFpmBlock = alignTo(OldBlockCount, BlockSize) + 1; + FreeBlocks.resize(NewBlockCount, true); + // If we crossed over an fpm page, we actually need to allocate 2 extra + // blocks for each FPM group crossed and mark both blocks from the group as + // used. We may not actually use them since there are many more FPM blocks + // present than are required to represent all blocks in a given PDB, but we + // need to make sure they aren't allocated to a stream or something else. + // At the end when committing the PDB, we'll go through and mark the + // extraneous ones unused. + while (NextFpmBlock < NewBlockCount) { + NewBlockCount += 2; + FreeBlocks.resize(NewBlockCount, true); + FreeBlocks.reset(NextFpmBlock, NextFpmBlock + 2); + NextFpmBlock += BlockSize; + } } int I = 0; @@ -229,6 +244,19 @@ uint32_t MSFBuilder::computeDirectoryByteSize() const { return Size; } +static void finalizeFpmBlockStatus(uint32_t B, ArrayRef<ulittle32_t> &FpmBlocks, + BitVector &Fpm) { + if (FpmBlocks.empty() || FpmBlocks.front() != B) { + Fpm.set(B); + return; + } + + // If the next block in the actual layout is this block, it should *not* be + // free. + assert(!Fpm.test(B)); + FpmBlocks = FpmBlocks.drop_front(); +} + Expected<MSFLayout> MSFBuilder::build() { SuperBlock *SB = Allocator.Allocate<SuperBlock>(); MSFLayout L; @@ -287,5 +315,20 @@ Expected<MSFLayout> MSFBuilder::build() { } } + // FPM blocks occur in pairs at every `BlockLength` interval. While blocks of + // this form are reserved for FPM blocks, not all blocks of this form will + // actually be needed for FPM data because there are more blocks of this form + // than are required to represent a PDB file with a given number of blocks. + // So we need to find out which blocks are *actually* going to be real FPM + // blocks, then mark the reset of the reserved blocks as unallocated. + MSFStreamLayout FpmLayout = msf::getFpmStreamLayout(L, true); + auto FpmBlocks = makeArrayRef(FpmLayout.Blocks); + for (uint32_t B = kFreePageMap0Block; B < SB->NumBlocks; + B += msf::getFpmIntervalLength(L)) { + finalizeFpmBlockStatus(B, FpmBlocks, FreeBlocks); + finalizeFpmBlockStatus(B + 1, FpmBlocks, FreeBlocks); + } + L.FreePageMap = FreeBlocks; + return L; } diff --git a/lib/DebugInfo/MSF/MSFCommon.cpp b/lib/DebugInfo/MSF/MSFCommon.cpp index 1facf5efb4bbb..d7e1dcf31a3a4 100644 --- a/lib/DebugInfo/MSF/MSFCommon.cpp +++ b/lib/DebugInfo/MSF/MSFCommon.cpp @@ -59,3 +59,27 @@ Error llvm::msf::validateSuperBlock(const SuperBlock &SB) { return Error::success(); } + +MSFStreamLayout llvm::msf::getFpmStreamLayout(const MSFLayout &Msf, + bool IncludeUnusedFpmData, + bool AltFpm) { + MSFStreamLayout FL; + uint32_t NumFpmIntervals = getNumFpmIntervals(Msf, IncludeUnusedFpmData); + support::ulittle32_t FpmBlock = Msf.SB->FreeBlockMapBlock; + assert(FpmBlock == 1 || FpmBlock == 2); + if (AltFpm) { + // If they requested the alternate FPM, then 2 becomes 1 and 1 becomes 2. + FpmBlock = 3U - FpmBlock; + } + for (uint32_t I = 0; I < NumFpmIntervals; ++I) { + FL.Blocks.push_back(FpmBlock); + FpmBlock += msf::getFpmIntervalLength(Msf); + } + + if (IncludeUnusedFpmData) + FL.Length = NumFpmIntervals * Msf.SB->BlockSize; + else + FL.Length = divideCeil(Msf.SB->NumBlocks, 8); + + return FL; +} diff --git a/lib/DebugInfo/MSF/MappedBlockStream.cpp b/lib/DebugInfo/MSF/MappedBlockStream.cpp index e45f4ae0ed940..dec28eb306972 100644 --- a/lib/DebugInfo/MSF/MappedBlockStream.cpp +++ b/lib/DebugInfo/MSF/MappedBlockStream.cpp @@ -11,7 +11,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" -#include "llvm/DebugInfo/MSF/MSFStreamLayout.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" @@ -36,19 +36,6 @@ public: } // end anonymous namespace -static void initializeFpmStreamLayout(const MSFLayout &Layout, - MSFStreamLayout &FpmLayout) { - uint32_t NumFpmIntervals = msf::getNumFpmIntervals(Layout); - support::ulittle32_t FpmBlock = Layout.SB->FreeBlockMapBlock; - assert(FpmBlock == 1 || FpmBlock == 2); - while (NumFpmIntervals > 0) { - FpmLayout.Blocks.push_back(FpmBlock); - FpmBlock += msf::getFpmIntervalLength(Layout); - --NumFpmIntervals; - } - FpmLayout.Length = msf::getFullFpmByteSize(Layout); -} - using Interval = std::pair<uint32_t, uint32_t>; static Interval intersect(const Interval &I1, const Interval &I2) { @@ -95,15 +82,14 @@ std::unique_ptr<MappedBlockStream> MappedBlockStream::createFpmStream(const MSFLayout &Layout, BinaryStreamRef MsfData, BumpPtrAllocator &Allocator) { - MSFStreamLayout SL; - initializeFpmStreamLayout(Layout, SL); + MSFStreamLayout SL(getFpmStreamLayout(Layout)); return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator); } Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, ArrayRef<uint8_t> &Buffer) { // Make sure we aren't trying to read beyond the end of the stream. - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; if (tryReadContiguously(Offset, Size, Buffer)) @@ -181,7 +167,7 @@ Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, Error MappedBlockStream::readLongestContiguousChunk(uint32_t Offset, ArrayRef<uint8_t> &Buffer) { // Make sure we aren't trying to read beyond the end of the stream. - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; uint32_t First = Offset / BlockSize; @@ -257,7 +243,7 @@ Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t OffsetInBlock = Offset % BlockSize; // Make sure we aren't trying to read beyond the end of the stream. - if (auto EC = checkOffset(Offset, Buffer.size())) + if (auto EC = checkOffsetForRead(Offset, Buffer.size())) return EC; uint32_t BytesLeft = Buffer.size(); @@ -362,10 +348,27 @@ WritableMappedBlockStream::createDirectoryStream( std::unique_ptr<WritableMappedBlockStream> WritableMappedBlockStream::createFpmStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData, - BumpPtrAllocator &Allocator) { - MSFStreamLayout SL; - initializeFpmStreamLayout(Layout, SL); - return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator); + BumpPtrAllocator &Allocator, + bool AltFpm) { + // We only want to give the user a stream containing the bytes of the FPM that + // are actually valid, but we want to initialize all of the bytes, even those + // that come from reserved FPM blocks where the entire block is unused. To do + // this, we first create the full layout, which gives us a stream with all + // bytes and all blocks, and initialize everything to 0xFF (all blocks in the + // file are unused). Then we create the minimal layout (which contains only a + // subset of the bytes previously initialized), and return that to the user. + MSFStreamLayout MinLayout(getFpmStreamLayout(Layout, false, AltFpm)); + + MSFStreamLayout FullLayout(getFpmStreamLayout(Layout, true, AltFpm)); + auto Result = + createStream(Layout.SB->BlockSize, FullLayout, MsfData, Allocator); + if (!Result) + return Result; + std::vector<uint8_t> InitData(Layout.SB->BlockSize, 0xFF); + BinaryStreamWriter Initializer(*Result); + while (Initializer.bytesRemaining() > 0) + cantFail(Initializer.writeBytes(InitData)); + return createStream(Layout.SB->BlockSize, MinLayout, MsfData, Allocator); } Error WritableMappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, @@ -385,7 +388,7 @@ uint32_t WritableMappedBlockStream::getLength() { Error WritableMappedBlockStream::writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) { // Make sure we aren't trying to write beyond the end of the stream. - if (auto EC = checkOffset(Offset, Buffer.size())) + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) return EC; uint32_t BlockNum = Offset / getBlockSize(); diff --git a/lib/DebugInfo/PDB/CMakeLists.txt b/lib/DebugInfo/PDB/CMakeLists.txt index 9b1f37943e678..0be05e9bda5e1 100644 --- a/lib/DebugInfo/PDB/CMakeLists.txt +++ b/lib/DebugInfo/PDB/CMakeLists.txt @@ -17,11 +17,13 @@ if(LLVM_ENABLE_DIA_SDK) DIA/DIAEnumLineNumbers.cpp DIA/DIAEnumSourceFiles.cpp DIA/DIAEnumSymbols.cpp + DIA/DIAEnumTables.cpp DIA/DIAError.cpp DIA/DIALineNumber.cpp DIA/DIARawSymbol.cpp DIA/DIASession.cpp DIA/DIASourceFile.cpp + DIA/DIATable.cpp ) set(LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB/DIA") @@ -35,7 +37,6 @@ add_pdb_impl_folder(Native Native/DbiStreamBuilder.cpp Native/EnumTables.cpp Native/GlobalsStream.cpp - Native/GSI.cpp Native/Hash.cpp Native/HashTable.cpp Native/InfoStream.cpp @@ -44,6 +45,8 @@ add_pdb_impl_folder(Native Native/NativeBuiltinSymbol.cpp Native/NativeCompilandSymbol.cpp Native/NativeEnumModules.cpp + Native/NativeEnumSymbol.cpp + Native/NativeEnumTypes.cpp Native/NativeExeSymbol.cpp Native/NativeRawSymbol.cpp Native/NamedStreamMap.cpp @@ -53,7 +56,7 @@ add_pdb_impl_folder(Native Native/PDBStringTable.cpp Native/PDBStringTableBuilder.cpp Native/PublicsStream.cpp - Native/PublicsStreamBuilder.cpp + Native/GSIStreamBuilder.cpp Native/RawError.cpp Native/SymbolStream.cpp Native/TpiHashing.cpp diff --git a/lib/DebugInfo/PDB/DIA/DIAEnumTables.cpp b/lib/DebugInfo/PDB/DIA/DIAEnumTables.cpp new file mode 100644 index 0000000000000..511b55585ebd7 --- /dev/null +++ b/lib/DebugInfo/PDB/DIA/DIAEnumTables.cpp @@ -0,0 +1,53 @@ +//===- DIAEnumTables.cpp - DIA Table Enumerator Impl ------------*- 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/PDB/DIA/DIAEnumTables.h" +#include "llvm/DebugInfo/PDB/DIA/DIATable.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumTables::DIAEnumTables( + CComPtr<IDiaEnumTables> DiaEnumerator) + : Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumTables::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBTable> +DIAEnumTables::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaTable> Item; + VARIANT Var; + Var.vt = VT_UINT; + Var.uintVal = Index; + if (S_OK != Enumerator->Item(Var, &Item)) + return nullptr; + + return std::unique_ptr<IPDBTable>(new DIATable(Item)); +} + +std::unique_ptr<IPDBTable> DIAEnumTables::getNext() { + CComPtr<IDiaTable> Item; + ULONG CeltFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &CeltFetched)) + return nullptr; + + return std::unique_ptr<IPDBTable>(new DIATable(Item)); +} + +void DIAEnumTables::reset() { Enumerator->Reset(); } + +DIAEnumTables *DIAEnumTables::clone() const { + CComPtr<IDiaEnumTables> EnumeratorClone; + if (S_OK != Enumerator->Clone(&EnumeratorClone)) + return nullptr; + return new DIAEnumTables(EnumeratorClone); +} diff --git a/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp b/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp index 4c59d2f2a9d95..8e4b1f8aa8c94 100644 --- a/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp +++ b/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp @@ -439,6 +439,20 @@ void DIARawSymbol::getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) const { Symbol->get_dataBytes(DataSize, &DataSize, bytes.data()); } +std::string +DIARawSymbol::getUndecoratedNameEx(PDB_UndnameFlags Flags) const { + CComBSTR Result16; + if (S_OK != Symbol->get_undecoratedNameEx((DWORD)Flags, &Result16)) + return std::string(); + + const char *SrcBytes = reinterpret_cast<const char *>(Result16.m_str); + llvm::ArrayRef<char> SrcByteArray(SrcBytes, Result16.ByteLength()); + std::string Result8; + if (!llvm::convertUTF16ToUTF8String(SrcByteArray, Result8)) + return std::string(); + return Result8; +} + PDB_MemberAccess DIARawSymbol::getAccess() const { return PrivateGetDIAValue<DWORD, PDB_MemberAccess>(Symbol, &IDiaSymbol::get_access); diff --git a/lib/DebugInfo/PDB/DIA/DIASession.cpp b/lib/DebugInfo/PDB/DIA/DIASession.cpp index ef9390cda3127..b8aaebbf73803 100644 --- a/lib/DebugInfo/PDB/DIA/DIASession.cpp +++ b/lib/DebugInfo/PDB/DIA/DIASession.cpp @@ -11,6 +11,7 @@ #include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h" #include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h" #include "llvm/DebugInfo/PDB/DIA/DIAEnumSourceFiles.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumTables.h" #include "llvm/DebugInfo/PDB/DIA/DIAError.h" #include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" #include "llvm/DebugInfo/PDB/DIA/DIASourceFile.h" @@ -301,3 +302,11 @@ std::unique_ptr<IPDBEnumDataStreams> DIASession::getDebugStreams() const { return llvm::make_unique<DIAEnumDebugStreams>(DiaEnumerator); } + +std::unique_ptr<IPDBEnumTables> DIASession::getEnumTables() const { + CComPtr<IDiaEnumTables> DiaEnumerator; + if (S_OK != Session->getEnumTables(&DiaEnumerator)) + return nullptr; + + return llvm::make_unique<DIAEnumTables>(DiaEnumerator); +} diff --git a/lib/DebugInfo/PDB/DIA/DIATable.cpp b/lib/DebugInfo/PDB/DIA/DIATable.cpp new file mode 100644 index 0000000000000..5705c2370dc62 --- /dev/null +++ b/lib/DebugInfo/PDB/DIA/DIATable.cpp @@ -0,0 +1,62 @@ +//===- DIATable.cpp - DIA implementation of IPDBTable -----------*- 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/PDB/DIA/DIATable.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/ConvertUTF.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIATable::DIATable(CComPtr<IDiaTable> DiaTable) + : Table(DiaTable) {} + +uint32_t DIATable::getItemCount() const { + LONG Count = 0; + return (S_OK == Table->get_Count(&Count)) ? Count : 0; +} + +std::string DIATable::getName() const { + CComBSTR Name16; + if (S_OK != Table->get_name(&Name16)) + return std::string(); + + std::string Name8; + llvm::ArrayRef<char> Name16Bytes(reinterpret_cast<char *>(Name16.m_str), + Name16.ByteLength()); + if (!llvm::convertUTF16ToUTF8String(Name16Bytes, Name8)) + return std::string(); + return Name8; +} + +PDB_TableType DIATable::getTableType() const { + CComBSTR Name16; + if (S_OK != Table->get_name(&Name16)) + return PDB_TableType::TableInvalid; + + if (Name16 == DiaTable_Symbols) + return PDB_TableType::Symbols; + if (Name16 == DiaTable_SrcFiles) + return PDB_TableType::SourceFiles; + if (Name16 == DiaTable_Sections) + return PDB_TableType::SectionContribs; + if (Name16 == DiaTable_LineNums) + return PDB_TableType::LineNumbers; + if (Name16 == DiaTable_SegMap) + return PDB_TableType::Segments; + if (Name16 == DiaTable_InjSrc) + return PDB_TableType::InjectedSources; + if (Name16 == DiaTable_FrameData) + return PDB_TableType::FrameData; + if (Name16 == DiaTable_InputAssemblyFiles) + return PDB_TableType::InputAssemblyFiles; + if (Name16 == DiaTable_Dbg) + return PDB_TableType::Dbg; + return PDB_TableType::TableInvalid; +} diff --git a/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp b/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp index 897f78c510322..d765485bdb6d1 100644 --- a/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/Support/BinaryItemStream.h" @@ -26,16 +27,6 @@ using namespace llvm::codeview; using namespace llvm::msf; using namespace llvm::pdb; -namespace llvm { -template <> struct BinaryItemTraits<CVSymbol> { - static size_t length(const CVSymbol &Item) { return Item.RecordData.size(); } - - static ArrayRef<uint8_t> bytes(const CVSymbol &Item) { - return Item.RecordData; - } -}; -} - static uint32_t calculateDiSymbolStreamSize(uint32_t SymbolByteSize, uint32_t C13Size) { uint32_t Size = sizeof(uint32_t); // Signature @@ -98,14 +89,6 @@ uint32_t DbiModuleDescriptorBuilder::calculateSerializedLength() const { return alignTo(L + M + O, sizeof(uint32_t)); } -template <typename T> struct Foo { - explicit Foo(T &&Answer) : Answer(Answer) {} - - T Answer; -}; - -template <typename T> Foo<T> makeFoo(T &&t) { return Foo<T>(std::move(t)); } - void DbiModuleDescriptorBuilder::finalize() { Layout.SC.ModuleIndex = Layout.Mod; Layout.FileNameOffs = 0; // TODO: Fix this diff --git a/lib/DebugInfo/PDB/Native/DbiStream.cpp b/lib/DebugInfo/PDB/Native/DbiStream.cpp index 0eeac7e4c0847..04e6664c68dbb 100644 --- a/lib/DebugInfo/PDB/Native/DbiStream.cpp +++ b/lib/DebugInfo/PDB/Native/DbiStream.cpp @@ -12,7 +12,6 @@ #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" -#include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" diff --git a/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp b/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp index 25076e40fc98c..c96553ff9b168 100644 --- a/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -49,6 +49,10 @@ void DbiStreamBuilder::setSectionMap(ArrayRef<SecMapEntry> SecMap) { SectionMap = SecMap; } +void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index) { + GlobalsStreamIndex = Index; +} + void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index) { SymRecordStreamIndex = Index; } @@ -86,24 +90,9 @@ uint32_t DbiStreamBuilder::calculateSerializedLength() const { Expected<DbiModuleDescriptorBuilder &> DbiStreamBuilder::addModuleInfo(StringRef ModuleName) { uint32_t Index = ModiList.size(); - auto MIB = - llvm::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf); - auto M = MIB.get(); - auto Result = ModiMap.insert(std::make_pair(ModuleName, std::move(MIB))); - - if (!Result.second) - return make_error<RawError>(raw_error_code::duplicate_entry, - "The specified module already exists"); - ModiList.push_back(M); - return *M; -} - -Error DbiStreamBuilder::addModuleSourceFile(StringRef Module, StringRef File) { - auto ModIter = ModiMap.find(Module); - if (ModIter == ModiMap.end()) - return make_error<RawError>(raw_error_code::no_entry, - "The specified module was not found"); - return addModuleSourceFile(*ModIter->second, File); + ModiList.push_back( + llvm::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf)); + return *ModiList.back(); } Error DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder &Module, @@ -270,7 +259,7 @@ Error DbiStreamBuilder::finalize() { H->SymRecordStreamIndex = SymRecordStreamIndex; H->PublicSymbolStreamIndex = PublicsStreamIndex; H->MFCTypeServerIndex = kInvalidStreamIndex; - H->GlobalSymbolStreamIndex = kInvalidStreamIndex; + H->GlobalSymbolStreamIndex = GlobalsStreamIndex; Header = H; return Error::success(); @@ -307,19 +296,6 @@ static uint16_t toSecMapFlags(uint32_t Flags) { return Ret; } -void DbiStreamBuilder::addSectionContrib(DbiModuleDescriptorBuilder *ModuleDbi, - const object::coff_section *SecHdr) { - SectionContrib SC; - memset(&SC, 0, sizeof(SC)); - SC.ISect = (uint16_t)~0U; // This represents nil. - SC.Off = SecHdr->PointerToRawData; - SC.Size = SecHdr->SizeOfRawData; - SC.Characteristics = SecHdr->Characteristics; - // Use the module index in the module dbi stream or nil (-1). - SC.Imod = ModuleDbi ? ModuleDbi->getModuleIndex() : (uint16_t)~0U; - SectionContribs.emplace_back(SC); -} - // A utility function to create a Section Map for a given list of COFF sections. // // A Section Map seem to be a copy of a COFF section list in other format. diff --git a/lib/DebugInfo/PDB/Native/GSI.cpp b/lib/DebugInfo/PDB/Native/GSI.cpp deleted file mode 100644 index b219fe275f735..0000000000000 --- a/lib/DebugInfo/PDB/Native/GSI.cpp +++ /dev/null @@ -1,93 +0,0 @@ -//===- GSI.cpp - Common Functions for GlobalsStream and PublicsStream ----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "GSI.h" - -#include "llvm/DebugInfo/PDB/Native/RawError.h" -#include "llvm/DebugInfo/PDB/Native/RawTypes.h" -#include "llvm/Support/BinaryStreamArray.h" -#include "llvm/Support/BinaryStreamReader.h" - -#include "llvm/Support/Error.h" - -namespace llvm { -namespace pdb { - -static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) { - if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) - return make_error<RawError>( - raw_error_code::feature_unsupported, - "Encountered unsupported globals stream version."); - - return Error::success(); -} - -Error readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, - const GSIHashHeader *HashHdr, - BinaryStreamReader &Reader) { - if (auto EC = checkHashHdrVersion(HashHdr)) - return EC; - - // Before the actual hash buckets, there is a bitmap of length determined by - // IPHR_HASH. - ArrayRef<uint8_t> Bitmap; - size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); - uint32_t NumBitmapEntries = BitmapSizeInBits / 8; - if (auto EC = Reader.readBytes(Bitmap, NumBitmapEntries)) - return joinErrors(std::move(EC), - make_error<RawError>(raw_error_code::corrupt_file, - "Could not read a bitmap.")); - uint32_t NumBuckets = 0; - for (uint8_t B : Bitmap) - NumBuckets += countPopulation(B); - - // Hash buckets follow. - if (auto EC = Reader.readArray(HashBuckets, NumBuckets)) - return joinErrors(std::move(EC), - make_error<RawError>(raw_error_code::corrupt_file, - "Hash buckets corrupted.")); - - return Error::success(); -} - -Error readGSIHashHeader(const GSIHashHeader *&HashHdr, - BinaryStreamReader &Reader) { - if (Reader.readObject(HashHdr)) - return make_error<RawError>(raw_error_code::corrupt_file, - "Stream does not contain a GSIHashHeader."); - - if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) - return make_error<RawError>( - raw_error_code::feature_unsupported, - "GSIHashHeader signature (0xffffffff) not found."); - - return Error::success(); -} - -Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, - const GSIHashHeader *HashHdr, - BinaryStreamReader &Reader) { - if (auto EC = checkHashHdrVersion(HashHdr)) - return EC; - - // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. - // Verify that we can read them all. - if (HashHdr->HrSize % sizeof(PSHashRecord)) - return make_error<RawError>(raw_error_code::corrupt_file, - "Invalid HR array size."); - uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); - if (auto EC = Reader.readArray(HashRecords, NumHashRecords)) - return joinErrors(std::move(EC), - make_error<RawError>(raw_error_code::corrupt_file, - "Error reading hash records.")); - - return Error::success(); -} -} -} diff --git a/lib/DebugInfo/PDB/Native/GSI.h b/lib/DebugInfo/PDB/Native/GSI.h deleted file mode 100644 index 9e63bc83548fb..0000000000000 --- a/lib/DebugInfo/PDB/Native/GSI.h +++ /dev/null @@ -1,68 +0,0 @@ -//===- GSI.h - Common Declarations for GlobalsStream and PublicsStream ----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// The data structures defined in this file are based on the reference -// implementation which is available at -// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h -// -// When you are reading the reference source code, you'd find the -// information below useful. -// -// - ppdb1->m_fMinimalDbgInfo seems to be always true. -// - SMALLBUCKETS macro is defined. -// -// The reference doesn't compile, so I learned just by reading code. -// It's not guaranteed to be correct. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H -#define LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H - -#include "llvm/DebugInfo/PDB/Native/RawTypes.h" -#include "llvm/Support/BinaryStreamArray.h" - -#include "llvm/Support/Endian.h" -#include "llvm/Support/Error.h" - -namespace llvm { - -class BinaryStreamReader; - -namespace pdb { - -/// From https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.cpp -static const unsigned IPHR_HASH = 4096; - -/// Header of the hash tables found in the globals and publics sections. -/// Based on GSIHashHeader in -/// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h -struct GSIHashHeader { - enum : unsigned { - HdrSignature = ~0U, - HdrVersion = 0xeffe0000 + 19990810, - }; - support::ulittle32_t VerSignature; - support::ulittle32_t VerHdr; - support::ulittle32_t HrSize; - support::ulittle32_t NumBuckets; -}; - -Error readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, - const GSIHashHeader *HashHdr, - BinaryStreamReader &Reader); -Error readGSIHashHeader(const GSIHashHeader *&HashHdr, - BinaryStreamReader &Reader); -Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, - const GSIHashHeader *HashHdr, - BinaryStreamReader &Reader); -} -} - -#endif diff --git a/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp b/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp new file mode 100644 index 0000000000000..e84f25dfeefa4 --- /dev/null +++ b/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp @@ -0,0 +1,322 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- 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/PDB/Native/GSIStreamBuilder.h" + +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include <algorithm> +#include <vector> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::codeview; + +struct llvm::pdb::GSIHashStreamBuilder { + std::vector<CVSymbol> Records; + uint32_t StreamIndex; + std::vector<PSHashRecord> HashRecords; + std::array<support::ulittle32_t, (IPHR_HASH + 32) / 32> HashBitmap; + std::vector<support::ulittle32_t> HashBuckets; + + uint32_t calculateSerializedLength() const; + uint32_t calculateRecordByteSize() const; + Error commit(BinaryStreamWriter &Writer); + void finalizeBuckets(uint32_t RecordZeroOffset); + + template <typename T> void addSymbol(const T &Symbol, MSFBuilder &Msf) { + T Copy(Symbol); + Records.push_back(SymbolSerializer::writeOneSymbol(Copy, Msf.getAllocator(), + CodeViewContainer::Pdb)); + } + void addSymbol(const CVSymbol &Symbol) { Records.push_back(Symbol); } +}; + +uint32_t GSIHashStreamBuilder::calculateSerializedLength() const { + uint32_t Size = sizeof(GSIHashHeader); + Size += HashRecords.size() * sizeof(PSHashRecord); + Size += HashBitmap.size() * sizeof(uint32_t); + Size += HashBuckets.size() * sizeof(uint32_t); + return Size; +} + +uint32_t GSIHashStreamBuilder::calculateRecordByteSize() const { + uint32_t Size = 0; + for (const auto &Sym : Records) + Size += Sym.length(); + return Size; +} + +Error GSIHashStreamBuilder::commit(BinaryStreamWriter &Writer) { + GSIHashHeader Header; + Header.VerSignature = GSIHashHeader::HdrSignature; + Header.VerHdr = GSIHashHeader::HdrVersion; + Header.HrSize = HashRecords.size() * sizeof(PSHashRecord); + Header.NumBuckets = HashBitmap.size() * 4 + HashBuckets.size() * 4; + + if (auto EC = Writer.writeObject(Header)) + return EC; + + if (auto EC = Writer.writeArray(makeArrayRef(HashRecords))) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(HashBitmap))) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(HashBuckets))) + return EC; + return Error::success(); +} + +void GSIHashStreamBuilder::finalizeBuckets(uint32_t RecordZeroOffset) { + std::array<std::vector<PSHashRecord>, IPHR_HASH + 1> TmpBuckets; + uint32_t SymOffset = RecordZeroOffset; + for (const CVSymbol &Sym : Records) { + PSHashRecord HR; + // Add one when writing symbol offsets to disk. See GSI1::fixSymRecs. + HR.Off = SymOffset + 1; + HR.CRef = 1; // Always use a refcount of 1. + + // Hash the name to figure out which bucket this goes into. + StringRef Name = getSymbolName(Sym); + size_t BucketIdx = hashStringV1(Name) % IPHR_HASH; + TmpBuckets[BucketIdx].push_back(HR); // FIXME: Does order matter? + + SymOffset += Sym.length(); + } + + // Compute the three tables: the hash records in bucket and chain order, the + // bucket presence bitmap, and the bucket chain start offsets. + HashRecords.reserve(Records.size()); + for (ulittle32_t &Word : HashBitmap) + Word = 0; + for (size_t BucketIdx = 0; BucketIdx < IPHR_HASH + 1; ++BucketIdx) { + auto &Bucket = TmpBuckets[BucketIdx]; + if (Bucket.empty()) + continue; + HashBitmap[BucketIdx / 32] |= 1U << (BucketIdx % 32); + + // Calculate what the offset of the first hash record in the chain would + // be if it were inflated to contain 32-bit pointers. On a 32-bit system, + // each record would be 12 bytes. See HROffsetCalc in gsi.h. + const int SizeOfHROffsetCalc = 12; + ulittle32_t ChainStartOff = + ulittle32_t(HashRecords.size() * SizeOfHROffsetCalc); + HashBuckets.push_back(ChainStartOff); + for (const auto &HR : Bucket) + HashRecords.push_back(HR); + } +} + +GSIStreamBuilder::GSIStreamBuilder(msf::MSFBuilder &Msf) + : Msf(Msf), PSH(llvm::make_unique<GSIHashStreamBuilder>()), + GSH(llvm::make_unique<GSIHashStreamBuilder>()) {} + +GSIStreamBuilder::~GSIStreamBuilder() {} + +uint32_t GSIStreamBuilder::calculatePublicsHashStreamSize() const { + uint32_t Size = 0; + Size += sizeof(PublicsStreamHeader); + Size += PSH->calculateSerializedLength(); + Size += PSH->Records.size() * sizeof(uint32_t); // AddrMap + // FIXME: Add thunk map and section offsets for incremental linking. + + return Size; +} + +uint32_t GSIStreamBuilder::calculateGlobalsHashStreamSize() const { + return GSH->calculateSerializedLength(); +} + +Error GSIStreamBuilder::finalizeMsfLayout() { + // First we write public symbol records, then we write global symbol records. + uint32_t PSHZero = 0; + uint32_t GSHZero = PSH->calculateRecordByteSize(); + + PSH->finalizeBuckets(PSHZero); + GSH->finalizeBuckets(GSHZero); + + Expected<uint32_t> Idx = Msf.addStream(calculatePublicsHashStreamSize()); + if (!Idx) + return Idx.takeError(); + PSH->StreamIndex = *Idx; + Idx = Msf.addStream(calculateGlobalsHashStreamSize()); + if (!Idx) + return Idx.takeError(); + GSH->StreamIndex = *Idx; + + uint32_t RecordBytes = + GSH->calculateRecordByteSize() + PSH->calculateRecordByteSize(); + + Idx = Msf.addStream(RecordBytes); + if (!Idx) + return Idx.takeError(); + RecordStreamIdx = *Idx; + return Error::success(); +} + +static bool comparePubSymByAddrAndName( + const std::pair<const CVSymbol *, const PublicSym32 *> &LS, + const std::pair<const CVSymbol *, const PublicSym32 *> &RS) { + if (LS.second->Segment != RS.second->Segment) + return LS.second->Segment < RS.second->Segment; + if (LS.second->Offset != RS.second->Offset) + return LS.second->Offset < RS.second->Offset; + + return LS.second->Name < RS.second->Name; +} + +/// Compute the address map. The address map is an array of symbol offsets +/// sorted so that it can be binary searched by address. +static std::vector<ulittle32_t> computeAddrMap(ArrayRef<CVSymbol> Records) { + // Make a vector of pointers to the symbols so we can sort it by address. + // Also gather the symbol offsets while we're at it. + + std::vector<PublicSym32> DeserializedPublics; + std::vector<std::pair<const CVSymbol *, const PublicSym32 *>> PublicsByAddr; + std::vector<uint32_t> SymOffsets; + DeserializedPublics.reserve(Records.size()); + PublicsByAddr.reserve(Records.size()); + SymOffsets.reserve(Records.size()); + + uint32_t SymOffset = 0; + for (const CVSymbol &Sym : Records) { + assert(Sym.kind() == SymbolKind::S_PUB32); + DeserializedPublics.push_back( + cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(Sym))); + PublicsByAddr.emplace_back(&Sym, &DeserializedPublics.back()); + SymOffsets.push_back(SymOffset); + SymOffset += Sym.length(); + } + std::stable_sort(PublicsByAddr.begin(), PublicsByAddr.end(), + comparePubSymByAddrAndName); + + // Fill in the symbol offsets in the appropriate order. + std::vector<ulittle32_t> AddrMap; + AddrMap.reserve(Records.size()); + for (auto &Sym : PublicsByAddr) { + ptrdiff_t Idx = std::distance(Records.data(), Sym.first); + assert(Idx >= 0 && size_t(Idx) < Records.size()); + AddrMap.push_back(ulittle32_t(SymOffsets[Idx])); + } + return AddrMap; +} + +uint32_t GSIStreamBuilder::getPublicsStreamIndex() const { + return PSH->StreamIndex; +} + +uint32_t GSIStreamBuilder::getGlobalsStreamIndex() const { + return GSH->StreamIndex; +} + +void GSIStreamBuilder::addPublicSymbol(const PublicSym32 &Pub) { + PSH->addSymbol(Pub, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const ProcRefSym &Sym) { + GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const DataSym &Sym) { + GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const ConstantSym &Sym) { + GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const UDTSym &Sym) { + GSH->addSymbol(Sym, Msf); +} + +void GSIStreamBuilder::addGlobalSymbol(const codeview::CVSymbol &Sym) { + GSH->addSymbol(Sym); +} + +static Error writeRecords(BinaryStreamWriter &Writer, + ArrayRef<CVSymbol> Records) { + BinaryItemStream<CVSymbol> ItemStream(support::endianness::little); + ItemStream.setItems(Records); + BinaryStreamRef RecordsRef(ItemStream); + return Writer.writeStreamRef(RecordsRef); +} + +Error GSIStreamBuilder::commitSymbolRecordStream( + WritableBinaryStreamRef Stream) { + BinaryStreamWriter Writer(Stream); + + // Write public symbol records first, followed by global symbol records. This + // must match the order that we assume in finalizeMsfLayout when computing + // PSHZero and GSHZero. + if (auto EC = writeRecords(Writer, PSH->Records)) + return EC; + if (auto EC = writeRecords(Writer, GSH->Records)) + return EC; + + return Error::success(); +} + +Error GSIStreamBuilder::commitPublicsHashStream( + WritableBinaryStreamRef Stream) { + BinaryStreamWriter Writer(Stream); + PublicsStreamHeader Header; + + // FIXME: Fill these in. They are for incremental linking. + Header.NumThunks = 0; + Header.SizeOfThunk = 0; + Header.ISectThunkTable = 0; + Header.OffThunkTable = 0; + Header.NumSections = 0; + Header.SymHash = PSH->calculateSerializedLength(); + Header.AddrMap = PSH->Records.size() * 4; + if (auto EC = Writer.writeObject(Header)) + return EC; + + if (auto EC = PSH->commit(Writer)) + return EC; + + std::vector<ulittle32_t> AddrMap = computeAddrMap(PSH->Records); + if (auto EC = Writer.writeArray(makeArrayRef(AddrMap))) + return EC; + + return Error::success(); +} + +Error GSIStreamBuilder::commitGlobalsHashStream( + WritableBinaryStreamRef Stream) { + BinaryStreamWriter Writer(Stream); + return GSH->commit(Writer); +} + +Error GSIStreamBuilder::commit(const msf::MSFLayout &Layout, + WritableBinaryStreamRef Buffer) { + auto GS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, getGlobalsStreamIndex(), Msf.getAllocator()); + auto PS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, getPublicsStreamIndex(), Msf.getAllocator()); + auto PRS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, getRecordStreamIdx(), Msf.getAllocator()); + + if (auto EC = commitSymbolRecordStream(*PRS)) + return EC; + if (auto EC = commitGlobalsHashStream(*GS)) + return EC; + if (auto EC = commitPublicsHashStream(*PS)) + return EC; + return Error::success(); +} diff --git a/lib/DebugInfo/PDB/Native/GlobalsStream.cpp b/lib/DebugInfo/PDB/Native/GlobalsStream.cpp index a2ee0f047c58a..36076f436ad0a 100644 --- a/lib/DebugInfo/PDB/Native/GlobalsStream.cpp +++ b/lib/DebugInfo/PDB/Native/GlobalsStream.cpp @@ -1,4 +1,4 @@ -//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---- ----*- C++ -*-===// +//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -6,9 +6,21 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +// +// The on-disk structores used in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +// - ppdb1->m_fMinimalDbgInfo seems to be always true. +// - SMALLBUCKETS macro is defined. +// +//===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" -#include "GSI.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Error.h" #include <algorithm> @@ -24,19 +36,89 @@ GlobalsStream::~GlobalsStream() = default; Error GlobalsStream::reload() { BinaryStreamReader Reader(*Stream); + if (auto E = GlobalsTable.read(Reader)) + return E; + return Error::success(); +} - const GSIHashHeader *HashHdr; - if (auto EC = readGSIHashHeader(HashHdr, Reader)) - return EC; +static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) { + if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) + return make_error<RawError>( + raw_error_code::feature_unsupported, + "Encountered unsupported globals stream version."); - if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return Error::success(); +} + +static Error readGSIHashHeader(const GSIHashHeader *&HashHdr, + BinaryStreamReader &Reader) { + if (Reader.readObject(HashHdr)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Stream does not contain a GSIHashHeader."); + + if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) + return make_error<RawError>( + raw_error_code::feature_unsupported, + "GSIHashHeader signature (0xffffffff) not found."); + + return Error::success(); +} + +static Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, + const GSIHashHeader *HashHdr, + BinaryStreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) return EC; - if (auto EC = readGSIHashBuckets(HashBuckets, HashHdr, Reader)) + // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. + // Verify that we can read them all. + if (HashHdr->HrSize % sizeof(PSHashRecord)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid HR array size."); + uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); + if (auto EC = Reader.readArray(HashRecords, NumHashRecords)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Error reading hash records.")); + + return Error::success(); +} + +static Error +readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, + ArrayRef<uint8_t> &HashBitmap, const GSIHashHeader *HashHdr, + BinaryStreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) return EC; - NumBuckets = HashBuckets.size(); + + // Before the actual hash buckets, there is a bitmap of length determined by + // IPHR_HASH. + size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); + uint32_t NumBitmapEntries = BitmapSizeInBits / 8; + if (auto EC = Reader.readBytes(HashBitmap, NumBitmapEntries)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read a bitmap.")); + uint32_t NumBuckets = 0; + for (uint8_t B : HashBitmap) + NumBuckets += countPopulation(B); + + // Hash buckets follow. + if (auto EC = Reader.readArray(HashBuckets, NumBuckets)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Hash buckets corrupted.")); return Error::success(); } -Error GlobalsStream::commit() { return Error::success(); } +Error GSIHashTable::read(BinaryStreamReader &Reader) { + if (auto EC = readGSIHashHeader(HashHdr, Reader)) + return EC; + if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return EC; + if (HashHdr->HrSize > 0) + if (auto EC = readGSIHashBuckets(HashBuckets, HashBitmap, HashHdr, Reader)) + return EC; + return Error::success(); +} diff --git a/lib/DebugInfo/PDB/Native/InfoStream.cpp b/lib/DebugInfo/PDB/Native/InfoStream.cpp index 829879060c33c..17c9392a9dd56 100644 --- a/lib/DebugInfo/PDB/Native/InfoStream.cpp +++ b/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -10,12 +10,10 @@ #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamReader.h" -#include "llvm/Support/BinaryStreamWriter.h" using namespace llvm; using namespace llvm::codeview; diff --git a/lib/DebugInfo/PDB/Native/NativeBuiltinSymbol.cpp b/lib/DebugInfo/PDB/Native/NativeBuiltinSymbol.cpp index 60416f69e137c..4644ddcf24e39 100644 --- a/lib/DebugInfo/PDB/Native/NativeBuiltinSymbol.cpp +++ b/lib/DebugInfo/PDB/Native/NativeBuiltinSymbol.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/PDB/Native/NativeBuiltinSymbol.h" -#include "llvm/DebugInfo/PDB/Native/NativeSession.h" namespace llvm { namespace pdb { diff --git a/lib/DebugInfo/PDB/Native/NativeEnumSymbol.cpp b/lib/DebugInfo/PDB/Native/NativeEnumSymbol.cpp new file mode 100644 index 0000000000000..38d65917306a7 --- /dev/null +++ b/lib/DebugInfo/PDB/Native/NativeEnumSymbol.cpp @@ -0,0 +1,108 @@ +//===- NativeEnumSymbol.cpp - info about enum type --------------*- 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/PDB/Native/NativeEnumSymbol.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::pdb; + +NativeEnumSymbol::NativeEnumSymbol(NativeSession &Session, SymIndexId Id, + const codeview::CVType &CVT) + : NativeRawSymbol(Session, Id), CV(CVT), + Record(codeview::TypeRecordKind::Enum) { + assert(CV.kind() == codeview::TypeLeafKind::LF_ENUM); + cantFail(visitTypeRecord(CV, *this)); +} + +NativeEnumSymbol::~NativeEnumSymbol() {} + +std::unique_ptr<NativeRawSymbol> NativeEnumSymbol::clone() const { + return llvm::make_unique<NativeEnumSymbol>(Session, SymbolId, CV); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeEnumSymbol::findChildren(PDB_SymType Type) const { + switch (Type) { + case PDB_SymType::Data: { + // TODO(amccarth): Provide an actual implementation. + return nullptr; + } + default: + return nullptr; + } +} + +Error NativeEnumSymbol::visitKnownRecord(codeview::CVType &CVR, + codeview::EnumRecord &ER) { + Record = ER; + return Error::success(); +} + +Error NativeEnumSymbol::visitKnownMember(codeview::CVMemberRecord &CVM, + codeview::EnumeratorRecord &R) { + return Error::success(); +} + +PDB_SymType NativeEnumSymbol::getSymTag() const { return PDB_SymType::Enum; } + +uint32_t NativeEnumSymbol::getClassParentId() const { return 0xFFFFFFFF; } + +uint32_t NativeEnumSymbol::getUnmodifiedTypeId() const { return 0; } + +bool NativeEnumSymbol::hasConstructor() const { + return bool(Record.getOptions() & + codeview::ClassOptions::HasConstructorOrDestructor); +} + +bool NativeEnumSymbol::hasAssignmentOperator() const { + return bool(Record.getOptions() & + codeview::ClassOptions::HasOverloadedAssignmentOperator); +} + +bool NativeEnumSymbol::hasCastOperator() const { + return bool(Record.getOptions() & + codeview::ClassOptions::HasConversionOperator); +} + +uint64_t NativeEnumSymbol::getLength() const { + const auto Id = Session.findSymbolByTypeIndex(Record.getUnderlyingType()); + const auto UnderlyingType = + Session.getConcreteSymbolById<PDBSymbolTypeBuiltin>(Id); + return UnderlyingType ? UnderlyingType->getLength() : 0; +} + +std::string NativeEnumSymbol::getName() const { return Record.getName(); } + +bool NativeEnumSymbol::isNested() const { + return bool(Record.getOptions() & codeview::ClassOptions::Nested); +} + +bool NativeEnumSymbol::hasOverloadedOperator() const { + return bool(Record.getOptions() & + codeview::ClassOptions::HasOverloadedOperator); +} + +bool NativeEnumSymbol::isPacked() const { + return bool(Record.getOptions() & codeview::ClassOptions::Packed); +} + +bool NativeEnumSymbol::isScoped() const { + return bool(Record.getOptions() & codeview::ClassOptions::Scoped); +} + +uint32_t NativeEnumSymbol::getTypeId() const { + return Session.findSymbolByTypeIndex(Record.getUnderlyingType()); +} diff --git a/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp b/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp new file mode 100644 index 0000000000000..36a68a1c62de4 --- /dev/null +++ b/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp @@ -0,0 +1,59 @@ +//==- NativeEnumTypes.cpp - Native Type Enumerator impl ----------*- 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/PDB/Native/NativeEnumTypes.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +namespace llvm { +namespace pdb { + +NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession, + codeview::LazyRandomTypeCollection &Types, + codeview::TypeLeafKind Kind) + : Matches(), Index(0), Session(PDBSession), Kind(Kind) { + for (auto Index = Types.getFirst(); Index; + Index = Types.getNext(Index.getValue())) { + if (Types.getType(Index.getValue()).kind() == Kind) + Matches.push_back(Index.getValue()); + } +} + +NativeEnumTypes::NativeEnumTypes( + NativeSession &PDBSession, const std::vector<codeview::TypeIndex> &Matches, + codeview::TypeLeafKind Kind) + : Matches(Matches), Index(0), Session(PDBSession), Kind(Kind) {} + +uint32_t NativeEnumTypes::getChildCount() const { + return static_cast<uint32_t>(Matches.size()); +} + +std::unique_ptr<PDBSymbol> +NativeEnumTypes::getChildAtIndex(uint32_t Index) const { + if (Index < Matches.size()) + return Session.createEnumSymbol(Matches[Index]); + return nullptr; +} + +std::unique_ptr<PDBSymbol> NativeEnumTypes::getNext() { + return getChildAtIndex(Index++); +} + +void NativeEnumTypes::reset() { Index = 0; } + +NativeEnumTypes *NativeEnumTypes::clone() const { + return new NativeEnumTypes(Session, Matches, Kind); +} + +} // namespace pdb +} // namespace llvm diff --git a/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp b/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp index 3241000b06db0..e8b06065fc607 100644 --- a/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp +++ b/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp @@ -38,6 +38,8 @@ NativeExeSymbol::findChildren(PDB_SymType Type) const { consumeError(Dbi.takeError()); break; } + case PDB_SymType::Enum: + return Session.createTypeEnumerator(codeview::LF_ENUM); default: break; } diff --git a/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp b/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp index df3f418052a9d..d23ee0a091962 100644 --- a/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp +++ b/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp @@ -286,6 +286,11 @@ std::string NativeRawSymbol::getUndecoratedName() const { return {}; } +std::string NativeRawSymbol::getUndecoratedNameEx( + PDB_UndnameFlags Flags) const { + return {}; +} + uint32_t NativeRawSymbol::getUnmodifiedTypeId() const { return 0; } diff --git a/lib/DebugInfo/PDB/Native/NativeSession.cpp b/lib/DebugInfo/PDB/Native/NativeSession.cpp index 76de0d8f9e7ef..b01c2b54796c6 100644 --- a/lib/DebugInfo/PDB/Native/NativeSession.cpp +++ b/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -16,11 +16,15 @@ #include "llvm/DebugInfo/PDB/IPDBSourceFile.h" #include "llvm/DebugInfo/PDB/Native/NativeBuiltinSymbol.h" #include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" #include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Error.h" @@ -28,6 +32,7 @@ #include "llvm/Support/MemoryBuffer.h" #include <algorithm> +#include <cassert> #include <memory> #include <utility> @@ -63,15 +68,9 @@ NativeSession::NativeSession(std::unique_ptr<PDBFile> PdbFile, NativeSession::~NativeSession() = default; -Error NativeSession::createFromPdb(StringRef Path, +Error NativeSession::createFromPdb(std::unique_ptr<MemoryBuffer> Buffer, std::unique_ptr<IPDBSession> &Session) { - ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = - MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, - /*RequiresNullTerminator=*/false); - if (!ErrorOrBuffer) - return make_error<GenericError>(generic_error_code::invalid_path); - - std::unique_ptr<MemoryBuffer> Buffer = std::move(*ErrorOrBuffer); + StringRef Path = Buffer->getBufferIdentifier(); auto Stream = llvm::make_unique<MemoryBufferByteStream>( std::move(Buffer), llvm::support::little); @@ -102,6 +101,25 @@ NativeSession::createCompilandSymbol(DbiModuleDescriptor MI) { *this, std::unique_ptr<IPDBRawSymbol>(SymbolCache[Id]->clone())); } +std::unique_ptr<PDBSymbolTypeEnum> +NativeSession::createEnumSymbol(codeview::TypeIndex Index) { + const auto Id = findSymbolByTypeIndex(Index); + return llvm::make_unique<PDBSymbolTypeEnum>( + *this, std::unique_ptr<IPDBRawSymbol>(SymbolCache[Id]->clone())); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeSession::createTypeEnumerator(codeview::TypeLeafKind Kind) { + auto Tpi = Pdb->getPDBTpiStream(); + if (!Tpi) { + consumeError(Tpi.takeError()); + return nullptr; + } + auto &Types = Tpi->typeCollection(); + return std::unique_ptr<IPDBEnumSymbols>( + new NativeEnumTypes(*this, Types, codeview::LF_ENUM)); +} + SymIndexId NativeSession::findSymbolByTypeIndex(codeview::TypeIndex Index) { // First see if it's already in our cache. const auto Entry = TypeIndexToSymbolId.find(Index); @@ -129,9 +147,20 @@ SymIndexId NativeSession::findSymbolByTypeIndex(codeview::TypeIndex Index) { return Id; } - // TODO: Look up PDB type by type index - - return 0; + // We need to instantiate and cache the desired type symbol. + auto Tpi = Pdb->getPDBTpiStream(); + if (!Tpi) { + consumeError(Tpi.takeError()); + return 0; + } + auto &Types = Tpi->typeCollection(); + const auto &I = Types.getType(Index); + const auto Id = static_cast<SymIndexId>(SymbolCache.size()); + // TODO(amccarth): Make this handle all types, not just LF_ENUMs. + assert(I.kind() == codeview::LF_ENUM); + SymbolCache.emplace_back(llvm::make_unique<NativeEnumSymbol>(*this, Id, I)); + TypeIndexToSymbolId[Index] = Id; + return Id; } uint64_t NativeSession::getLoadAddress() const { return 0; } @@ -216,3 +245,7 @@ NativeSession::getSourceFileById(uint32_t FileId) const { std::unique_ptr<IPDBEnumDataStreams> NativeSession::getDebugStreams() const { return nullptr; } + +std::unique_ptr<IPDBEnumTables> NativeSession::getEnumTables() const { + return nullptr; +} diff --git a/lib/DebugInfo/PDB/Native/PDBFile.cpp b/lib/DebugInfo/PDB/Native/PDBFile.cpp index 0b6492efc70f3..15b31d821b1c2 100644 --- a/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ b/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -85,6 +85,11 @@ uint32_t PDBFile::getNumStreams() const { return ContainerLayout.StreamSizes.size(); } +uint32_t PDBFile::getMaxStreamSize() const { + return *std::max_element(ContainerLayout.StreamSizes.begin(), + ContainerLayout.StreamSizes.end()); +} + uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const { return ContainerLayout.StreamSizes[StreamIndex]; } @@ -150,8 +155,7 @@ Error PDBFile::parseFileHeaders() { MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator); BinaryStreamReader FpmReader(*FpmStream); ArrayRef<uint8_t> FpmBytes; - if (auto EC = FpmReader.readBytes(FpmBytes, - msf::getFullFpmByteSize(ContainerLayout))) + if (auto EC = FpmReader.readBytes(FpmBytes, FpmReader.bytesRemaining())) return EC; uint32_t BlocksRemaining = getBlockCount(); uint32_t BI = 0; @@ -230,6 +234,13 @@ ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const { return ContainerLayout.DirectoryBlocks; } +std::unique_ptr<MappedBlockStream> PDBFile::createIndexedStream(uint16_t SN) { + if (SN == kInvalidStreamIndex) + return nullptr; + return MappedBlockStream::createIndexedStream(ContainerLayout, *Buffer, SN, + Allocator); +} + MSFStreamLayout PDBFile::getStreamLayout(uint32_t StreamIdx) const { MSFStreamLayout Result; auto Blocks = getStreamBlockList(StreamIdx); @@ -238,6 +249,10 @@ MSFStreamLayout PDBFile::getStreamLayout(uint32_t StreamIdx) const { return Result; } +msf::MSFStreamLayout PDBFile::getFpmStreamLayout() const { + return msf::getFpmStreamLayout(ContainerLayout); +} + Expected<GlobalsStream &> PDBFile::getPDBGlobalsStream() { if (!Globals) { auto DbiS = getPDBDbiStream(); @@ -297,6 +312,9 @@ Expected<TpiStream &> PDBFile::getPDBTpiStream() { Expected<TpiStream &> PDBFile::getPDBIpiStream() { if (!Ipi) { + if (!hasPDBIpiStream()) + return make_error<RawError>(raw_error_code::no_stream); + auto IpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamIPI); if (!IpiS) return IpiS.takeError(); @@ -318,8 +336,7 @@ Expected<PublicsStream &> PDBFile::getPDBPublicsStream() { ContainerLayout, *Buffer, DbiS->getPublicSymbolStreamIndex()); if (!PublicS) return PublicS.takeError(); - auto TempPublics = - llvm::make_unique<PublicsStream>(*this, std::move(*PublicS)); + auto TempPublics = llvm::make_unique<PublicsStream>(std::move(*PublicS)); if (auto EC = TempPublics->reload()) return std::move(EC); Publics = std::move(TempPublics); @@ -393,9 +410,18 @@ bool PDBFile::hasPDBGlobalsStream() { return DbiS->getGlobalSymbolStreamIndex() < getNumStreams(); } -bool PDBFile::hasPDBInfoStream() { return StreamPDB < getNumStreams(); } +bool PDBFile::hasPDBInfoStream() const { return StreamPDB < getNumStreams(); } + +bool PDBFile::hasPDBIpiStream() const { + if (!hasPDBInfoStream()) + return false; + + if (StreamIPI >= getNumStreams()) + return false; -bool PDBFile::hasPDBIpiStream() const { return StreamIPI < getNumStreams(); } + auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream()); + return InfoStream.containsIdStream(); +} bool PDBFile::hasPDBPublicsStream() { auto DbiS = getPDBDbiStream(); diff --git a/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp b/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp index 9f35fd73629cd..dee27c621face 100644 --- a/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -15,10 +15,10 @@ #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" -#include "llvm/DebugInfo/PDB/Native/PublicsStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" @@ -74,10 +74,10 @@ PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() { return Strings; } -PublicsStreamBuilder &PDBFileBuilder::getPublicsBuilder() { - if (!Publics) - Publics = llvm::make_unique<PublicsStreamBuilder>(*Msf); - return *Publics; +GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() { + if (!Gsi) + Gsi = llvm::make_unique<GSIStreamBuilder>(*Msf); + return *Gsi; } Error PDBFileBuilder::addNamedStream(StringRef Name, uint32_t Size) { @@ -122,12 +122,13 @@ Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() { if (auto EC = Ipi->finalizeMsfLayout()) return std::move(EC); } - if (Publics) { - if (auto EC = Publics->finalizeMsfLayout()) + if (Gsi) { + if (auto EC = Gsi->finalizeMsfLayout()) return std::move(EC); if (Dbi) { - Dbi->setPublicsStreamIndex(Publics->getStreamIndex()); - Dbi->setSymbolRecordStreamIndex(Publics->getRecordStreamIdx()); + Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex()); + Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex()); + Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIdx()); } } @@ -141,6 +142,31 @@ Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const { return SN; } +void PDBFileBuilder::commitFpm(WritableBinaryStream &MsfBuffer, + const MSFLayout &Layout) { + auto FpmStream = + WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator); + + // We only need to create the alt fpm stream so that it gets initialized. + WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator, + true); + + uint32_t BI = 0; + BinaryStreamWriter FpmWriter(*FpmStream); + while (BI < Layout.SB->NumBlocks) { + uint8_t ThisByte = 0; + for (uint32_t I = 0; I < 8; ++I) { + bool IsFree = + (BI < Layout.SB->NumBlocks) ? Layout.FreePageMap.test(BI) : true; + uint8_t Mask = uint8_t(IsFree) << I; + ThisByte |= Mask; + ++BI; + } + cantFail(FpmWriter.writeObject(ThisByte)); + } + assert(FpmWriter.bytesRemaining() == 0); +} + Error PDBFileBuilder::commit(StringRef Filename) { assert(!Filename.empty()); auto ExpectedLayout = finalizeMsfLayout(); @@ -150,15 +176,17 @@ Error PDBFileBuilder::commit(StringRef Filename) { uint64_t Filesize = Layout.SB->BlockSize * Layout.SB->NumBlocks; auto OutFileOrError = FileOutputBuffer::create(Filename, Filesize); - if (OutFileOrError.getError()) - return llvm::make_error<pdb::GenericError>(generic_error_code::invalid_path, - Filename); + if (auto E = OutFileOrError.takeError()) + return E; FileBufferByteStream Buffer(std::move(*OutFileOrError), llvm::support::little); BinaryStreamWriter Writer(Buffer); if (auto EC = Writer.writeObject(*Layout.SB)) return EC; + + commitFpm(Buffer, Layout); + uint32_t BlockMapOffset = msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize); Writer.setOffset(BlockMapOffset); @@ -209,11 +237,8 @@ Error PDBFileBuilder::commit(StringRef Filename) { return EC; } - if (Publics) { - auto PS = WritableMappedBlockStream::createIndexedStream( - Layout, Buffer, Publics->getStreamIndex(), Allocator); - BinaryStreamWriter PSWriter(*PS); - if (auto EC = Publics->commit(PSWriter)) + if (Gsi) { + if (auto EC = Gsi->commit(Layout, Buffer)) return EC; } diff --git a/lib/DebugInfo/PDB/Native/PDBStringTable.cpp b/lib/DebugInfo/PDB/Native/PDBStringTable.cpp index acd45f7a62192..f1c10357132be 100644 --- a/lib/DebugInfo/PDB/Native/PDBStringTable.cpp +++ b/lib/DebugInfo/PDB/Native/PDBStringTable.cpp @@ -10,7 +10,6 @@ #include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/Hash.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" diff --git a/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp b/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp index 90acfadd311ff..ece3e00b1a87e 100644 --- a/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp @@ -10,9 +10,7 @@ #include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/Hash.h" -#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" diff --git a/lib/DebugInfo/PDB/Native/PublicsStream.cpp b/lib/DebugInfo/PDB/Native/PublicsStream.cpp index 9c3e654f808ba..f6466eb80464d 100644 --- a/lib/DebugInfo/PDB/Native/PublicsStream.cpp +++ b/lib/DebugInfo/PDB/Native/PublicsStream.cpp @@ -23,13 +23,10 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" -#include "GSI.h" #include "llvm/ADT/iterator_range.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" -#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -41,14 +38,18 @@ using namespace llvm::msf; using namespace llvm::support; using namespace llvm::pdb; -PublicsStream::PublicsStream(PDBFile &File, - std::unique_ptr<MappedBlockStream> Stream) - : Pdb(File), Stream(std::move(Stream)) {} +PublicsStream::PublicsStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} PublicsStream::~PublicsStream() = default; uint32_t PublicsStream::getSymHash() const { return Header->SymHash; } -uint32_t PublicsStream::getAddrMap() const { return Header->AddrMap; } +uint16_t PublicsStream::getThunkTableSection() const { + return Header->ISectThunkTable; +} +uint32_t PublicsStream::getThunkTableOffset() const { + return Header->OffThunkTable; +} // Publics stream contains fixed-size headers and a serialized hash table. // This implementation is not complete yet. It reads till the end of the @@ -64,20 +65,14 @@ Error PublicsStream::reload() { return make_error<RawError>(raw_error_code::corrupt_file, "Publics Stream does not contain a header."); - // Read PSGSIHDR and GSIHashHdr structs. + // Read PSGSIHDR struct. if (Reader.readObject(Header)) return make_error<RawError>(raw_error_code::corrupt_file, "Publics Stream does not contain a header."); - if (auto EC = readGSIHashHeader(HashHdr, Reader)) - return EC; - - if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) - return EC; - - if (auto EC = readGSIHashBuckets(HashBuckets, HashHdr, Reader)) - return EC; - NumBuckets = HashBuckets.size(); + // Read the hash table. + if (auto E = PublicsTable.read(Reader)) + return E; // Something called "address map" follows. uint32_t NumAddressMapEntries = Header->AddrMap / sizeof(uint32_t); @@ -105,26 +100,3 @@ Error PublicsStream::reload() { "Corrupted publics stream."); return Error::success(); } - -iterator_range<codeview::CVSymbolArray::Iterator> -PublicsStream::getSymbols(bool *HadError) const { - auto SymbolS = Pdb.getPDBSymbolStream(); - if (SymbolS.takeError()) { - codeview::CVSymbolArray::Iterator Iter; - return make_range(Iter, Iter); - } - SymbolStream &SS = SymbolS.get(); - - return SS.getSymbols(HadError); -} - -Expected<const codeview::CVSymbolArray &> -PublicsStream::getSymbolArray() const { - auto SymbolS = Pdb.getPDBSymbolStream(); - if (!SymbolS) - return SymbolS.takeError(); - - return SymbolS->getSymbolArray(); -} - -Error PublicsStream::commit() { return Error::success(); } diff --git a/lib/DebugInfo/PDB/Native/PublicsStreamBuilder.cpp b/lib/DebugInfo/PDB/Native/PublicsStreamBuilder.cpp deleted file mode 100644 index 28c4a8fc35d92..0000000000000 --- a/lib/DebugInfo/PDB/Native/PublicsStreamBuilder.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- 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/PDB/Native/PublicsStreamBuilder.h" - -#include "llvm/DebugInfo/MSF/MSFBuilder.h" -#include "llvm/DebugInfo/MSF/MSFCommon.h" -#include "llvm/DebugInfo/MSF/MappedBlockStream.h" - -#include "GSI.h" - -using namespace llvm; -using namespace llvm::msf; -using namespace llvm::pdb; - -PublicsStreamBuilder::PublicsStreamBuilder(msf::MSFBuilder &Msf) : Msf(Msf) {} - -PublicsStreamBuilder::~PublicsStreamBuilder() {} - -uint32_t PublicsStreamBuilder::calculateSerializedLength() const { - uint32_t Size = 0; - Size += sizeof(PublicsStreamHeader); - Size += sizeof(GSIHashHeader); - Size += HashRecords.size() * sizeof(PSHashRecord); - size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); - uint32_t NumBitmapEntries = BitmapSizeInBits / 8; - Size += NumBitmapEntries; - - // FIXME: Account for hash buckets. For now since we we write a zero-bitmap - // indicating that no hash buckets are valid, we also write zero byets of hash - // bucket data. - Size += 0; - return Size; -} - -Error PublicsStreamBuilder::finalizeMsfLayout() { - Expected<uint32_t> Idx = Msf.addStream(calculateSerializedLength()); - if (!Idx) - return Idx.takeError(); - StreamIdx = *Idx; - - Expected<uint32_t> RecordIdx = Msf.addStream(0); - if (!RecordIdx) - return RecordIdx.takeError(); - RecordStreamIdx = *RecordIdx; - return Error::success(); -} - -Error PublicsStreamBuilder::commit(BinaryStreamWriter &PublicsWriter) { - PublicsStreamHeader PSH; - GSIHashHeader GSH; - - // FIXME: Figure out what to put for these values. - PSH.AddrMap = 0; - PSH.ISectThunkTable = 0; - PSH.NumSections = 0; - PSH.NumThunks = 0; - PSH.OffThunkTable = 0; - PSH.SizeOfThunk = 0; - PSH.SymHash = 0; - - GSH.VerSignature = GSIHashHeader::HdrSignature; - GSH.VerHdr = GSIHashHeader::HdrVersion; - GSH.HrSize = 0; - GSH.NumBuckets = 0; - - if (auto EC = PublicsWriter.writeObject(PSH)) - return EC; - if (auto EC = PublicsWriter.writeObject(GSH)) - return EC; - if (auto EC = PublicsWriter.writeArray(makeArrayRef(HashRecords))) - return EC; - - size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); - uint32_t NumBitmapEntries = BitmapSizeInBits / 8; - std::vector<uint8_t> BitmapData(NumBitmapEntries); - // FIXME: Build an actual bitmap - if (auto EC = PublicsWriter.writeBytes(makeArrayRef(BitmapData))) - return EC; - - // FIXME: Write actual hash buckets. - return Error::success(); -} diff --git a/lib/DebugInfo/PDB/Native/SymbolStream.cpp b/lib/DebugInfo/PDB/Native/SymbolStream.cpp index 9e9ebd11495b2..2d8d04ceca4d5 100644 --- a/lib/DebugInfo/PDB/Native/SymbolStream.cpp +++ b/lib/DebugInfo/PDB/Native/SymbolStream.cpp @@ -10,11 +10,8 @@ #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/PDB/Native/PDBFile.h" -#include "llvm/DebugInfo/PDB/Native/RawConstants.h" -#include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Endian.h" @@ -43,3 +40,7 @@ SymbolStream::getSymbols(bool *HadError) const { } Error SymbolStream::commit() { return Error::success(); } + +codeview::CVSymbol SymbolStream::readRecord(uint32_t Offset) const { + return *SymbolRecords.at(Offset); +} diff --git a/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp b/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp index 9e943c7f114d5..8dd30018028e7 100644 --- a/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -17,7 +17,6 @@ #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" -#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryStreamArray.h" diff --git a/lib/DebugInfo/PDB/PDB.cpp b/lib/DebugInfo/PDB/PDB.cpp index 501d4f5985b7d..40f5ae9ba8455 100644 --- a/lib/DebugInfo/PDB/PDB.cpp +++ b/lib/DebugInfo/PDB/PDB.cpp @@ -16,6 +16,7 @@ #endif #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" using namespace llvm; using namespace llvm::pdb; @@ -23,8 +24,15 @@ using namespace llvm::pdb; Error llvm::pdb::loadDataForPDB(PDB_ReaderType Type, StringRef Path, std::unique_ptr<IPDBSession> &Session) { // Create the correct concrete instance type based on the value of Type. - if (Type == PDB_ReaderType::Native) - return NativeSession::createFromPdb(Path, Session); + if (Type == PDB_ReaderType::Native) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return make_error<GenericError>(generic_error_code::invalid_path, Path); + + return NativeSession::createFromPdb(std::move(*ErrorOrBuffer), Session); + } #if LLVM_ENABLE_DIA_SDK return DIASession::createFromPdb(Path, Session); diff --git a/lib/DebugInfo/PDB/PDBExtras.cpp b/lib/DebugInfo/PDB/PDBExtras.cpp index c291185bc67a6..ee752cda346e7 100644 --- a/lib/DebugInfo/PDB/PDBExtras.cpp +++ b/lib/DebugInfo/PDB/PDBExtras.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/PDB/PDBExtras.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/DebugInfo/CodeView/Formatters.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -94,56 +93,11 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_DataKind &Data) { raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const codeview::RegisterId &Reg) { switch (Reg) { - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, AL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, AH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, AX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, SP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, SI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EAX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ECX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EDX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EBX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ESP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EBP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ESI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EDI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ES, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, SS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, FS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, GS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, IP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RAX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RBX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RCX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RDX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RSI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RDI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RBP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RSP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R8, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R9, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R10, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R11, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R12, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R13, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R14, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R15, OS) - default: - OS << static_cast<int>(Reg); +#define CV_REGISTER(name, val) case codeview::RegisterId::name: OS << #name; return OS; +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER } + OS << static_cast<int>(Reg); return OS; } @@ -208,6 +162,7 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_Lang &Lang) { CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, JScript, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, MSIL, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, HLSL, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, D, OS) } return OS; } diff --git a/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp b/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp index 541fcda451770..b2b03fbe167b9 100644 --- a/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp +++ b/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp @@ -15,6 +15,7 @@ #include "llvm/DebugInfo/PDB/IPDBLineNumber.h" #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/IPDBTable.h" using namespace llvm; using namespace llvm::pdb; @@ -26,3 +27,5 @@ IPDBDataStream::~IPDBDataStream() = default; IPDBRawSymbol::~IPDBRawSymbol() = default; IPDBLineNumber::~IPDBLineNumber() = default; + +IPDBTable::~IPDBTable() = default; diff --git a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp index 5a5cb4c1b5cac..c8c44d97e2f79 100644 --- a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp @@ -15,7 +15,6 @@ #include "llvm/DebugInfo/PDB/PDBSymDumper.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" -#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" #include <unordered_set> diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp index a8054a42d8660..ba40f65ef40fd 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" -#include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBSymDumper.h" #include <utility> diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp index 2addea072c885..f9c3067c20bfb 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp @@ -9,10 +9,8 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" -#include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBSymDumper.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" -#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include <utility> diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp index 15dc153521656..715ae15e1a7a9 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp @@ -17,7 +17,6 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h" -#include "llvm/DebugInfo/PDB/UDTLayout.h" #include <utility> diff --git a/lib/DebugInfo/Symbolize/Symbolize.cpp b/lib/DebugInfo/Symbolize/Symbolize.cpp index 19711ca58c6f0..e997ef5b6069c 100644 --- a/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -22,7 +22,6 @@ #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBContext.h" #include "llvm/Object/COFF.h" -#include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Support/Casting.h" @@ -53,10 +52,11 @@ namespace llvm { namespace symbolize { -Expected<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, - uint64_t ModuleOffset) { +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, + uint64_t ModuleOffset, StringRef DWPName) { SymbolizableModule *Info; - if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) + if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName, DWPName)) Info = InfoOrErr.get(); else return InfoOrErr.takeError(); @@ -80,9 +80,9 @@ Expected<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, - uint64_t ModuleOffset) { + uint64_t ModuleOffset, StringRef DWPName) { SymbolizableModule *Info; - if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) + if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName, DWPName)) Info = InfoOrErr.get(); else return InfoOrErr.takeError(); @@ -364,7 +364,8 @@ LLVMSymbolizer::getOrCreateObject(const std::string &Path, } Expected<SymbolizableModule *> -LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { +LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName, + StringRef DWPName) { const auto &I = Modules.find(ModuleName); if (I != Modules.end()) { return I->second.get(); @@ -409,7 +410,8 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { } } if (!Context) - Context.reset(new DWARFContextInMemory(*Objects.second)); + Context = DWARFContext::create(*Objects.second, nullptr, + DWARFContext::defaultErrorHandler, DWPName); assert(Context); auto InfoOrErr = SymbolizableObjectFile::create(Objects.first, std::move(Context)); |