diff options
Diffstat (limited to 'llvm/lib/DebugInfo')
184 files changed, 34985 insertions, 0 deletions
diff --git a/llvm/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp b/llvm/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp new file mode 100644 index 000000000000..86a6f9eebfa2 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/AppendingTypeTableBuilder.cpp @@ -0,0 +1,95 @@ +//===- AppendingTypeTableBuilder.cpp --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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){ + return CVType(SeenRecords[Index.toArrayIndex()]); +} + +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/llvm/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp b/llvm/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp new file mode 100644 index 000000000000..48b9b0496ffe --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp @@ -0,0 +1,82 @@ +//===- CVSymbolVisitor.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" + +using namespace llvm; +using namespace llvm::codeview; + +CVSymbolVisitor::CVSymbolVisitor(SymbolVisitorCallbacks &Callbacks) + : Callbacks(Callbacks) {} + +template <typename T> +static Error visitKnownRecord(CVSymbol &Record, + SymbolVisitorCallbacks &Callbacks) { + SymbolRecordKind RK = static_cast<SymbolRecordKind>(Record.kind()); + T KnownRecord(RK); + if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord)) + return EC; + return Error::success(); +} + +static Error finishVisitation(CVSymbol &Record, + SymbolVisitorCallbacks &Callbacks) { + switch (Record.kind()) { + default: + if (auto EC = Callbacks.visitUnknownSymbol(Record)) + return EC; + break; +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (auto EC = visitKnownRecord<Name>(Record, Callbacks)) \ + return EC; \ + break; \ + } +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + SYMBOL_RECORD(EnumVal, EnumVal, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + } + + if (auto EC = Callbacks.visitSymbolEnd(Record)) + return EC; + + return Error::success(); +} + +Error CVSymbolVisitor::visitSymbolRecord(CVSymbol &Record) { + if (auto EC = Callbacks.visitSymbolBegin(Record)) + return EC; + return finishVisitation(Record, Callbacks); +} + +Error CVSymbolVisitor::visitSymbolRecord(CVSymbol &Record, uint32_t Offset) { + if (auto EC = Callbacks.visitSymbolBegin(Record, Offset)) + return EC; + return finishVisitation(Record, Callbacks); +} + +Error CVSymbolVisitor::visitSymbolStream(const CVSymbolArray &Symbols) { + for (auto I : Symbols) { + if (auto EC = visitSymbolRecord(I)) + return EC; + } + return Error::success(); +} + +Error CVSymbolVisitor::visitSymbolStream(const CVSymbolArray &Symbols, + uint32_t InitialOffset) { + for (auto I : Symbols) { + if (auto EC = visitSymbolRecord(I, InitialOffset + Symbols.skew())) + return EC; + InitialOffset += I.length(); + } + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp new file mode 100644 index 000000000000..dd6f75f97a4a --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -0,0 +1,274 @@ +//===- CVTypeVisitor.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/TypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamReader.h" + +using namespace llvm; +using namespace llvm::codeview; + + +template <typename T> +static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) { + TypeRecordKind RK = static_cast<TypeRecordKind>(Record.kind()); + T KnownRecord(RK); + if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord)) + return EC; + return Error::success(); +} + +template <typename T> +static Error visitKnownMember(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks) { + TypeRecordKind RK = static_cast<TypeRecordKind>(Record.Kind); + T KnownRecord(RK); + if (auto EC = Callbacks.visitKnownMember(Record, KnownRecord)) + return EC; + return Error::success(); +} + +static Error visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks) { + if (auto EC = Callbacks.visitMemberBegin(Record)) + return EC; + + switch (Record.Kind) { + default: + if (auto EC = Callbacks.visitUnknownMember(Record)) + return EC; + break; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (auto EC = visitKnownMember<Name##Record>(Record, Callbacks)) \ + return EC; \ + break; \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + MEMBER_RECORD(EnumVal, EnumVal, AliasName) +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + if (auto EC = Callbacks.visitMemberEnd(Record)) + return EC; + + return Error::success(); +} + +namespace { + +class CVTypeVisitor { +public: + explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks); + + Error visitTypeRecord(CVType &Record, TypeIndex Index); + Error visitTypeRecord(CVType &Record); + + /// Visits the type records in Data. Sets the error flag on parse failures. + Error visitTypeStream(const CVTypeArray &Types); + Error visitTypeStream(CVTypeRange Types); + Error visitTypeStream(TypeCollection &Types); + + Error visitMemberRecord(CVMemberRecord Record); + Error visitFieldListMemberStream(BinaryStreamReader &Stream); + +private: + Error finishVisitation(CVType &Record); + + /// The interface to the class that gets notified of each visitation. + TypeVisitorCallbacks &Callbacks; +}; + +CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks) + : Callbacks(Callbacks) {} + +Error CVTypeVisitor::finishVisitation(CVType &Record) { + switch (Record.kind()) { + default: + if (auto EC = Callbacks.visitUnknownType(Record)) + return EC; + break; +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (auto EC = visitKnownRecord<Name##Record>(Record, Callbacks)) \ + return EC; \ + break; \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + TYPE_RECORD(EnumVal, EnumVal, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + if (auto EC = Callbacks.visitTypeEnd(Record)) + return EC; + + return Error::success(); +} + +Error CVTypeVisitor::visitTypeRecord(CVType &Record, TypeIndex Index) { + if (auto EC = Callbacks.visitTypeBegin(Record, Index)) + return EC; + + return finishVisitation(Record); +} + +Error CVTypeVisitor::visitTypeRecord(CVType &Record) { + if (auto EC = Callbacks.visitTypeBegin(Record)) + return EC; + + return finishVisitation(Record); +} + +Error CVTypeVisitor::visitMemberRecord(CVMemberRecord Record) { + return ::visitMemberRecord(Record, Callbacks); +} + +/// Visits the type records in Data. Sets the error flag on parse failures. +Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) { + for (auto I : Types) { + if (auto EC = visitTypeRecord(I)) + return EC; + } + return Error::success(); +} + +Error CVTypeVisitor::visitTypeStream(CVTypeRange Types) { + for (auto I : Types) { + if (auto EC = visitTypeRecord(I)) + return EC; + } + return Error::success(); +} + +Error CVTypeVisitor::visitTypeStream(TypeCollection &Types) { + Optional<TypeIndex> I = Types.getFirst(); + while (I) { + CVType Type = Types.getType(*I); + if (auto EC = visitTypeRecord(Type, *I)) + return EC; + I = Types.getNext(*I); + } + return Error::success(); +} + +Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader &Reader) { + TypeLeafKind Leaf; + while (!Reader.empty()) { + if (auto EC = Reader.readEnum(Leaf)) + return EC; + + CVMemberRecord Record; + Record.Kind = Leaf; + if (auto EC = ::visitMemberRecord(Record, Callbacks)) + return EC; + } + + return Error::success(); +} + +struct FieldListVisitHelper { + FieldListVisitHelper(TypeVisitorCallbacks &Callbacks, ArrayRef<uint8_t> Data, + VisitorDataSource Source) + : Stream(Data, llvm::support::little), Reader(Stream), + Deserializer(Reader), + Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) { + if (Source == VDS_BytesPresent) { + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Callbacks); + } + } + + BinaryByteStream Stream; + BinaryStreamReader Reader; + FieldListDeserializer Deserializer; + TypeVisitorCallbackPipeline Pipeline; + CVTypeVisitor Visitor; +}; + +struct VisitHelper { + VisitHelper(TypeVisitorCallbacks &Callbacks, VisitorDataSource Source) + : Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) { + if (Source == VDS_BytesPresent) { + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Callbacks); + } + } + + TypeDeserializer Deserializer; + TypeVisitorCallbackPipeline Pipeline; + CVTypeVisitor Visitor; +}; +} + +Error llvm::codeview::visitTypeRecord(CVType &Record, TypeIndex Index, + TypeVisitorCallbacks &Callbacks, + VisitorDataSource Source) { + VisitHelper V(Callbacks, Source); + return V.Visitor.visitTypeRecord(Record, Index); +} + +Error llvm::codeview::visitTypeRecord(CVType &Record, + TypeVisitorCallbacks &Callbacks, + VisitorDataSource Source) { + VisitHelper V(Callbacks, Source); + return V.Visitor.visitTypeRecord(Record); +} + +Error llvm::codeview::visitTypeStream(const CVTypeArray &Types, + TypeVisitorCallbacks &Callbacks, + VisitorDataSource Source) { + VisitHelper V(Callbacks, Source); + return V.Visitor.visitTypeStream(Types); +} + +Error llvm::codeview::visitTypeStream(CVTypeRange Types, + TypeVisitorCallbacks &Callbacks) { + VisitHelper V(Callbacks, VDS_BytesPresent); + return V.Visitor.visitTypeStream(Types); +} + +Error llvm::codeview::visitTypeStream(TypeCollection &Types, + TypeVisitorCallbacks &Callbacks) { + // When the internal visitor calls Types.getType(Index) the interface is + // required to return a CVType with the bytes filled out. So we can assume + // that the bytes will be present when individual records are visited. + VisitHelper V(Callbacks, VDS_BytesPresent); + return V.Visitor.visitTypeStream(Types); +} + +Error llvm::codeview::visitMemberRecord(CVMemberRecord Record, + TypeVisitorCallbacks &Callbacks, + VisitorDataSource Source) { + FieldListVisitHelper V(Callbacks, Record.Data, Source); + return V.Visitor.visitMemberRecord(Record); +} + +Error llvm::codeview::visitMemberRecord(TypeLeafKind Kind, + ArrayRef<uint8_t> Record, + TypeVisitorCallbacks &Callbacks) { + CVMemberRecord R; + R.Data = Record; + R.Kind = Kind; + return visitMemberRecord(R, Callbacks, VDS_BytesPresent); +} + +Error llvm::codeview::visitMemberRecordStream(ArrayRef<uint8_t> FieldList, + TypeVisitorCallbacks &Callbacks) { + FieldListVisitHelper V(Callbacks, FieldList, VDS_BytesPresent); + return V.Visitor.visitFieldListMemberStream(V.Reader); +} diff --git a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp new file mode 100644 index 000000000000..69390c708f59 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp @@ -0,0 +1,49 @@ +//===- CodeViewError.cpp - Error extensions for CodeView --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class CodeViewErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.codeview"; } + std::string message(int Condition) const override { + switch (static_cast<cv_error_code>(Condition)) { + case cv_error_code::unspecified: + return "An unknown CodeView error has occurred."; + case cv_error_code::insufficient_buffer: + return "The buffer is not large enough to read the requested number of " + "bytes."; + case cv_error_code::corrupt_record: + return "The CodeView record is corrupted."; + case cv_error_code::no_records: + return "There are no records."; + case cv_error_code::operation_unsupported: + return "The requested operation is not supported."; + case cv_error_code::unknown_member_record: + return "The member record is of an unknown type."; + } + llvm_unreachable("Unrecognized cv_error_code"); + } +}; +} // namespace + +static llvm::ManagedStatic<CodeViewErrorCategory> CodeViewErrCategory; +const std::error_category &llvm::codeview::CVErrorCategory() { + return *CodeViewErrCategory; +} + +char CodeViewError::ID; diff --git a/llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp b/llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp new file mode 100644 index 000000000000..36a384baa13d --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp @@ -0,0 +1,372 @@ +//===- CodeViewRecordIO.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/CodeViewRecordIO.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error CodeViewRecordIO::beginRecord(Optional<uint32_t> MaxLength) { + RecordLimit Limit; + Limit.MaxLength = MaxLength; + Limit.BeginOffset = getCurrentOffset(); + Limits.push_back(Limit); + return Error::success(); +} + +Error CodeViewRecordIO::endRecord() { + assert(!Limits.empty() && "Not in a record!"); + Limits.pop_back(); + // We would like to assert that we actually read / wrote all the bytes that we + // expected to for this record, but unfortunately we can't do this. Some + // producers such as MASM over-allocate for certain types of records and + // commit the extraneous data, so when reading we can't be sure every byte + // will have been read. And when writing we over-allocate temporarily since + // we don't know how big the record is until we're finished writing it, so + // even though we don't commit the extraneous data, we still can't guarantee + // we're at the end of the allocated data. + + if (isStreaming()) { + // For streaming mode, add padding to align with 4 byte boundaries for each + // record + uint32_t Align = getStreamedLen() % 4; + if (Align == 0) + return Error::success(); + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + char Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); + StringRef BytesSR = StringRef(&Pad, sizeof(Pad)); + Streamer->EmitBytes(BytesSR); + --PaddingBytes; + } + resetStreamedLen(); + } + return Error::success(); +} + +uint32_t CodeViewRecordIO::maxFieldLength() const { + if (isStreaming()) + return 0; + + assert(!Limits.empty() && "Not in a record!"); + + // The max length of the next field is the minimum of all lengths that would + // be allowed by any of the sub-records we're in. In practice, we can only + // ever be at most 1 sub-record deep (in a FieldList), but this works for + // the general case. + uint32_t Offset = getCurrentOffset(); + Optional<uint32_t> Min = Limits.front().bytesRemaining(Offset); + for (auto X : makeArrayRef(Limits).drop_front()) { + Optional<uint32_t> ThisMin = X.bytesRemaining(Offset); + if (ThisMin.hasValue()) + Min = (Min.hasValue()) ? std::min(*Min, *ThisMin) : *ThisMin; + } + assert(Min.hasValue() && "Every field must have a maximum length!"); + + return *Min; +} + +Error CodeViewRecordIO::padToAlignment(uint32_t Align) { + if (isReading()) + return Reader->padToAlignment(Align); + return Writer->padToAlignment(Align); +} + +Error CodeViewRecordIO::skipPadding() { + assert(!isWriting() && "Cannot skip padding while writing!"); + + if (Reader->bytesRemaining() == 0) + return Error::success(); + + uint8_t Leaf = Reader->peek(); + if (Leaf < LF_PAD0) + return Error::success(); + // Leaf is greater than 0xf0. We should advance by the number of bytes in + // the low 4 bits. + unsigned BytesToAdvance = Leaf & 0x0F; + return Reader->skip(BytesToAdvance); +} + +Error CodeViewRecordIO::mapByteVectorTail(ArrayRef<uint8_t> &Bytes, + const Twine &Comment) { + if (isStreaming()) { + emitComment(Comment); + Streamer->EmitBinaryData(toStringRef(Bytes)); + incrStreamedLen(Bytes.size()); + } else if (isWriting()) { + if (auto EC = Writer->writeBytes(Bytes)) + return EC; + } else { + if (auto EC = Reader->readBytes(Bytes, Reader->bytesRemaining())) + return EC; + } + return Error::success(); +} + +Error CodeViewRecordIO::mapByteVectorTail(std::vector<uint8_t> &Bytes, + const Twine &Comment) { + ArrayRef<uint8_t> BytesRef(Bytes); + if (auto EC = mapByteVectorTail(BytesRef, Comment)) + return EC; + if (!isWriting()) + Bytes.assign(BytesRef.begin(), BytesRef.end()); + + return Error::success(); +} + +Error CodeViewRecordIO::mapInteger(TypeIndex &TypeInd, const Twine &Comment) { + if (isStreaming()) { + std::string TypeNameStr = Streamer->getTypeName(TypeInd); + if (!TypeNameStr.empty()) + emitComment(Comment + ": " + TypeNameStr); + else + emitComment(Comment); + Streamer->EmitIntValue(TypeInd.getIndex(), sizeof(TypeInd.getIndex())); + incrStreamedLen(sizeof(TypeInd.getIndex())); + } else if (isWriting()) { + if (auto EC = Writer->writeInteger(TypeInd.getIndex())) + return EC; + } else { + uint32_t I; + if (auto EC = Reader->readInteger(I)) + return EC; + TypeInd.setIndex(I); + } + return Error::success(); +} + +Error CodeViewRecordIO::mapEncodedInteger(int64_t &Value, + const Twine &Comment) { + if (isStreaming()) { + if (Value >= 0) + emitEncodedUnsignedInteger(static_cast<uint64_t>(Value), Comment); + else + emitEncodedSignedInteger(Value, Comment); + } else if (isWriting()) { + if (Value >= 0) { + if (auto EC = writeEncodedUnsignedInteger(static_cast<uint64_t>(Value))) + return EC; + } else { + if (auto EC = writeEncodedSignedInteger(Value)) + return EC; + } + } else { + APSInt N; + if (auto EC = consume(*Reader, N)) + return EC; + Value = N.getExtValue(); + } + + return Error::success(); +} + +Error CodeViewRecordIO::mapEncodedInteger(uint64_t &Value, + const Twine &Comment) { + if (isStreaming()) + emitEncodedUnsignedInteger(Value, Comment); + else if (isWriting()) { + if (auto EC = writeEncodedUnsignedInteger(Value)) + return EC; + } else { + APSInt N; + if (auto EC = consume(*Reader, N)) + return EC; + Value = N.getZExtValue(); + } + return Error::success(); +} + +Error CodeViewRecordIO::mapEncodedInteger(APSInt &Value, const Twine &Comment) { + if (isStreaming()) { + if (Value.isSigned()) + emitEncodedSignedInteger(Value.getSExtValue(), Comment); + else + emitEncodedUnsignedInteger(Value.getZExtValue(), Comment); + } else if (isWriting()) { + if (Value.isSigned()) + return writeEncodedSignedInteger(Value.getSExtValue()); + return writeEncodedUnsignedInteger(Value.getZExtValue()); + } else + return consume(*Reader, Value); + return Error::success(); +} + +Error CodeViewRecordIO::mapStringZ(StringRef &Value, const Twine &Comment) { + if (isStreaming()) { + auto NullTerminatedString = StringRef(Value.data(), Value.size() + 1); + emitComment(Comment); + Streamer->EmitBytes(NullTerminatedString); + incrStreamedLen(NullTerminatedString.size()); + } else if (isWriting()) { + // Truncate if we attempt to write too much. + StringRef S = Value.take_front(maxFieldLength() - 1); + if (auto EC = Writer->writeCString(S)) + return EC; + } else { + if (auto EC = Reader->readCString(Value)) + return EC; + } + return Error::success(); +} + +Error CodeViewRecordIO::mapGuid(GUID &Guid, const Twine &Comment) { + constexpr uint32_t GuidSize = 16; + + if (isStreaming()) { + StringRef GuidSR = + StringRef((reinterpret_cast<const char *>(&Guid)), GuidSize); + emitComment(Comment); + Streamer->EmitBytes(GuidSR); + incrStreamedLen(GuidSize); + return Error::success(); + } + + if (maxFieldLength() < GuidSize) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + + if (isWriting()) { + if (auto EC = Writer->writeBytes(Guid.Guid)) + return EC; + } else { + ArrayRef<uint8_t> GuidBytes; + if (auto EC = Reader->readBytes(GuidBytes, GuidSize)) + return EC; + memcpy(Guid.Guid, GuidBytes.data(), GuidSize); + } + return Error::success(); +} + +Error CodeViewRecordIO::mapStringZVectorZ(std::vector<StringRef> &Value, + const Twine &Comment) { + + if (!isReading()) { + emitComment(Comment); + for (auto V : Value) { + if (auto EC = mapStringZ(V)) + return EC; + } + uint8_t FinalZero = 0; + if (auto EC = mapInteger(FinalZero)) + return EC; + } else { + StringRef S; + if (auto EC = mapStringZ(S)) + return EC; + while (!S.empty()) { + Value.push_back(S); + if (auto EC = mapStringZ(S)) + return EC; + }; + } + return Error::success(); +} + +void CodeViewRecordIO::emitEncodedSignedInteger(const int64_t &Value, + const Twine &Comment) { + assert(Value < 0 && "Encoded integer is not signed!"); + if (Value >= std::numeric_limits<int8_t>::min()) { + Streamer->EmitIntValue(LF_CHAR, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 1); + incrStreamedLen(3); + } else if (Value >= std::numeric_limits<int16_t>::min()) { + Streamer->EmitIntValue(LF_SHORT, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 2); + incrStreamedLen(4); + } else if (Value >= std::numeric_limits<int32_t>::min()) { + Streamer->EmitIntValue(LF_LONG, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 4); + incrStreamedLen(6); + } else { + Streamer->EmitIntValue(LF_QUADWORD, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 4); + incrStreamedLen(6); + } +} + +void CodeViewRecordIO::emitEncodedUnsignedInteger(const uint64_t &Value, + const Twine &Comment) { + if (Value < LF_NUMERIC) { + emitComment(Comment); + Streamer->EmitIntValue(Value, 2); + incrStreamedLen(2); + } else if (Value <= std::numeric_limits<uint16_t>::max()) { + Streamer->EmitIntValue(LF_USHORT, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 2); + incrStreamedLen(4); + } else if (Value <= std::numeric_limits<uint32_t>::max()) { + Streamer->EmitIntValue(LF_ULONG, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 4); + incrStreamedLen(6); + } else { + Streamer->EmitIntValue(LF_UQUADWORD, 2); + emitComment(Comment); + Streamer->EmitIntValue(Value, 8); + incrStreamedLen(6); + } +} + +Error CodeViewRecordIO::writeEncodedSignedInteger(const int64_t &Value) { + assert(Value < 0 && "Encoded integer is not signed!"); + if (Value >= std::numeric_limits<int8_t>::min()) { + if (auto EC = Writer->writeInteger<uint16_t>(LF_CHAR)) + return EC; + if (auto EC = Writer->writeInteger<int8_t>(Value)) + return EC; + } else if (Value >= std::numeric_limits<int16_t>::min()) { + if (auto EC = Writer->writeInteger<uint16_t>(LF_SHORT)) + return EC; + if (auto EC = Writer->writeInteger<int16_t>(Value)) + return EC; + } else if (Value >= std::numeric_limits<int32_t>::min()) { + if (auto EC = Writer->writeInteger<uint16_t>(LF_LONG)) + return EC; + if (auto EC = Writer->writeInteger<int32_t>(Value)) + return EC; + } else { + if (auto EC = Writer->writeInteger<uint16_t>(LF_QUADWORD)) + return EC; + if (auto EC = Writer->writeInteger(Value)) + return EC; + } + return Error::success(); +} + +Error CodeViewRecordIO::writeEncodedUnsignedInteger(const uint64_t &Value) { + if (Value < LF_NUMERIC) { + if (auto EC = Writer->writeInteger<uint16_t>(Value)) + return EC; + } else if (Value <= std::numeric_limits<uint16_t>::max()) { + if (auto EC = Writer->writeInteger<uint16_t>(LF_USHORT)) + return EC; + if (auto EC = Writer->writeInteger<uint16_t>(Value)) + return EC; + } else if (Value <= std::numeric_limits<uint32_t>::max()) { + if (auto EC = Writer->writeInteger<uint16_t>(LF_ULONG)) + return EC; + if (auto EC = Writer->writeInteger<uint32_t>(Value)) + return EC; + } else { + if (auto EC = Writer->writeInteger<uint16_t>(LF_UQUADWORD)) + return EC; + if (auto EC = Writer->writeInteger(Value)) + return EC; + } + + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp b/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp new file mode 100644 index 000000000000..799cffb7116e --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp @@ -0,0 +1,251 @@ +#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)); + + // Seed the first record with an appropriate record prefix. + RecordPrefix Prefix(getTypeLeafKind(RecordKind)); + CVType Type(&Prefix, sizeof(Prefix)); + cantFail(Mapping.visitTypeBegin(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); + + // Write the length to the RecordPrefix, making sure it does not include + // sizeof(RecordPrefix.Length) + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data()); + 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 CVType(Data); +} + +std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) { + RecordPrefix Prefix(getTypeLeafKind(*Kind)); + CVType Type(&Prefix, sizeof(Prefix)); + 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/llvm/lib/DebugInfo/CodeView/DebugChecksumsSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugChecksumsSubsection.cpp new file mode 100644 index 000000000000..3d28bac00c44 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugChecksumsSubsection.cpp @@ -0,0 +1,115 @@ +//===- DebugChecksumsSubsection.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +struct FileChecksumEntryHeader { + using ulittle32_t = support::ulittle32_t; + + ulittle32_t FileNameOffset; // Byte offset of filename in global string table. + uint8_t ChecksumSize; // Number of bytes of checksum. + uint8_t ChecksumKind; // FileChecksumKind + // Checksum bytes follow. +}; + +Error VarStreamArrayExtractor<FileChecksumEntry>:: +operator()(BinaryStreamRef Stream, uint32_t &Len, FileChecksumEntry &Item) { + BinaryStreamReader Reader(Stream); + + const FileChecksumEntryHeader *Header; + if (auto EC = Reader.readObject(Header)) + return EC; + + Item.FileNameOffset = Header->FileNameOffset; + Item.Kind = static_cast<FileChecksumKind>(Header->ChecksumKind); + if (auto EC = Reader.readBytes(Item.Checksum, Header->ChecksumSize)) + return EC; + + Len = alignTo(Header->ChecksumSize + sizeof(FileChecksumEntryHeader), 4); + return Error::success(); +} + +Error DebugChecksumsSubsectionRef::initialize(BinaryStreamReader Reader) { + if (auto EC = Reader.readArray(Checksums, Reader.bytesRemaining())) + return EC; + + return Error::success(); +} + +Error DebugChecksumsSubsectionRef::initialize(BinaryStreamRef Section) { + BinaryStreamReader Reader(Section); + return initialize(Reader); +} + +DebugChecksumsSubsection::DebugChecksumsSubsection( + DebugStringTableSubsection &Strings) + : DebugSubsection(DebugSubsectionKind::FileChecksums), Strings(Strings) {} + +void DebugChecksumsSubsection::addChecksum(StringRef FileName, + FileChecksumKind Kind, + ArrayRef<uint8_t> Bytes) { + FileChecksumEntry Entry; + if (!Bytes.empty()) { + uint8_t *Copy = Storage.Allocate<uint8_t>(Bytes.size()); + ::memcpy(Copy, Bytes.data(), Bytes.size()); + Entry.Checksum = makeArrayRef(Copy, Bytes.size()); + } + + Entry.FileNameOffset = Strings.insert(FileName); + Entry.Kind = Kind; + Checksums.push_back(Entry); + + // This maps the offset of this string in the string table to the offset + // of this checksum entry in the checksum buffer. + OffsetMap[Entry.FileNameOffset] = SerializedSize; + assert(SerializedSize % 4 == 0); + + uint32_t Len = alignTo(sizeof(FileChecksumEntryHeader) + Bytes.size(), 4); + SerializedSize += Len; +} + +uint32_t DebugChecksumsSubsection::calculateSerializedSize() const { + return SerializedSize; +} + +Error DebugChecksumsSubsection::commit(BinaryStreamWriter &Writer) const { + for (const auto &FC : Checksums) { + FileChecksumEntryHeader Header; + Header.ChecksumKind = uint8_t(FC.Kind); + Header.ChecksumSize = FC.Checksum.size(); + Header.FileNameOffset = FC.FileNameOffset; + if (auto EC = Writer.writeObject(Header)) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(FC.Checksum))) + return EC; + if (auto EC = Writer.padToAlignment(4)) + return EC; + } + return Error::success(); +} + +uint32_t DebugChecksumsSubsection::mapChecksumOffset(StringRef FileName) const { + uint32_t Offset = Strings.getIdForString(FileName); + auto Iter = OffsetMap.find(Offset); + assert(Iter != OffsetMap.end()); + return Iter->second; +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugCrossExSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugCrossExSubsection.cpp new file mode 100644 index 000000000000..b23410409f88 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugCrossExSubsection.cpp @@ -0,0 +1,52 @@ +//===- DebugCrossExSubsection.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; + +Error DebugCrossModuleExportsSubsectionRef::initialize( + BinaryStreamReader Reader) { + if (Reader.bytesRemaining() % sizeof(CrossModuleExport) != 0) + return make_error<CodeViewError>( + cv_error_code::corrupt_record, + "Cross Scope Exports section is an invalid size!"); + + uint32_t Size = Reader.bytesRemaining() / sizeof(CrossModuleExport); + return Reader.readArray(References, Size); +} + +Error DebugCrossModuleExportsSubsectionRef::initialize(BinaryStreamRef Stream) { + BinaryStreamReader Reader(Stream); + return initialize(Reader); +} + +void DebugCrossModuleExportsSubsection::addMapping(uint32_t Local, + uint32_t Global) { + Mappings[Local] = Global; +} + +uint32_t DebugCrossModuleExportsSubsection::calculateSerializedSize() const { + return Mappings.size() * sizeof(CrossModuleExport); +} + +Error DebugCrossModuleExportsSubsection::commit( + BinaryStreamWriter &Writer) const { + for (const auto &M : Mappings) { + if (auto EC = Writer.writeInteger(M.first)) + return EC; + if (auto EC = Writer.writeInteger(M.second)) + return EC; + } + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugCrossImpSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugCrossImpSubsection.cpp new file mode 100644 index 000000000000..dbadafd3aaf3 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugCrossImpSubsection.cpp @@ -0,0 +1,96 @@ +//===- DebugCrossImpSubsection.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace llvm::codeview; + +Error VarStreamArrayExtractor<CrossModuleImportItem>:: +operator()(BinaryStreamRef Stream, uint32_t &Len, + codeview::CrossModuleImportItem &Item) { + BinaryStreamReader Reader(Stream); + if (Reader.bytesRemaining() < sizeof(CrossModuleImport)) + return make_error<CodeViewError>( + cv_error_code::insufficient_buffer, + "Not enough bytes for a Cross Module Import Header!"); + if (auto EC = Reader.readObject(Item.Header)) + return EC; + if (Reader.bytesRemaining() < Item.Header->Count * sizeof(uint32_t)) + return make_error<CodeViewError>( + cv_error_code::insufficient_buffer, + "Not enough to read specified number of Cross Module References!"); + if (auto EC = Reader.readArray(Item.Imports, Item.Header->Count)) + return EC; + return Error::success(); +} + +Error DebugCrossModuleImportsSubsectionRef::initialize( + BinaryStreamReader Reader) { + return Reader.readArray(References, Reader.bytesRemaining()); +} + +Error DebugCrossModuleImportsSubsectionRef::initialize(BinaryStreamRef Stream) { + BinaryStreamReader Reader(Stream); + return initialize(Reader); +} + +void DebugCrossModuleImportsSubsection::addImport(StringRef Module, + uint32_t ImportId) { + Strings.insert(Module); + std::vector<support::ulittle32_t> Targets = {support::ulittle32_t(ImportId)}; + auto Result = Mappings.insert(std::make_pair(Module, Targets)); + if (!Result.second) + Result.first->getValue().push_back(Targets[0]); +} + +uint32_t DebugCrossModuleImportsSubsection::calculateSerializedSize() const { + uint32_t Size = 0; + for (const auto &Item : Mappings) { + Size += sizeof(CrossModuleImport); + Size += sizeof(support::ulittle32_t) * Item.second.size(); + } + return Size; +} + +Error DebugCrossModuleImportsSubsection::commit( + BinaryStreamWriter &Writer) const { + using T = decltype(&*Mappings.begin()); + std::vector<T> Ids; + Ids.reserve(Mappings.size()); + + for (const auto &M : Mappings) + Ids.push_back(&M); + + llvm::sort(Ids, [this](const T &L1, const T &L2) { + return Strings.getIdForString(L1->getKey()) < + Strings.getIdForString(L2->getKey()); + }); + + for (const auto &Item : Ids) { + CrossModuleImport Imp; + Imp.ModuleNameOffset = Strings.getIdForString(Item->getKey()); + Imp.Count = Item->getValue().size(); + if (auto EC = Writer.writeObject(Imp)) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(Item->getValue()))) + return EC; + } + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugFrameDataSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugFrameDataSubsection.cpp new file mode 100644 index 000000000000..be8c32d5b294 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugFrameDataSubsection.cpp @@ -0,0 +1,61 @@ +//===- DebugFrameDataSubsection.cpp -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error DebugFrameDataSubsectionRef::initialize(BinaryStreamReader Reader) { + if (Reader.bytesRemaining() % sizeof(FrameData) != 0) { + if (auto EC = Reader.readObject(RelocPtr)) + return EC; + } + + if (Reader.bytesRemaining() % sizeof(FrameData) != 0) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid frame data record format!"); + + uint32_t Count = Reader.bytesRemaining() / sizeof(FrameData); + if (auto EC = Reader.readArray(Frames, Count)) + return EC; + return Error::success(); +} + +Error DebugFrameDataSubsectionRef::initialize(BinaryStreamRef Section) { + BinaryStreamReader Reader(Section); + return initialize(Reader); +} + +uint32_t DebugFrameDataSubsection::calculateSerializedSize() const { + uint32_t Size = sizeof(FrameData) * Frames.size(); + if (IncludeRelocPtr) + Size += sizeof(uint32_t); + return Size; +} + +Error DebugFrameDataSubsection::commit(BinaryStreamWriter &Writer) const { + if (IncludeRelocPtr) { + if (auto EC = Writer.writeInteger<uint32_t>(0)) + return EC; + } + + std::vector<FrameData> SortedFrames(Frames.begin(), Frames.end()); + std::sort(SortedFrames.begin(), SortedFrames.end(), + [](const FrameData &LHS, const FrameData &RHS) { + return LHS.RvaStart < RHS.RvaStart; + }); + if (auto EC = Writer.writeArray(makeArrayRef(SortedFrames))) + return EC; + return Error::success(); +} + +void DebugFrameDataSubsection::addFrameData(const FrameData &Frame) { + Frames.push_back(Frame); +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugInlineeLinesSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugInlineeLinesSubsection.cpp new file mode 100644 index 000000000000..48ec7e4ecdd6 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugInlineeLinesSubsection.cpp @@ -0,0 +1,125 @@ +//===- DebugInlineeLinesSubsection.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <cassert> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; + +Error VarStreamArrayExtractor<InlineeSourceLine>:: +operator()(BinaryStreamRef Stream, uint32_t &Len, InlineeSourceLine &Item) { + BinaryStreamReader Reader(Stream); + + if (auto EC = Reader.readObject(Item.Header)) + return EC; + + if (HasExtraFiles) { + uint32_t ExtraFileCount; + if (auto EC = Reader.readInteger(ExtraFileCount)) + return EC; + if (auto EC = Reader.readArray(Item.ExtraFiles, ExtraFileCount)) + return EC; + } + + Len = Reader.getOffset(); + return Error::success(); +} + +DebugInlineeLinesSubsectionRef::DebugInlineeLinesSubsectionRef() + : DebugSubsectionRef(DebugSubsectionKind::InlineeLines) {} + +Error DebugInlineeLinesSubsectionRef::initialize(BinaryStreamReader Reader) { + if (auto EC = Reader.readEnum(Signature)) + return EC; + + Lines.getExtractor().HasExtraFiles = hasExtraFiles(); + if (auto EC = Reader.readArray(Lines, Reader.bytesRemaining())) + return EC; + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} + +bool DebugInlineeLinesSubsectionRef::hasExtraFiles() const { + return Signature == InlineeLinesSignature::ExtraFiles; +} + +DebugInlineeLinesSubsection::DebugInlineeLinesSubsection( + DebugChecksumsSubsection &Checksums, bool HasExtraFiles) + : DebugSubsection(DebugSubsectionKind::InlineeLines), Checksums(Checksums), + HasExtraFiles(HasExtraFiles) {} + +uint32_t DebugInlineeLinesSubsection::calculateSerializedSize() const { + // 4 bytes for the signature + uint32_t Size = sizeof(InlineeLinesSignature); + + // one header for each entry. + Size += Entries.size() * sizeof(InlineeSourceLineHeader); + if (HasExtraFiles) { + // If extra files are enabled, one count for each entry. + Size += Entries.size() * sizeof(uint32_t); + + // And one file id for each file. + Size += ExtraFileCount * sizeof(uint32_t); + } + assert(Size % 4 == 0); + return Size; +} + +Error DebugInlineeLinesSubsection::commit(BinaryStreamWriter &Writer) const { + InlineeLinesSignature Sig = InlineeLinesSignature::Normal; + if (HasExtraFiles) + Sig = InlineeLinesSignature::ExtraFiles; + + if (auto EC = Writer.writeEnum(Sig)) + return EC; + + for (const auto &E : Entries) { + if (auto EC = Writer.writeObject(E.Header)) + return EC; + + if (!HasExtraFiles) + continue; + + if (auto EC = Writer.writeInteger<uint32_t>(E.ExtraFiles.size())) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(E.ExtraFiles))) + return EC; + } + + return Error::success(); +} + +void DebugInlineeLinesSubsection::addExtraFile(StringRef FileName) { + uint32_t Offset = Checksums.mapChecksumOffset(FileName); + + auto &Entry = Entries.back(); + Entry.ExtraFiles.push_back(ulittle32_t(Offset)); + ++ExtraFileCount; +} + +void DebugInlineeLinesSubsection::addInlineSite(TypeIndex FuncId, + StringRef FileName, + uint32_t SourceLine) { + uint32_t Offset = Checksums.mapChecksumOffset(FileName); + + Entries.emplace_back(); + auto &Entry = Entries.back(); + Entry.Header.FileID = Offset; + Entry.Header.SourceLineNum = SourceLine; + Entry.Header.Inlinee = FuncId; +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugLinesSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugLinesSubsection.cpp new file mode 100644 index 000000000000..ea16c0a6c671 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugLinesSubsection.cpp @@ -0,0 +1,160 @@ +//===- DebugLinesSubsection.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include <cassert> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; + +Error LineColumnExtractor::operator()(BinaryStreamRef Stream, uint32_t &Len, + LineColumnEntry &Item) { + const LineBlockFragmentHeader *BlockHeader; + BinaryStreamReader Reader(Stream); + if (auto EC = Reader.readObject(BlockHeader)) + return EC; + bool HasColumn = Header->Flags & uint16_t(LF_HaveColumns); + uint32_t LineInfoSize = + BlockHeader->NumLines * + (sizeof(LineNumberEntry) + (HasColumn ? sizeof(ColumnNumberEntry) : 0)); + if (BlockHeader->BlockSize < sizeof(LineBlockFragmentHeader)) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid line block record size"); + uint32_t Size = BlockHeader->BlockSize - sizeof(LineBlockFragmentHeader); + if (LineInfoSize > Size) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid line block record size"); + // The value recorded in BlockHeader->BlockSize includes the size of + // LineBlockFragmentHeader. + Len = BlockHeader->BlockSize; + Item.NameIndex = BlockHeader->NameIndex; + if (auto EC = Reader.readArray(Item.LineNumbers, BlockHeader->NumLines)) + return EC; + if (HasColumn) { + if (auto EC = Reader.readArray(Item.Columns, BlockHeader->NumLines)) + return EC; + } + return Error::success(); +} + +DebugLinesSubsectionRef::DebugLinesSubsectionRef() + : DebugSubsectionRef(DebugSubsectionKind::Lines) {} + +Error DebugLinesSubsectionRef::initialize(BinaryStreamReader Reader) { + if (auto EC = Reader.readObject(Header)) + return EC; + + LinesAndColumns.getExtractor().Header = Header; + if (auto EC = Reader.readArray(LinesAndColumns, Reader.bytesRemaining())) + return EC; + + return Error::success(); +} + +bool DebugLinesSubsectionRef::hasColumnInfo() const { + return !!(Header->Flags & LF_HaveColumns); +} + +DebugLinesSubsection::DebugLinesSubsection(DebugChecksumsSubsection &Checksums, + DebugStringTableSubsection &Strings) + : DebugSubsection(DebugSubsectionKind::Lines), Checksums(Checksums) {} + +void DebugLinesSubsection::createBlock(StringRef FileName) { + uint32_t Offset = Checksums.mapChecksumOffset(FileName); + + Blocks.emplace_back(Offset); +} + +void DebugLinesSubsection::addLineInfo(uint32_t Offset, const LineInfo &Line) { + Block &B = Blocks.back(); + LineNumberEntry LNE; + LNE.Flags = Line.getRawData(); + LNE.Offset = Offset; + B.Lines.push_back(LNE); +} + +void DebugLinesSubsection::addLineAndColumnInfo(uint32_t Offset, + const LineInfo &Line, + uint32_t ColStart, + uint32_t ColEnd) { + Block &B = Blocks.back(); + assert(B.Lines.size() == B.Columns.size()); + + addLineInfo(Offset, Line); + ColumnNumberEntry CNE; + CNE.StartColumn = ColStart; + CNE.EndColumn = ColEnd; + B.Columns.push_back(CNE); +} + +Error DebugLinesSubsection::commit(BinaryStreamWriter &Writer) const { + LineFragmentHeader Header; + Header.CodeSize = CodeSize; + Header.Flags = hasColumnInfo() ? LF_HaveColumns : 0; + Header.RelocOffset = RelocOffset; + Header.RelocSegment = RelocSegment; + + if (auto EC = Writer.writeObject(Header)) + return EC; + + for (const auto &B : Blocks) { + LineBlockFragmentHeader BlockHeader; + assert(B.Lines.size() == B.Columns.size() || B.Columns.empty()); + + BlockHeader.NumLines = B.Lines.size(); + BlockHeader.BlockSize = sizeof(LineBlockFragmentHeader); + BlockHeader.BlockSize += BlockHeader.NumLines * sizeof(LineNumberEntry); + if (hasColumnInfo()) + BlockHeader.BlockSize += BlockHeader.NumLines * sizeof(ColumnNumberEntry); + BlockHeader.NameIndex = B.ChecksumBufferOffset; + if (auto EC = Writer.writeObject(BlockHeader)) + return EC; + + if (auto EC = Writer.writeArray(makeArrayRef(B.Lines))) + return EC; + + if (hasColumnInfo()) { + if (auto EC = Writer.writeArray(makeArrayRef(B.Columns))) + return EC; + } + } + return Error::success(); +} + +uint32_t DebugLinesSubsection::calculateSerializedSize() const { + uint32_t Size = sizeof(LineFragmentHeader); + for (const auto &B : Blocks) { + Size += sizeof(LineBlockFragmentHeader); + Size += B.Lines.size() * sizeof(LineNumberEntry); + if (hasColumnInfo()) + Size += B.Columns.size() * sizeof(ColumnNumberEntry); + } + return Size; +} + +void DebugLinesSubsection::setRelocationAddress(uint16_t Segment, + uint32_t Offset) { + RelocOffset = Offset; + RelocSegment = Segment; +} + +void DebugLinesSubsection::setCodeSize(uint32_t Size) { CodeSize = Size; } + +void DebugLinesSubsection::setFlags(LineFlags Flags) { this->Flags = Flags; } + +bool DebugLinesSubsection::hasColumnInfo() const { + return Flags & LF_HaveColumns; +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugStringTableSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugStringTableSubsection.cpp new file mode 100644 index 000000000000..63342749918d --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugStringTableSubsection.cpp @@ -0,0 +1,107 @@ +//===- DebugStringTableSubsection.cpp - CodeView String Table -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; + +DebugStringTableSubsectionRef::DebugStringTableSubsectionRef() + : DebugSubsectionRef(DebugSubsectionKind::StringTable) {} + +Error DebugStringTableSubsectionRef::initialize(BinaryStreamRef Contents) { + Stream = Contents; + return Error::success(); +} + +Error DebugStringTableSubsectionRef::initialize(BinaryStreamReader &Reader) { + return Reader.readStreamRef(Stream); +} + +Expected<StringRef> +DebugStringTableSubsectionRef::getString(uint32_t Offset) const { + BinaryStreamReader Reader(Stream); + Reader.setOffset(Offset); + StringRef Result; + if (auto EC = Reader.readCString(Result)) + return std::move(EC); + return Result; +} + +DebugStringTableSubsection::DebugStringTableSubsection() + : DebugSubsection(DebugSubsectionKind::StringTable) {} + +uint32_t DebugStringTableSubsection::insert(StringRef S) { + auto P = StringToId.insert({S, StringSize}); + + // If a given string didn't exist in the string table, we want to increment + // the string table size and insert it into the reverse lookup. + if (P.second) { + IdToString.insert({P.first->getValue(), P.first->getKey()}); + StringSize += S.size() + 1; // +1 for '\0' + } + + return P.first->second; +} + +uint32_t DebugStringTableSubsection::calculateSerializedSize() const { + return StringSize; +} + +Error DebugStringTableSubsection::commit(BinaryStreamWriter &Writer) const { + uint32_t Begin = Writer.getOffset(); + uint32_t End = Begin + StringSize; + + // Write a null string at the beginning. + if (auto EC = Writer.writeCString(StringRef())) + return EC; + + for (auto &Pair : StringToId) { + StringRef S = Pair.getKey(); + uint32_t Offset = Begin + Pair.getValue(); + Writer.setOffset(Offset); + if (auto EC = Writer.writeCString(S)) + return EC; + assert(Writer.getOffset() <= End); + } + + Writer.setOffset(End); + assert((End - Begin) == StringSize); + return Error::success(); +} + +uint32_t DebugStringTableSubsection::size() const { return StringToId.size(); } + +std::vector<uint32_t> DebugStringTableSubsection::sortedIds() const { + std::vector<uint32_t> Result; + Result.reserve(IdToString.size()); + for (const auto &Entry : IdToString) + Result.push_back(Entry.first); + llvm::sort(Result); + return Result; +} + +uint32_t DebugStringTableSubsection::getIdForString(StringRef S) const { + auto Iter = StringToId.find(S); + assert(Iter != StringToId.end()); + return Iter->second; +} + +StringRef DebugStringTableSubsection::getStringForId(uint32_t Id) const { + auto Iter = IdToString.find(Id); + assert(Iter != IdToString.end()); + return Iter->second; +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugSubsection.cpp new file mode 100644 index 000000000000..3f93463fe6d6 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugSubsection.cpp @@ -0,0 +1,15 @@ +//===- DebugSubsection.cpp -----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSubsection.h" + +using namespace llvm::codeview; + +DebugSubsectionRef::~DebugSubsectionRef() {} + +DebugSubsection::~DebugSubsection() {} diff --git a/llvm/lib/DebugInfo/CodeView/DebugSubsectionRecord.cpp b/llvm/lib/DebugInfo/CodeView/DebugSubsectionRecord.cpp new file mode 100644 index 000000000000..0f704f286ee9 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugSubsectionRecord.cpp @@ -0,0 +1,96 @@ +//===- DebugSubsectionRecord.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cassert> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; + +DebugSubsectionRecord::DebugSubsectionRecord() = default; + +DebugSubsectionRecord::DebugSubsectionRecord(DebugSubsectionKind Kind, + BinaryStreamRef Data, + CodeViewContainer Container) + : Container(Container), Kind(Kind), Data(Data) {} + +Error DebugSubsectionRecord::initialize(BinaryStreamRef Stream, + DebugSubsectionRecord &Info, + CodeViewContainer Container) { + const DebugSubsectionHeader *Header; + BinaryStreamReader Reader(Stream); + if (auto EC = Reader.readObject(Header)) + return EC; + + DebugSubsectionKind Kind = + static_cast<DebugSubsectionKind>(uint32_t(Header->Kind)); + if (auto EC = Reader.readStreamRef(Info.Data, Header->Length)) + return EC; + Info.Container = Container; + Info.Kind = Kind; + return Error::success(); +} + +uint32_t DebugSubsectionRecord::getRecordLength() const { + return sizeof(DebugSubsectionHeader) + Data.getLength(); +} + +DebugSubsectionKind DebugSubsectionRecord::kind() const { return Kind; } + +BinaryStreamRef DebugSubsectionRecord::getRecordData() const { return Data; } + +DebugSubsectionRecordBuilder::DebugSubsectionRecordBuilder( + std::shared_ptr<DebugSubsection> Subsection, CodeViewContainer Container) + : Subsection(std::move(Subsection)), Container(Container) {} + +DebugSubsectionRecordBuilder::DebugSubsectionRecordBuilder( + const DebugSubsectionRecord &Contents, CodeViewContainer Container) + : Contents(Contents), Container(Container) {} + +uint32_t DebugSubsectionRecordBuilder::calculateSerializedLength() { + uint32_t DataSize = Subsection ? Subsection->calculateSerializedSize() + : Contents.getRecordData().getLength(); + // The length of the entire subsection is always padded to 4 bytes, + // regardless of the container kind. + return sizeof(DebugSubsectionHeader) + alignTo(DataSize, 4); +} + +Error DebugSubsectionRecordBuilder::commit(BinaryStreamWriter &Writer) const { + assert(Writer.getOffset() % alignOf(Container) == 0 && + "Debug Subsection not properly aligned"); + + DebugSubsectionHeader Header; + Header.Kind = uint32_t(Subsection ? Subsection->kind() : Contents.kind()); + // The value written into the Header's Length field is only padded to the + // container's alignment + uint32_t DataSize = Subsection ? Subsection->calculateSerializedSize() + : Contents.getRecordData().getLength(); + Header.Length = alignTo(DataSize, alignOf(Container)); + + if (auto EC = Writer.writeObject(Header)) + return EC; + if (Subsection) { + if (auto EC = Subsection->commit(Writer)) + return EC; + } else { + if (auto EC = Writer.writeStreamRef(Contents.getRecordData())) + return EC; + } + if (auto EC = Writer.padToAlignment(4)) + return EC; + + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugSubsectionVisitor.cpp b/llvm/lib/DebugInfo/CodeView/DebugSubsectionVisitor.cpp new file mode 100644 index 000000000000..7968b6a2d757 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugSubsectionVisitor.cpp @@ -0,0 +1,94 @@ +//===- DebugSubsectionVisitor.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" + +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/DebugSymbolRVASubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error llvm::codeview::visitDebugSubsection( + const DebugSubsectionRecord &R, DebugSubsectionVisitor &V, + const StringsAndChecksumsRef &State) { + BinaryStreamReader Reader(R.getRecordData()); + switch (R.kind()) { + case DebugSubsectionKind::Lines: { + DebugLinesSubsectionRef Fragment; + if (auto EC = Fragment.initialize(Reader)) + return EC; + + return V.visitLines(Fragment, State); + } + case DebugSubsectionKind::FileChecksums: { + DebugChecksumsSubsectionRef Fragment; + if (auto EC = Fragment.initialize(Reader)) + return EC; + + return V.visitFileChecksums(Fragment, State); + } + case DebugSubsectionKind::InlineeLines: { + DebugInlineeLinesSubsectionRef Fragment; + if (auto EC = Fragment.initialize(Reader)) + return EC; + return V.visitInlineeLines(Fragment, State); + } + case DebugSubsectionKind::CrossScopeExports: { + DebugCrossModuleExportsSubsectionRef Section; + if (auto EC = Section.initialize(Reader)) + return EC; + return V.visitCrossModuleExports(Section, State); + } + case DebugSubsectionKind::CrossScopeImports: { + DebugCrossModuleImportsSubsectionRef Section; + if (auto EC = Section.initialize(Reader)) + return EC; + return V.visitCrossModuleImports(Section, State); + } + case DebugSubsectionKind::Symbols: { + DebugSymbolsSubsectionRef Section; + if (auto EC = Section.initialize(Reader)) + return EC; + return V.visitSymbols(Section, State); + } + case DebugSubsectionKind::StringTable: { + DebugStringTableSubsectionRef Section; + if (auto EC = Section.initialize(Reader)) + return EC; + return V.visitStringTable(Section, State); + } + case DebugSubsectionKind::FrameData: { + DebugFrameDataSubsectionRef Section; + if (auto EC = Section.initialize(Reader)) + return EC; + return V.visitFrameData(Section, State); + } + case DebugSubsectionKind::CoffSymbolRVA: { + DebugSymbolRVASubsectionRef Section; + if (auto EC = Section.initialize(Reader)) + return EC; + return V.visitCOFFSymbolRVAs(Section, State); + } + default: { + DebugUnknownSubsectionRef Fragment(R.kind(), R.getRecordData()); + return V.visitUnknown(Fragment); + } + } +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugSymbolRVASubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugSymbolRVASubsection.cpp new file mode 100644 index 000000000000..52328967357b --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugSymbolRVASubsection.cpp @@ -0,0 +1,35 @@ +//===- DebugSymbolRVASubsection.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSymbolRVASubsection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; + +DebugSymbolRVASubsectionRef::DebugSymbolRVASubsectionRef() + : DebugSubsectionRef(DebugSubsectionKind::CoffSymbolRVA) {} + +Error DebugSymbolRVASubsectionRef::initialize(BinaryStreamReader &Reader) { + return Reader.readArray(RVAs, Reader.bytesRemaining() / sizeof(uint32_t)); +} + +DebugSymbolRVASubsection::DebugSymbolRVASubsection() + : DebugSubsection(DebugSubsectionKind::CoffSymbolRVA) {} + +Error DebugSymbolRVASubsection::commit(BinaryStreamWriter &Writer) const { + return Writer.writeArray(makeArrayRef(RVAs)); +} + +uint32_t DebugSymbolRVASubsection::calculateSerializedSize() const { + return RVAs.size() * sizeof(uint32_t); +} diff --git a/llvm/lib/DebugInfo/CodeView/DebugSymbolsSubsection.cpp b/llvm/lib/DebugInfo/CodeView/DebugSymbolsSubsection.cpp new file mode 100644 index 000000000000..c833103663e4 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/DebugSymbolsSubsection.cpp @@ -0,0 +1,33 @@ +//===- DebugSymbolsSubsection.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error DebugSymbolsSubsectionRef::initialize(BinaryStreamReader Reader) { + return Reader.readArray(Records, Reader.getLength()); +} + +uint32_t DebugSymbolsSubsection::calculateSerializedSize() const { + return Length; +} + +Error DebugSymbolsSubsection::commit(BinaryStreamWriter &Writer) const { + for (const auto &Record : Records) { + if (auto EC = Writer.writeBytes(Record.RecordData)) + return EC; + } + return Error::success(); +} + +void DebugSymbolsSubsection::addSymbol(CVSymbol Symbol) { + Records.push_back(Symbol); + Length += Symbol.length(); +} diff --git a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp new file mode 100644 index 000000000000..82f6713a88f5 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp @@ -0,0 +1,549 @@ +//===- EnumTables.cpp - Enum to string conversion tables ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/Support/ScopedPrinter.h" +#include <type_traits> + +using namespace llvm; +using namespace codeview; + +#define CV_ENUM_CLASS_ENT(enum_class, enum) \ + { #enum, std::underlying_type < enum_class > ::type(enum_class::enum) } + +#define CV_ENUM_ENT(ns, enum) \ + { #enum, ns::enum } + +static const EnumEntry<SymbolKind> SymbolTypeNames[] = { +#define CV_SYMBOL(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" +#undef CV_SYMBOL +}; + +static const EnumEntry<TypeLeafKind> TypeLeafNames[] = { +#define CV_TYPE(name, val) {#name, name}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +#undef CV_TYPE +}; + +static const EnumEntry<uint16_t> RegisterNames_X86[] = { +#define CV_REGISTERS_X86 +#define CV_REGISTER(name, val) CV_ENUM_CLASS_ENT(RegisterId, name), +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_X86 +}; + +static const EnumEntry<uint16_t> RegisterNames_ARM64[] = { +#define CV_REGISTERS_ARM64 +#define CV_REGISTER(name, val) CV_ENUM_CLASS_ENT(RegisterId, name), +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM64 +}; + +static const EnumEntry<uint32_t> PublicSymFlagNames[] = { + CV_ENUM_CLASS_ENT(PublicSymFlags, Code), + CV_ENUM_CLASS_ENT(PublicSymFlags, Function), + CV_ENUM_CLASS_ENT(PublicSymFlags, Managed), + CV_ENUM_CLASS_ENT(PublicSymFlags, MSIL), +}; + +static const EnumEntry<uint8_t> ProcSymFlagNames[] = { + CV_ENUM_CLASS_ENT(ProcSymFlags, HasFP), + CV_ENUM_CLASS_ENT(ProcSymFlags, HasIRET), + CV_ENUM_CLASS_ENT(ProcSymFlags, HasFRET), + CV_ENUM_CLASS_ENT(ProcSymFlags, IsNoReturn), + CV_ENUM_CLASS_ENT(ProcSymFlags, IsUnreachable), + CV_ENUM_CLASS_ENT(ProcSymFlags, HasCustomCallingConv), + CV_ENUM_CLASS_ENT(ProcSymFlags, IsNoInline), + CV_ENUM_CLASS_ENT(ProcSymFlags, HasOptimizedDebugInfo), +}; + +static const EnumEntry<uint16_t> LocalFlags[] = { + CV_ENUM_CLASS_ENT(LocalSymFlags, IsParameter), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsAddressTaken), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsCompilerGenerated), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsAggregate), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsAggregated), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsAliased), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsAlias), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsReturnValue), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsOptimizedOut), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsEnregisteredGlobal), + CV_ENUM_CLASS_ENT(LocalSymFlags, IsEnregisteredStatic), +}; + +static const EnumEntry<uint8_t> FrameCookieKinds[] = { + CV_ENUM_CLASS_ENT(FrameCookieKind, Copy), + CV_ENUM_CLASS_ENT(FrameCookieKind, XorStackPointer), + CV_ENUM_CLASS_ENT(FrameCookieKind, XorFramePointer), + CV_ENUM_CLASS_ENT(FrameCookieKind, XorR13), +}; + +static const EnumEntry<codeview::SourceLanguage> SourceLanguages[] = { + CV_ENUM_ENT(SourceLanguage, C), CV_ENUM_ENT(SourceLanguage, Cpp), + CV_ENUM_ENT(SourceLanguage, Fortran), CV_ENUM_ENT(SourceLanguage, Masm), + CV_ENUM_ENT(SourceLanguage, Pascal), CV_ENUM_ENT(SourceLanguage, Basic), + CV_ENUM_ENT(SourceLanguage, Cobol), CV_ENUM_ENT(SourceLanguage, Link), + CV_ENUM_ENT(SourceLanguage, Cvtres), CV_ENUM_ENT(SourceLanguage, Cvtpgd), + 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, D), + CV_ENUM_ENT(SourceLanguage, Swift), +}; + +static const EnumEntry<uint32_t> CompileSym2FlagNames[] = { + CV_ENUM_CLASS_ENT(CompileSym2Flags, EC), + CV_ENUM_CLASS_ENT(CompileSym2Flags, NoDbgInfo), + CV_ENUM_CLASS_ENT(CompileSym2Flags, LTCG), + CV_ENUM_CLASS_ENT(CompileSym2Flags, NoDataAlign), + CV_ENUM_CLASS_ENT(CompileSym2Flags, ManagedPresent), + CV_ENUM_CLASS_ENT(CompileSym2Flags, SecurityChecks), + CV_ENUM_CLASS_ENT(CompileSym2Flags, HotPatch), + CV_ENUM_CLASS_ENT(CompileSym2Flags, CVTCIL), + CV_ENUM_CLASS_ENT(CompileSym2Flags, MSILModule), +}; + +static const EnumEntry<uint32_t> CompileSym3FlagNames[] = { + CV_ENUM_CLASS_ENT(CompileSym3Flags, EC), + CV_ENUM_CLASS_ENT(CompileSym3Flags, NoDbgInfo), + CV_ENUM_CLASS_ENT(CompileSym3Flags, LTCG), + CV_ENUM_CLASS_ENT(CompileSym3Flags, NoDataAlign), + CV_ENUM_CLASS_ENT(CompileSym3Flags, ManagedPresent), + CV_ENUM_CLASS_ENT(CompileSym3Flags, SecurityChecks), + CV_ENUM_CLASS_ENT(CompileSym3Flags, HotPatch), + CV_ENUM_CLASS_ENT(CompileSym3Flags, CVTCIL), + CV_ENUM_CLASS_ENT(CompileSym3Flags, MSILModule), + CV_ENUM_CLASS_ENT(CompileSym3Flags, Sdl), + CV_ENUM_CLASS_ENT(CompileSym3Flags, PGO), + CV_ENUM_CLASS_ENT(CompileSym3Flags, Exp), +}; + +static const EnumEntry<uint32_t> FileChecksumNames[] = { + CV_ENUM_CLASS_ENT(FileChecksumKind, None), + CV_ENUM_CLASS_ENT(FileChecksumKind, MD5), + CV_ENUM_CLASS_ENT(FileChecksumKind, SHA1), + CV_ENUM_CLASS_ENT(FileChecksumKind, SHA256), +}; + +static const EnumEntry<unsigned> CPUTypeNames[] = { + CV_ENUM_CLASS_ENT(CPUType, Intel8080), + CV_ENUM_CLASS_ENT(CPUType, Intel8086), + CV_ENUM_CLASS_ENT(CPUType, Intel80286), + CV_ENUM_CLASS_ENT(CPUType, Intel80386), + CV_ENUM_CLASS_ENT(CPUType, Intel80486), + CV_ENUM_CLASS_ENT(CPUType, Pentium), + CV_ENUM_CLASS_ENT(CPUType, PentiumPro), + CV_ENUM_CLASS_ENT(CPUType, Pentium3), + CV_ENUM_CLASS_ENT(CPUType, MIPS), + CV_ENUM_CLASS_ENT(CPUType, MIPS16), + CV_ENUM_CLASS_ENT(CPUType, MIPS32), + CV_ENUM_CLASS_ENT(CPUType, MIPS64), + CV_ENUM_CLASS_ENT(CPUType, MIPSI), + CV_ENUM_CLASS_ENT(CPUType, MIPSII), + CV_ENUM_CLASS_ENT(CPUType, MIPSIII), + CV_ENUM_CLASS_ENT(CPUType, MIPSIV), + CV_ENUM_CLASS_ENT(CPUType, MIPSV), + CV_ENUM_CLASS_ENT(CPUType, M68000), + CV_ENUM_CLASS_ENT(CPUType, M68010), + CV_ENUM_CLASS_ENT(CPUType, M68020), + CV_ENUM_CLASS_ENT(CPUType, M68030), + CV_ENUM_CLASS_ENT(CPUType, M68040), + CV_ENUM_CLASS_ENT(CPUType, Alpha), + CV_ENUM_CLASS_ENT(CPUType, Alpha21164), + CV_ENUM_CLASS_ENT(CPUType, Alpha21164A), + CV_ENUM_CLASS_ENT(CPUType, Alpha21264), + CV_ENUM_CLASS_ENT(CPUType, Alpha21364), + CV_ENUM_CLASS_ENT(CPUType, PPC601), + CV_ENUM_CLASS_ENT(CPUType, PPC603), + CV_ENUM_CLASS_ENT(CPUType, PPC604), + CV_ENUM_CLASS_ENT(CPUType, PPC620), + CV_ENUM_CLASS_ENT(CPUType, PPCFP), + CV_ENUM_CLASS_ENT(CPUType, PPCBE), + CV_ENUM_CLASS_ENT(CPUType, SH3), + CV_ENUM_CLASS_ENT(CPUType, SH3E), + CV_ENUM_CLASS_ENT(CPUType, SH3DSP), + CV_ENUM_CLASS_ENT(CPUType, SH4), + CV_ENUM_CLASS_ENT(CPUType, SHMedia), + CV_ENUM_CLASS_ENT(CPUType, ARM3), + CV_ENUM_CLASS_ENT(CPUType, ARM4), + CV_ENUM_CLASS_ENT(CPUType, ARM4T), + CV_ENUM_CLASS_ENT(CPUType, ARM5), + CV_ENUM_CLASS_ENT(CPUType, ARM5T), + CV_ENUM_CLASS_ENT(CPUType, ARM6), + CV_ENUM_CLASS_ENT(CPUType, ARM_XMAC), + CV_ENUM_CLASS_ENT(CPUType, ARM_WMMX), + CV_ENUM_CLASS_ENT(CPUType, ARM7), + CV_ENUM_CLASS_ENT(CPUType, ARM64), + CV_ENUM_CLASS_ENT(CPUType, Omni), + CV_ENUM_CLASS_ENT(CPUType, Ia64), + CV_ENUM_CLASS_ENT(CPUType, Ia64_2), + CV_ENUM_CLASS_ENT(CPUType, CEE), + CV_ENUM_CLASS_ENT(CPUType, AM33), + CV_ENUM_CLASS_ENT(CPUType, M32R), + CV_ENUM_CLASS_ENT(CPUType, TriCore), + CV_ENUM_CLASS_ENT(CPUType, X64), + CV_ENUM_CLASS_ENT(CPUType, EBC), + CV_ENUM_CLASS_ENT(CPUType, Thumb), + CV_ENUM_CLASS_ENT(CPUType, ARMNT), + CV_ENUM_CLASS_ENT(CPUType, D3D11_Shader), +}; + +static const EnumEntry<uint32_t> FrameProcSymFlagNames[] = { + CV_ENUM_CLASS_ENT(FrameProcedureOptions, HasAlloca), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, HasSetJmp), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, HasLongJmp), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, HasInlineAssembly), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, HasExceptionHandling), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, MarkedInline), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, HasStructuredExceptionHandling), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, Naked), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, SecurityChecks), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, AsynchronousExceptionHandling), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, NoStackOrderingForSecurityChecks), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, Inlined), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, StrictSecurityChecks), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, SafeBuffers), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, EncodedLocalBasePointerMask), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, EncodedParamBasePointerMask), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, ProfileGuidedOptimization), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, ValidProfileCounts), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, OptimizedForSpeed), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, GuardCfg), + CV_ENUM_CLASS_ENT(FrameProcedureOptions, GuardCfw), +}; + +static const EnumEntry<uint32_t> ModuleSubstreamKindNames[] = { + CV_ENUM_CLASS_ENT(DebugSubsectionKind, None), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, Symbols), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, Lines), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, StringTable), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, FileChecksums), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, FrameData), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, InlineeLines), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, CrossScopeImports), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, CrossScopeExports), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, ILLines), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, FuncMDTokenMap), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, TypeMDTokenMap), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, MergedAssemblyInput), + CV_ENUM_CLASS_ENT(DebugSubsectionKind, CoffSymbolRVA), +}; + +static const EnumEntry<uint16_t> ExportSymFlagNames[] = { + CV_ENUM_CLASS_ENT(ExportFlags, IsConstant), + CV_ENUM_CLASS_ENT(ExportFlags, IsData), + CV_ENUM_CLASS_ENT(ExportFlags, IsPrivate), + CV_ENUM_CLASS_ENT(ExportFlags, HasNoName), + CV_ENUM_CLASS_ENT(ExportFlags, HasExplicitOrdinal), + CV_ENUM_CLASS_ENT(ExportFlags, IsForwarder), +}; + +static const EnumEntry<uint8_t> ThunkOrdinalNames[] = { + CV_ENUM_CLASS_ENT(ThunkOrdinal, Standard), + CV_ENUM_CLASS_ENT(ThunkOrdinal, ThisAdjustor), + CV_ENUM_CLASS_ENT(ThunkOrdinal, Vcall), + CV_ENUM_CLASS_ENT(ThunkOrdinal, Pcode), + CV_ENUM_CLASS_ENT(ThunkOrdinal, UnknownLoad), + CV_ENUM_CLASS_ENT(ThunkOrdinal, TrampIncremental), + CV_ENUM_CLASS_ENT(ThunkOrdinal, BranchIsland), +}; + +static const EnumEntry<uint16_t> TrampolineNames[] = { + CV_ENUM_CLASS_ENT(TrampolineType, TrampIncremental), + CV_ENUM_CLASS_ENT(TrampolineType, BranchIsland), +}; + +static const EnumEntry<COFF::SectionCharacteristics> + ImageSectionCharacteristicNames[] = { + CV_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD), + CV_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NO_PAD), + CV_ENUM_ENT(COFF, IMAGE_SCN_CNT_CODE), + CV_ENUM_ENT(COFF, IMAGE_SCN_CNT_INITIALIZED_DATA), + CV_ENUM_ENT(COFF, IMAGE_SCN_CNT_UNINITIALIZED_DATA), + CV_ENUM_ENT(COFF, IMAGE_SCN_LNK_OTHER), + CV_ENUM_ENT(COFF, IMAGE_SCN_LNK_INFO), + CV_ENUM_ENT(COFF, IMAGE_SCN_LNK_REMOVE), + CV_ENUM_ENT(COFF, IMAGE_SCN_LNK_COMDAT), + CV_ENUM_ENT(COFF, IMAGE_SCN_GPREL), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_PURGEABLE), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_16BIT), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_LOCKED), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_PRELOAD), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_1BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_2BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_4BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_8BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_16BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_32BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_64BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_128BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_256BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_512BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_1024BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_2048BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_4096BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_8192BYTES), + CV_ENUM_ENT(COFF, IMAGE_SCN_LNK_NRELOC_OVFL), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_DISCARDABLE), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_NOT_CACHED), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_NOT_PAGED), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_SHARED), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_EXECUTE), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_READ), + CV_ENUM_ENT(COFF, IMAGE_SCN_MEM_WRITE)}; + +static const EnumEntry<uint16_t> ClassOptionNames[] = { + CV_ENUM_CLASS_ENT(ClassOptions, Packed), + CV_ENUM_CLASS_ENT(ClassOptions, HasConstructorOrDestructor), + CV_ENUM_CLASS_ENT(ClassOptions, HasOverloadedOperator), + CV_ENUM_CLASS_ENT(ClassOptions, Nested), + CV_ENUM_CLASS_ENT(ClassOptions, ContainsNestedClass), + CV_ENUM_CLASS_ENT(ClassOptions, HasOverloadedAssignmentOperator), + CV_ENUM_CLASS_ENT(ClassOptions, HasConversionOperator), + CV_ENUM_CLASS_ENT(ClassOptions, ForwardReference), + CV_ENUM_CLASS_ENT(ClassOptions, Scoped), + CV_ENUM_CLASS_ENT(ClassOptions, HasUniqueName), + CV_ENUM_CLASS_ENT(ClassOptions, Sealed), + CV_ENUM_CLASS_ENT(ClassOptions, Intrinsic), +}; + +static const EnumEntry<uint8_t> MemberAccessNames[] = { + CV_ENUM_CLASS_ENT(MemberAccess, None), + CV_ENUM_CLASS_ENT(MemberAccess, Private), + CV_ENUM_CLASS_ENT(MemberAccess, Protected), + CV_ENUM_CLASS_ENT(MemberAccess, Public), +}; + +static const EnumEntry<uint16_t> MethodOptionNames[] = { + CV_ENUM_CLASS_ENT(MethodOptions, Pseudo), + CV_ENUM_CLASS_ENT(MethodOptions, NoInherit), + CV_ENUM_CLASS_ENT(MethodOptions, NoConstruct), + CV_ENUM_CLASS_ENT(MethodOptions, CompilerGenerated), + CV_ENUM_CLASS_ENT(MethodOptions, Sealed), +}; + +static const EnumEntry<uint16_t> MemberKindNames[] = { + CV_ENUM_CLASS_ENT(MethodKind, Vanilla), + CV_ENUM_CLASS_ENT(MethodKind, Virtual), + CV_ENUM_CLASS_ENT(MethodKind, Static), + CV_ENUM_CLASS_ENT(MethodKind, Friend), + CV_ENUM_CLASS_ENT(MethodKind, IntroducingVirtual), + CV_ENUM_CLASS_ENT(MethodKind, PureVirtual), + CV_ENUM_CLASS_ENT(MethodKind, PureIntroducingVirtual), +}; + +static const EnumEntry<uint8_t> PtrKindNames[] = { + CV_ENUM_CLASS_ENT(PointerKind, Near16), + CV_ENUM_CLASS_ENT(PointerKind, Far16), + CV_ENUM_CLASS_ENT(PointerKind, Huge16), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnSegment), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnValue), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnSegmentValue), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnAddress), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnSegmentAddress), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnType), + CV_ENUM_CLASS_ENT(PointerKind, BasedOnSelf), + CV_ENUM_CLASS_ENT(PointerKind, Near32), + CV_ENUM_CLASS_ENT(PointerKind, Far32), + CV_ENUM_CLASS_ENT(PointerKind, Near64), +}; + +static const EnumEntry<uint8_t> PtrModeNames[] = { + CV_ENUM_CLASS_ENT(PointerMode, Pointer), + CV_ENUM_CLASS_ENT(PointerMode, LValueReference), + CV_ENUM_CLASS_ENT(PointerMode, PointerToDataMember), + CV_ENUM_CLASS_ENT(PointerMode, PointerToMemberFunction), + CV_ENUM_CLASS_ENT(PointerMode, RValueReference), +}; + +static const EnumEntry<uint16_t> PtrMemberRepNames[] = { + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, Unknown), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, SingleInheritanceData), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, MultipleInheritanceData), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, VirtualInheritanceData), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, GeneralData), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, SingleInheritanceFunction), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, + MultipleInheritanceFunction), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, + VirtualInheritanceFunction), + CV_ENUM_CLASS_ENT(PointerToMemberRepresentation, GeneralFunction), +}; + +static const EnumEntry<uint16_t> TypeModifierNames[] = { + CV_ENUM_CLASS_ENT(ModifierOptions, Const), + CV_ENUM_CLASS_ENT(ModifierOptions, Volatile), + CV_ENUM_CLASS_ENT(ModifierOptions, Unaligned), +}; + +static const EnumEntry<uint8_t> CallingConventions[] = { + CV_ENUM_CLASS_ENT(CallingConvention, NearC), + CV_ENUM_CLASS_ENT(CallingConvention, FarC), + CV_ENUM_CLASS_ENT(CallingConvention, NearPascal), + CV_ENUM_CLASS_ENT(CallingConvention, FarPascal), + CV_ENUM_CLASS_ENT(CallingConvention, NearFast), + CV_ENUM_CLASS_ENT(CallingConvention, FarFast), + CV_ENUM_CLASS_ENT(CallingConvention, NearStdCall), + CV_ENUM_CLASS_ENT(CallingConvention, FarStdCall), + CV_ENUM_CLASS_ENT(CallingConvention, NearSysCall), + CV_ENUM_CLASS_ENT(CallingConvention, FarSysCall), + CV_ENUM_CLASS_ENT(CallingConvention, ThisCall), + CV_ENUM_CLASS_ENT(CallingConvention, MipsCall), + CV_ENUM_CLASS_ENT(CallingConvention, Generic), + CV_ENUM_CLASS_ENT(CallingConvention, AlphaCall), + CV_ENUM_CLASS_ENT(CallingConvention, PpcCall), + CV_ENUM_CLASS_ENT(CallingConvention, SHCall), + CV_ENUM_CLASS_ENT(CallingConvention, ArmCall), + CV_ENUM_CLASS_ENT(CallingConvention, AM33Call), + CV_ENUM_CLASS_ENT(CallingConvention, TriCall), + CV_ENUM_CLASS_ENT(CallingConvention, SH5Call), + CV_ENUM_CLASS_ENT(CallingConvention, M32RCall), + CV_ENUM_CLASS_ENT(CallingConvention, ClrCall), + CV_ENUM_CLASS_ENT(CallingConvention, Inline), + CV_ENUM_CLASS_ENT(CallingConvention, NearVector), +}; + +static const EnumEntry<uint8_t> FunctionOptionEnum[] = { + CV_ENUM_CLASS_ENT(FunctionOptions, CxxReturnUdt), + CV_ENUM_CLASS_ENT(FunctionOptions, Constructor), + CV_ENUM_CLASS_ENT(FunctionOptions, ConstructorWithVirtualBases), +}; + +static const EnumEntry<uint16_t> LabelTypeEnum[] = { + CV_ENUM_CLASS_ENT(LabelType, Near), + CV_ENUM_CLASS_ENT(LabelType, Far), +}; + +namespace llvm { +namespace codeview { + +ArrayRef<EnumEntry<SymbolKind>> getSymbolTypeNames() { + return makeArrayRef(SymbolTypeNames); +} + +ArrayRef<EnumEntry<TypeLeafKind>> getTypeLeafNames() { + return makeArrayRef(TypeLeafNames); +} + +ArrayRef<EnumEntry<uint16_t>> getRegisterNames(CPUType Cpu) { + if (Cpu == CPUType::ARM64) { + return makeArrayRef(RegisterNames_ARM64); + } + return makeArrayRef(RegisterNames_X86); +} + +ArrayRef<EnumEntry<uint32_t>> getPublicSymFlagNames() { + return makeArrayRef(PublicSymFlagNames); +} + +ArrayRef<EnumEntry<uint8_t>> getProcSymFlagNames() { + return makeArrayRef(ProcSymFlagNames); +} + +ArrayRef<EnumEntry<uint16_t>> getLocalFlagNames() { + return makeArrayRef(LocalFlags); +} + +ArrayRef<EnumEntry<uint8_t>> getFrameCookieKindNames() { + return makeArrayRef(FrameCookieKinds); +} + +ArrayRef<EnumEntry<SourceLanguage>> getSourceLanguageNames() { + return makeArrayRef(SourceLanguages); +} + +ArrayRef<EnumEntry<uint32_t>> getCompileSym2FlagNames() { + return makeArrayRef(CompileSym2FlagNames); +} + +ArrayRef<EnumEntry<uint32_t>> getCompileSym3FlagNames() { + return makeArrayRef(CompileSym3FlagNames); +} + +ArrayRef<EnumEntry<uint32_t>> getFileChecksumNames() { + return makeArrayRef(FileChecksumNames); +} + +ArrayRef<EnumEntry<unsigned>> getCPUTypeNames() { + return makeArrayRef(CPUTypeNames); +} + +ArrayRef<EnumEntry<uint32_t>> getFrameProcSymFlagNames() { + return makeArrayRef(FrameProcSymFlagNames); +} + +ArrayRef<EnumEntry<uint16_t>> getExportSymFlagNames() { + return makeArrayRef(ExportSymFlagNames); +} + +ArrayRef<EnumEntry<uint32_t>> getModuleSubstreamKindNames() { + return makeArrayRef(ModuleSubstreamKindNames); +} + +ArrayRef<EnumEntry<uint8_t>> getThunkOrdinalNames() { + return makeArrayRef(ThunkOrdinalNames); +} + +ArrayRef<EnumEntry<uint16_t>> getTrampolineNames() { + return makeArrayRef(TrampolineNames); +} + +ArrayRef<EnumEntry<COFF::SectionCharacteristics>> +getImageSectionCharacteristicNames() { + return makeArrayRef(ImageSectionCharacteristicNames); +} + +ArrayRef<EnumEntry<uint16_t>> getClassOptionNames() { + return makeArrayRef(ClassOptionNames); +} + +ArrayRef<EnumEntry<uint8_t>> getMemberAccessNames() { + return makeArrayRef(MemberAccessNames); +} + +ArrayRef<EnumEntry<uint16_t>> getMethodOptionNames() { + return makeArrayRef(MethodOptionNames); +} + +ArrayRef<EnumEntry<uint16_t>> getMemberKindNames() { + return makeArrayRef(MemberKindNames); +} + +ArrayRef<EnumEntry<uint8_t>> getPtrKindNames() { + return makeArrayRef(PtrKindNames); +} + +ArrayRef<EnumEntry<uint8_t>> getPtrModeNames() { + return makeArrayRef(PtrModeNames); +} + +ArrayRef<EnumEntry<uint16_t>> getPtrMemberRepNames() { + return makeArrayRef(PtrMemberRepNames); +} + +ArrayRef<EnumEntry<uint16_t>> getTypeModifierNames() { + return makeArrayRef(TypeModifierNames); +} + +ArrayRef<EnumEntry<uint8_t>> getCallingConventions() { + return makeArrayRef(CallingConventions); +} + +ArrayRef<EnumEntry<uint8_t>> getFunctionOptionEnum() { + return makeArrayRef(FunctionOptionEnum); +} + +ArrayRef<EnumEntry<uint16_t>> getLabelTypeEnum() { + return makeArrayRef(LabelTypeEnum); +} + +} // end namespace codeview +} // end namespace llvm diff --git a/llvm/lib/DebugInfo/CodeView/Formatters.cpp b/llvm/lib/DebugInfo/CodeView/Formatters.cpp new file mode 100644 index 000000000000..a7a8c7ff82bf --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/Formatters.cpp @@ -0,0 +1,47 @@ +//===- Formatters.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/GUID.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::codeview::detail; + +GuidAdapter::GuidAdapter(StringRef Guid) + : FormatAdapter(makeArrayRef(Guid.bytes_begin(), Guid.bytes_end())) {} + +GuidAdapter::GuidAdapter(ArrayRef<uint8_t> Guid) + : FormatAdapter(std::move(Guid)) {} + +void GuidAdapter::format(raw_ostream &Stream, StringRef Style) { + static const char *Lookup = "0123456789ABCDEF"; + + assert(Item.size() == 16 && "Expected 16-byte GUID"); + Stream << "{"; + for (int i = 0; i < 16;) { + uint8_t Byte = Item[i]; + uint8_t HighNibble = (Byte >> 4) & 0xF; + uint8_t LowNibble = Byte & 0xF; + Stream << Lookup[HighNibble] << Lookup[LowNibble]; + ++i; + if (i >= 4 && i <= 10 && i % 2 == 0) + Stream << "-"; + } + Stream << "}"; +} + +raw_ostream &llvm::codeview::operator<<(raw_ostream &OS, const GUID &Guid) { + codeview::detail::GuidAdapter A(Guid.Guid); + A.format(OS, ""); + return OS; +} diff --git a/llvm/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp b/llvm/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp new file mode 100644 index 000000000000..a7ad1d045f04 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/GlobalTypeTableBuilder.cpp @@ -0,0 +1,106 @@ +//===- GlobalTypeTableBuilder.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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(SeenRecords[Index.toArrayIndex()]); + 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(); +} + +TypeIndex GlobalTypeTableBuilder::insertRecordBytes(ArrayRef<uint8_t> Record) { + GloballyHashedType GHT = + GloballyHashedType::hashType(Record, SeenHashes, SeenHashes); + return insertRecordAs(GHT, Record.size(), + [Record](MutableArrayRef<uint8_t> Data) { + assert(Data.size() == Record.size()); + ::memcpy(Data.data(), Record.data(), Record.size()); + return Data; + }); +} + +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/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp new file mode 100644 index 000000000000..dc1253b7a39f --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -0,0 +1,279 @@ +//===- LazyRandomTypeCollection.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <iterator> + +using namespace llvm; +using namespace llvm::codeview; + +static void error(Error &&EC) { + assert(!static_cast<bool>(EC)); + if (EC) + consumeError(std::move(EC)); +} + +LazyRandomTypeCollection::LazyRandomTypeCollection(uint32_t RecordCountHint) + : LazyRandomTypeCollection(CVTypeArray(), RecordCountHint, + PartialOffsetArray()) {} + +LazyRandomTypeCollection::LazyRandomTypeCollection( + const CVTypeArray &Types, uint32_t RecordCountHint, + PartialOffsetArray PartialOffsets) + : NameStorage(Allocator), Types(Types), PartialOffsets(PartialOffsets) { + Records.resize(RecordCountHint); +} + +LazyRandomTypeCollection::LazyRandomTypeCollection(ArrayRef<uint8_t> Data, + uint32_t RecordCountHint) + : LazyRandomTypeCollection(RecordCountHint) { +} + +LazyRandomTypeCollection::LazyRandomTypeCollection(StringRef Data, + uint32_t RecordCountHint) + : LazyRandomTypeCollection( + makeArrayRef(Data.bytes_begin(), Data.bytes_end()), RecordCountHint) { +} + +LazyRandomTypeCollection::LazyRandomTypeCollection(const CVTypeArray &Types, + uint32_t NumRecords) + : LazyRandomTypeCollection(Types, NumRecords, PartialOffsetArray()) {} + +void LazyRandomTypeCollection::reset(BinaryStreamReader &Reader, + uint32_t RecordCountHint) { + Count = 0; + PartialOffsets = PartialOffsetArray(); + + 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) { + BinaryStreamReader Reader(Data, support::little); + reset(Reader, RecordCountHint); +} + +uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index) { + error(ensureTypeExists(Index)); + assert(contains(Index)); + + return Records[Index.toArrayIndex()].Offset; +} + +CVType LazyRandomTypeCollection::getType(TypeIndex Index) { + assert(!Index.isSimple()); + + auto EC = ensureTypeExists(Index); + error(std::move(EC)); + assert(contains(Index)); + + return Records[Index.toArrayIndex()].Type; +} + +Optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) { + if (Index.isSimple()) + return None; + + 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); + + // Try to make sure the type exists. Even if it doesn't though, it may be + // because we're dumping a symbol stream with no corresponding type stream + // present, in which case we still want to be able to print <unknown UDT> + // for the type names. + if (auto EC = ensureTypeExists(Index)) { + consumeError(std::move(EC)); + return "<unknown UDT>"; + } + + uint32_t I = Index.toArrayIndex(); + ensureCapacityFor(Index); + if (Records[I].Name.data() == nullptr) { + StringRef Result = NameStorage.save(computeTypeName(*this, Index)); + Records[I].Name = Result; + } + return Records[I].Name; +} + +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()) + return false; + return true; +} + +uint32_t LazyRandomTypeCollection::size() { return Count; } + +uint32_t LazyRandomTypeCollection::capacity() { return Records.size(); } + +Error LazyRandomTypeCollection::ensureTypeExists(TypeIndex TI) { + if (contains(TI)) + return Error::success(); + + return visitRangeForType(TI); +} + +void LazyRandomTypeCollection::ensureCapacityFor(TypeIndex Index) { + assert(!Index.isSimple()); + uint32_t MinSize = Index.toArrayIndex() + 1; + + if (MinSize <= capacity()) + return; + + uint32_t NewCapacity = MinSize * 3 / 2; + + assert(NewCapacity > capacity()); + Records.resize(NewCapacity); +} + +Error LazyRandomTypeCollection::visitRangeForType(TypeIndex TI) { + assert(!TI.isSimple()); + if (PartialOffsets.empty()) + return fullScanForType(TI); + + auto Next = std::upper_bound(PartialOffsets.begin(), PartialOffsets.end(), TI, + [](TypeIndex Value, const TypeIndexOffset &IO) { + return Value < IO.Type; + }); + + assert(Next != PartialOffsets.begin()); + auto Prev = std::prev(Next); + + TypeIndex TIB = Prev->Type; + if (contains(TIB)) { + // They've asked us to fetch a type index, but the entry we found in the + // partial offsets array has already been visited. Since we visit an entire + // block every time, that means this record should have been previously + // discovered. Ultimately, this means this is a request for a non-existant + // type index. + return make_error<CodeViewError>("Invalid type index"); + } + + TypeIndex TIE; + if (Next == PartialOffsets.end()) { + TIE = TypeIndex::fromArrayIndex(capacity()); + } else { + TIE = Next->Type; + } + + visitRange(TIB, Prev->Offset, TIE); + return Error::success(); +} + +Optional<TypeIndex> LazyRandomTypeCollection::getFirst() { + TypeIndex TI = TypeIndex::fromArrayIndex(0); + if (auto EC = ensureTypeExists(TI)) { + consumeError(std::move(EC)); + return None; + } + return TI; +} + +Optional<TypeIndex> LazyRandomTypeCollection::getNext(TypeIndex Prev) { + // We can't be sure how long this type stream is, given that the initial count + // given to the constructor is just a hint. So just try to make sure the next + // record exists, and if anything goes wrong, we must be at the end. + if (auto EC = ensureTypeExists(Prev + 1)) { + consumeError(std::move(EC)); + return None; + } + + return Prev + 1; +} + +Error LazyRandomTypeCollection::fullScanForType(TypeIndex TI) { + assert(!TI.isSimple()); + assert(PartialOffsets.empty()); + + TypeIndex CurrentTI = TypeIndex::fromArrayIndex(0); + auto Begin = Types.begin(); + + if (Count > 0) { + // In the case of type streams which we don't know the number of records of, + // it's possible to search for a type index triggering a full scan, but then + // later additional records are added since we didn't know how many there + // would be until we did a full visitation, then you try to access the new + // type triggering another full scan. To avoid this, we assume that if the + // database has some records, this must be what's going on. We can also + // assume that this index must be larger than the largest type index we've + // visited, so we start from there and scan forward. + uint32_t Offset = Records[LargestTypeIndex.toArrayIndex()].Offset; + CurrentTI = LargestTypeIndex + 1; + Begin = Types.at(Offset); + ++Begin; + } + + auto End = Types.end(); + while (Begin != End) { + ensureCapacityFor(CurrentTI); + LargestTypeIndex = std::max(LargestTypeIndex, CurrentTI); + auto Idx = CurrentTI.toArrayIndex(); + Records[Idx].Type = *Begin; + Records[Idx].Offset = Begin.offset(); + ++Count; + ++Begin; + ++CurrentTI; + } + if (CurrentTI <= TI) { + return make_error<CodeViewError>("Type Index does not exist!"); + } + return Error::success(); +} + +void LazyRandomTypeCollection::visitRange(TypeIndex Begin, uint32_t BeginOffset, + TypeIndex End) { + auto RI = Types.at(BeginOffset); + assert(RI != Types.end()); + + ensureCapacityFor(End); + while (Begin != End) { + LargestTypeIndex = std::max(LargestTypeIndex, Begin); + auto Idx = Begin.toArrayIndex(); + Records[Idx].Type = *RI; + Records[Idx].Offset = RI.offset(); + ++Count; + ++Begin; + ++RI; + } +} diff --git a/llvm/lib/DebugInfo/CodeView/Line.cpp b/llvm/lib/DebugInfo/CodeView/Line.cpp new file mode 100644 index 000000000000..53adc8cac511 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/Line.cpp @@ -0,0 +1,21 @@ +//===-- Line.cpp ----------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/Line.h" + +using namespace llvm; +using namespace codeview; + +LineInfo::LineInfo(uint32_t StartLine, uint32_t EndLine, bool IsStatement) { + LineData = StartLine & StartLineMask; + uint32_t LineDelta = EndLine - StartLine; + LineData |= (LineDelta << EndLineDeltaShift) & EndLineDeltaMask; + if (IsStatement) { + LineData |= StatementFlag; + } +} diff --git a/llvm/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp b/llvm/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp new file mode 100644 index 000000000000..4d7cd468f3ee --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/MergingTypeTableBuilder.cpp @@ -0,0 +1,123 @@ +//===- MergingTypeTableBuilder.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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(SeenRecords[Index.toArrayIndex()]); + 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/llvm/lib/DebugInfo/CodeView/RecordName.cpp b/llvm/lib/DebugInfo/CodeView/RecordName.cpp new file mode 100644 index 000000000000..cfaad1581159 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/RecordName.cpp @@ -0,0 +1,337 @@ +//===- RecordName.cpp ----------------------------------------- *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +class TypeNameComputer : public TypeVisitorCallbacks { + /// The type collection. Used to calculate names of nested types. + TypeCollection &Types; + TypeIndex CurrentTypeIndex = TypeIndex::None(); + + /// Name of the current type. Only valid before visitTypeEnd. + SmallString<256> Name; + +public: + explicit TypeNameComputer(TypeCollection &Types) : Types(Types) {} + + StringRef name() const { return Name; } + + /// Paired begin/end actions for all types. Receives all record data, + /// including the fixed-length record prefix. + Error visitTypeBegin(CVType &Record) override; + Error visitTypeBegin(CVType &Record, TypeIndex Index) override; + Error visitTypeEnd(CVType &Record) override; + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownRecord(CVType &CVR, Name##Record &Record) override; +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; +} // namespace + +Error TypeNameComputer::visitTypeBegin(CVType &Record) { + llvm_unreachable("Must call visitTypeBegin with a TypeIndex!"); + return Error::success(); +} + +Error TypeNameComputer::visitTypeBegin(CVType &Record, TypeIndex Index) { + // Reset Name to the empty string. If the visitor sets it, we know it. + Name = ""; + CurrentTypeIndex = Index; + return Error::success(); +} + +Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); } + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + FieldListRecord &FieldList) { + Name = "<field list>"; + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR, + StringIdRecord &String) { + Name = String.getString(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArgListRecord &Args) { + auto Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + Name = "("; + for (uint32_t I = 0; I < Size; ++I) { + assert(Indices[I] < CurrentTypeIndex); + + Name.append(Types.getTypeName(Indices[I])); + if (I + 1 != Size) + Name.append(", "); + } + Name.push_back(')'); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + StringListRecord &Strings) { + auto Indices = Strings.getIndices(); + uint32_t Size = Indices.size(); + Name = "\""; + for (uint32_t I = 0; I < Size; ++I) { + Name.append(Types.getTypeName(Indices[I])); + if (I + 1 != Size) + Name.append("\" \""); + } + Name.push_back('\"'); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) { + Name = Class.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) { + Name = Union.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { + Name = Enum.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { + Name = AT.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) { + Name = VFT.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) { + Name = Id.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) { + StringRef Ret = Types.getTypeName(Proc.getReturnType()); + StringRef Params = Types.getTypeName(Proc.getArgumentList()); + Name = formatv("{0} {1}", Ret, Params).sstr<256>(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + MemberFunctionRecord &MF) { + StringRef Ret = Types.getTypeName(MF.getReturnType()); + StringRef Class = Types.getTypeName(MF.getClassType()); + StringRef Params = Types.getTypeName(MF.getArgumentList()); + Name = formatv("{0} {1}::{2}", Ret, Class, Params).sstr<256>(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) { + Name = Func.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) { + Name = TS.getName(); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) { + + if (Ptr.isPointerToMember()) { + const MemberPointerInfo &MI = Ptr.getMemberInfo(); + + StringRef Pointee = Types.getTypeName(Ptr.getReferentType()); + StringRef Class = Types.getTypeName(MI.getContainingType()); + Name = formatv("{0} {1}::*", Pointee, Class); + } else { + Name.append(Types.getTypeName(Ptr.getReferentType())); + + if (Ptr.getMode() == PointerMode::LValueReference) + Name.append("&"); + else if (Ptr.getMode() == PointerMode::RValueReference) + Name.append("&&"); + else if (Ptr.getMode() == PointerMode::Pointer) + Name.append("*"); + + // Qualifiers in pointer records apply to the pointer, not the pointee, so + // they go on the right. + if (Ptr.isConst()) + Name.append(" const"); + if (Ptr.isVolatile()) + Name.append(" volatile"); + if (Ptr.isUnaligned()) + Name.append(" __unaligned"); + if (Ptr.isRestrict()) + Name.append(" __restrict"); + } + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) { + uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); + + if (Mods & uint16_t(ModifierOptions::Const)) + Name.append("const "); + if (Mods & uint16_t(ModifierOptions::Volatile)) + Name.append("volatile "); + if (Mods & uint16_t(ModifierOptions::Unaligned)) + Name.append("__unaligned "); + Name.append(Types.getTypeName(Mod.getModifiedType())); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Shape) { + Name = formatv("<vftable {0} methods>", Shape.getEntryCount()); + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord( + CVType &CVR, UdtModSourceLineRecord &ModSourceLine) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &SourceLine) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + MethodOverloadListRecord &Overloads) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + PrecompRecord &Precomp) { + return Error::success(); +} + +Error TypeNameComputer::visitKnownRecord(CVType &CVR, + EndPrecompRecord &EndPrecomp) { + return Error::success(); +} + +std::string llvm::codeview::computeTypeName(TypeCollection &Types, + TypeIndex Index) { + TypeNameComputer Computer(Types); + CVType Record = Types.getType(Index); + if (auto EC = visitTypeRecord(Record, Index, Computer)) { + consumeError(std::move(EC)); + return "<unknown UDT>"; + } + 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: + case SymbolKind::S_PROCREF: + case SymbolKind::S_LPROCREF: + 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; + // See UsingNamespaceSym + case SymbolKind::S_UNAMESPACE: + return 0; + 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/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp b/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp new file mode 100644 index 000000000000..e7f032f9c670 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp @@ -0,0 +1,154 @@ +//===-- RecordSerialization.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Utilities for serializing and deserializing CodeView records. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#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" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::support; + +/// Reinterpret a byte array as an array of characters. Does not interpret as +/// a C string, as StringRef has several helpers (split) that make that easy. +StringRef llvm::codeview::getBytesAsCharacters(ArrayRef<uint8_t> LeafData) { + return StringRef(reinterpret_cast<const char *>(LeafData.data()), + LeafData.size()); +} + +StringRef llvm::codeview::getBytesAsCString(ArrayRef<uint8_t> LeafData) { + return getBytesAsCharacters(LeafData).split('\0').first; +} + +Error llvm::codeview::consume(BinaryStreamReader &Reader, APSInt &Num) { + // Used to avoid overload ambiguity on APInt construtor. + bool FalseVal = false; + uint16_t Short; + if (auto EC = Reader.readInteger(Short)) + return EC; + + if (Short < LF_NUMERIC) { + Num = APSInt(APInt(/*numBits=*/16, Short, /*isSigned=*/false), + /*isUnsigned=*/true); + return Error::success(); + } + + switch (Short) { + case LF_CHAR: { + int8_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(8, N, true), false); + return Error::success(); + } + case LF_SHORT: { + int16_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(16, N, true), false); + return Error::success(); + } + case LF_USHORT: { + uint16_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(16, N, false), true); + return Error::success(); + } + case LF_LONG: { + int32_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(32, N, true), false); + return Error::success(); + } + case LF_ULONG: { + uint32_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(32, N, FalseVal), true); + return Error::success(); + } + case LF_QUADWORD: { + int64_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(64, N, true), false); + return Error::success(); + } + case LF_UQUADWORD: { + uint64_t N; + if (auto EC = Reader.readInteger(N)) + return EC; + Num = APSInt(APInt(64, N, false), true); + return Error::success(); + } + } + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Buffer contains invalid APSInt type"); +} + +Error llvm::codeview::consume(StringRef &Data, APSInt &Num) { + ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end()); + BinaryByteStream S(Bytes, llvm::support::little); + BinaryStreamReader SR(S); + auto EC = consume(SR, Num); + Data = Data.take_back(SR.bytesRemaining()); + return EC; +} + +/// Decode a numeric leaf value that is known to be a uint64_t. +Error llvm::codeview::consume_numeric(BinaryStreamReader &Reader, + uint64_t &Num) { + APSInt N; + if (auto EC = consume(Reader, N)) + return EC; + if (N.isSigned() || !N.isIntN(64)) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Data is not a numeric value!"); + Num = N.getLimitedValue(); + return Error::success(); +} + +Error llvm::codeview::consume(BinaryStreamReader &Reader, uint32_t &Item) { + return Reader.readInteger(Item); +} + +Error llvm::codeview::consume(StringRef &Data, uint32_t &Item) { + ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end()); + BinaryByteStream S(Bytes, llvm::support::little); + BinaryStreamReader SR(S); + auto EC = consume(SR, Item); + Data = Data.take_back(SR.bytesRemaining()); + return EC; +} + +Error llvm::codeview::consume(BinaryStreamReader &Reader, int32_t &Item) { + return Reader.readInteger(Item); +} + +Error llvm::codeview::consume(BinaryStreamReader &Reader, StringRef &Item) { + if (Reader.empty()) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Null terminated string buffer is empty!"); + + return Reader.readCString(Item); +} + +Expected<CVSymbol> llvm::codeview::readSymbolFromStream(BinaryStreamRef Stream, + uint32_t Offset) { + return readCVRecordFromStream<SymbolKind>(Stream, Offset); +} diff --git a/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp b/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp new file mode 100644 index 000000000000..654c40a7470d --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp @@ -0,0 +1,56 @@ +#include "llvm/DebugInfo/CodeView/SimpleTypeSerializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +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); + + // Write the record prefix first with a dummy length but real kind. + RecordPrefix DummyPrefix(uint16_t(Record.getKind())); + cantFail(Writer.writeObject(DummyPrefix)); + + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(ScratchBuffer.data()); + CVType CVT(Prefix, sizeof(RecordPrefix)); + + cantFail(Mapping.visitTypeBegin(CVT)); + cantFail(Mapping.visitKnownRecord(CVT, Record)); + cantFail(Mapping.visitTypeEnd(CVT)); + + addPadding(Writer); + + // Update the size and kind after serialization. + 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/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp b/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp new file mode 100644 index 000000000000..9e204eec8604 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp @@ -0,0 +1,80 @@ +//===- StringsAndChecksums.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/Support/Error.h" +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; + +StringsAndChecksumsRef::StringsAndChecksumsRef() = default; + +StringsAndChecksumsRef::StringsAndChecksumsRef( + const DebugStringTableSubsectionRef &Strings) + : Strings(&Strings) {} + +StringsAndChecksumsRef::StringsAndChecksumsRef( + const DebugStringTableSubsectionRef &Strings, + const DebugChecksumsSubsectionRef &Checksums) + : Strings(&Strings), Checksums(&Checksums) {} + +void StringsAndChecksumsRef::initializeStrings( + const DebugSubsectionRecord &SR) { + assert(SR.kind() == DebugSubsectionKind::StringTable); + assert(!Strings && "Found a string table even though we already have one!"); + + 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 = std::make_shared<DebugChecksumsSubsectionRef>(); + *OwnedChecksums = CS; + Checksums = OwnedChecksums.get(); +} + +void StringsAndChecksumsRef::initializeChecksums( + const DebugSubsectionRecord &FCR) { + assert(FCR.kind() == DebugSubsectionKind::FileChecksums); + if (Checksums) + return; + + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); + consumeError(OwnedChecksums->initialize(FCR.getRecordData())); + Checksums = OwnedChecksums.get(); +} diff --git a/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp new file mode 100644 index 000000000000..45b63983beb4 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -0,0 +1,679 @@ +//===-- SymbolDumper.cpp - CodeView symbol info dumper ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" + +#include <system_error> + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +/// Use this private dumper implementation to keep implementation details about +/// the visitor out of SymbolDumper.h. +class CVSymbolDumperImpl : public SymbolVisitorCallbacks { +public: + CVSymbolDumperImpl(TypeCollection &Types, SymbolDumpDelegate *ObjDelegate, + ScopedPrinter &W, CPUType CPU, bool PrintRecordBytes) + : Types(Types), ObjDelegate(ObjDelegate), W(W), CompilationCPUType(CPU), + PrintRecordBytes(PrintRecordBytes), InFunctionScope(false) {} + +/// CVSymbolVisitor overrides. +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownRecord(CVSymbol &CVR, Name &Record) override; +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + + Error visitSymbolBegin(CVSymbol &Record) override; + Error visitSymbolEnd(CVSymbol &Record) override; + Error visitUnknownSymbol(CVSymbol &Record) override; + + CPUType getCompilationCPUType() const { return CompilationCPUType; } + +private: + void printLocalVariableAddrRange(const LocalVariableAddrRange &Range, + uint32_t RelocationOffset); + void printLocalVariableAddrGap(ArrayRef<LocalVariableAddrGap> Gaps); + void printTypeIndex(StringRef FieldName, TypeIndex TI); + + TypeCollection &Types; + SymbolDumpDelegate *ObjDelegate; + ScopedPrinter &W; + + /// Save the machine or CPU type when dumping a compile symbols. + CPUType CompilationCPUType = CPUType::X64; + + bool PrintRecordBytes; + bool InFunctionScope; +}; +} + +static StringRef getSymbolKindName(SymbolKind Kind) { + switch (Kind) { +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + case EnumName: \ + return #Name; +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + default: + break; + } + return "UnknownSym"; +} + +void CVSymbolDumperImpl::printLocalVariableAddrRange( + const LocalVariableAddrRange &Range, uint32_t RelocationOffset) { + DictScope S(W, "LocalVariableAddrRange"); + if (ObjDelegate) + ObjDelegate->printRelocatedField("OffsetStart", RelocationOffset, + Range.OffsetStart); + W.printHex("ISectStart", Range.ISectStart); + W.printHex("Range", Range.Range); +} + +void CVSymbolDumperImpl::printLocalVariableAddrGap( + ArrayRef<LocalVariableAddrGap> Gaps) { + for (auto &Gap : Gaps) { + ListScope S(W, "LocalVariableAddrGap"); + W.printHex("GapStartOffset", Gap.GapStartOffset); + W.printHex("Range", Gap.Range); + } +} + +void CVSymbolDumperImpl::printTypeIndex(StringRef FieldName, TypeIndex TI) { + codeview::printTypeIndex(W, FieldName, TI, Types); +} + +Error CVSymbolDumperImpl::visitSymbolBegin(CVSymbol &CVR) { + W.startLine() << getSymbolKindName(CVR.kind()); + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("Kind", unsigned(CVR.kind()), getSymbolTypeNames()); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitSymbolEnd(CVSymbol &CVR) { + if (PrintRecordBytes && ObjDelegate) + ObjDelegate->printBinaryBlockWithRelocs("SymData", CVR.content()); + + W.unindent(); + W.startLine() << "}\n"; + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) { + StringRef LinkageName; + W.printHex("PtrParent", Block.Parent); + W.printHex("PtrEnd", Block.End); + W.printHex("CodeSize", Block.CodeSize); + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", Block.getRelocationOffset(), + Block.CodeOffset, &LinkageName); + } + W.printHex("Segment", Block.Segment); + W.printString("BlockName", Block.Name); + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, Thunk32Sym &Thunk) { + W.printString("Name", Thunk.Name); + W.printNumber("Parent", Thunk.Parent); + W.printNumber("End", Thunk.End); + W.printNumber("Next", Thunk.Next); + W.printNumber("Off", Thunk.Offset); + W.printNumber("Seg", Thunk.Segment); + W.printNumber("Len", Thunk.Length); + W.printEnum("Ordinal", uint8_t(Thunk.Thunk), getThunkOrdinalNames()); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + TrampolineSym &Tramp) { + W.printEnum("Type", uint16_t(Tramp.Type), getTrampolineNames()); + W.printNumber("Size", Tramp.Size); + W.printNumber("ThunkOff", Tramp.ThunkOffset); + W.printNumber("TargetOff", Tramp.TargetOffset); + W.printNumber("ThunkSection", Tramp.ThunkSection); + W.printNumber("TargetSection", Tramp.TargetSection); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, SectionSym &Section) { + W.printNumber("SectionNumber", Section.SectionNumber); + W.printNumber("Alignment", Section.Alignment); + W.printNumber("Rva", Section.Rva); + W.printNumber("Length", Section.Length); + W.printFlags("Characteristics", Section.Characteristics, + getImageSectionCharacteristicNames(), + COFF::SectionCharacteristics(0x00F00000)); + + W.printString("Name", Section.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + CoffGroupSym &CoffGroup) { + W.printNumber("Size", CoffGroup.Size); + W.printFlags("Characteristics", CoffGroup.Characteristics, + getImageSectionCharacteristicNames(), + COFF::SectionCharacteristics(0x00F00000)); + W.printNumber("Offset", CoffGroup.Offset); + W.printNumber("Segment", CoffGroup.Segment); + W.printString("Name", CoffGroup.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + BPRelativeSym &BPRel) { + W.printNumber("Offset", BPRel.Offset); + printTypeIndex("Type", BPRel.Type); + W.printString("VarName", BPRel.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + printTypeIndex("BuildId", BuildInfo.BuildId); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + CallSiteInfoSym &CallSiteInfo) { + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", + CallSiteInfo.getRelocationOffset(), + CallSiteInfo.CodeOffset, &LinkageName); + } + W.printHex("Segment", CallSiteInfo.Segment); + printTypeIndex("Type", CallSiteInfo.Type); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + EnvBlockSym &EnvBlock) { + ListScope L(W, "Entries"); + for (auto Entry : EnvBlock.Fields) { + W.printString(Entry); + } + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + FileStaticSym &FileStatic) { + printTypeIndex("Index", FileStatic.Index); + W.printNumber("ModFilenameOffset", FileStatic.ModFilenameOffset); + W.printFlags("Flags", uint16_t(FileStatic.Flags), getLocalFlagNames()); + W.printString("Name", FileStatic.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, ExportSym &Export) { + W.printNumber("Ordinal", Export.Ordinal); + W.printFlags("Flags", uint16_t(Export.Flags), getExportSymFlagNames()); + W.printString("Name", Export.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + Compile2Sym &Compile2) { + W.printEnum("Language", Compile2.getLanguage(), getSourceLanguageNames()); + W.printFlags("Flags", Compile2.getFlags(), getCompileSym2FlagNames()); + W.printEnum("Machine", unsigned(Compile2.Machine), getCPUTypeNames()); + CompilationCPUType = Compile2.Machine; + std::string FrontendVersion; + { + raw_string_ostream Out(FrontendVersion); + Out << Compile2.VersionFrontendMajor << '.' << Compile2.VersionFrontendMinor + << '.' << Compile2.VersionFrontendBuild; + } + std::string BackendVersion; + { + raw_string_ostream Out(BackendVersion); + Out << Compile2.VersionBackendMajor << '.' << Compile2.VersionBackendMinor + << '.' << Compile2.VersionBackendBuild; + } + W.printString("FrontendVersion", FrontendVersion); + W.printString("BackendVersion", BackendVersion); + W.printString("VersionName", Compile2.Version); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + Compile3Sym &Compile3) { + W.printEnum("Language", uint8_t(Compile3.getLanguage()), getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile3.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile3.Machine), getCPUTypeNames()); + CompilationCPUType = Compile3.Machine; + std::string FrontendVersion; + { + raw_string_ostream Out(FrontendVersion); + Out << Compile3.VersionFrontendMajor << '.' << Compile3.VersionFrontendMinor + << '.' << Compile3.VersionFrontendBuild << '.' + << Compile3.VersionFrontendQFE; + } + std::string BackendVersion; + { + raw_string_ostream Out(BackendVersion); + Out << Compile3.VersionBackendMajor << '.' << Compile3.VersionBackendMinor + << '.' << Compile3.VersionBackendBuild << '.' + << Compile3.VersionBackendQFE; + } + W.printString("FrontendVersion", FrontendVersion); + W.printString("BackendVersion", BackendVersion); + W.printString("VersionName", Compile3.Version); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + ConstantSym &Constant) { + printTypeIndex("Type", Constant.Type); + W.printNumber("Value", Constant.Value); + W.printString("Name", Constant.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, DataSym &Data) { + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("DataOffset", Data.getRelocationOffset(), + Data.DataOffset, &LinkageName); + } + printTypeIndex("Type", Data.Type); + W.printString("DisplayName", Data.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, + DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) { + W.printNumber("Offset", DefRangeFramePointerRelFullScope.Offset); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + W.printNumber("Offset", DefRangeFramePointerRel.Hdr.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterRelSym &DefRangeRegisterRel) { + W.printEnum("BaseRegister", uint16_t(DefRangeRegisterRel.Hdr.Register), + getRegisterNames(CompilationCPUType)); + W.printBoolean("HasSpilledUDTMember", + DefRangeRegisterRel.hasSpilledUDTMember()); + W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); + W.printNumber("BasePointerOffset", DefRangeRegisterRel.Hdr.BasePointerOffset); + printLocalVariableAddrRange(DefRangeRegisterRel.Range, + DefRangeRegisterRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegisterRel.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) { + W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register), + getRegisterNames(CompilationCPUType)); + W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); + printLocalVariableAddrRange(DefRangeRegister.Range, + DefRangeRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegister.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register), + getRegisterNames(CompilationCPUType)); + W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName); + W.printNumber("OffsetInParent", DefRangeSubfieldRegister.Hdr.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfieldRegister.Range, + DefRangeSubfieldRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfieldRegister.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeSubfieldSym &DefRangeSubfield) { + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRangeSubfield.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error<CodeViewError>( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + W.printNumber("OffsetInParent", DefRangeSubfield.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfield.Range, + DefRangeSubfield.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfield.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + DefRangeSym &DefRange) { + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRange.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error<CodeViewError>( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + printLocalVariableAddrRange(DefRange.Range, DefRange.getRelocationOffset()); + printLocalVariableAddrGap(DefRange.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + FrameCookieSym &FrameCookie) { + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", + FrameCookie.getRelocationOffset(), + FrameCookie.CodeOffset, &LinkageName); + } + W.printEnum("Register", uint16_t(FrameCookie.Register), + getRegisterNames(CompilationCPUType)); + W.printEnum("CookieKind", uint16_t(FrameCookie.CookieKind), + getFrameCookieKindNames()); + W.printHex("Flags", FrameCookie.Flags); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + FrameProcSym &FrameProc) { + W.printHex("TotalFrameBytes", FrameProc.TotalFrameBytes); + W.printHex("PaddingFrameBytes", FrameProc.PaddingFrameBytes); + W.printHex("OffsetToPadding", FrameProc.OffsetToPadding); + W.printHex("BytesOfCalleeSavedRegisters", + FrameProc.BytesOfCalleeSavedRegisters); + W.printHex("OffsetOfExceptionHandler", FrameProc.OffsetOfExceptionHandler); + W.printHex("SectionIdOfExceptionHandler", + FrameProc.SectionIdOfExceptionHandler); + W.printFlags("Flags", static_cast<uint32_t>(FrameProc.Flags), + getFrameProcSymFlagNames()); + W.printEnum("LocalFramePtrReg", + uint16_t(FrameProc.getLocalFramePtrReg(CompilationCPUType)), + getRegisterNames(CompilationCPUType)); + W.printEnum("ParamFramePtrReg", + uint16_t(FrameProc.getParamFramePtrReg(CompilationCPUType)), + getRegisterNames(CompilationCPUType)); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, HeapAllocationSiteSym &HeapAllocSite) { + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", + HeapAllocSite.getRelocationOffset(), + HeapAllocSite.CodeOffset, &LinkageName); + } + W.printHex("Segment", HeapAllocSite.Segment); + W.printHex("CallInstructionSize", HeapAllocSite.CallInstructionSize); + printTypeIndex("Type", HeapAllocSite.Type); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + InlineSiteSym &InlineSite) { + W.printHex("PtrParent", InlineSite.Parent); + W.printHex("PtrEnd", InlineSite.End); + printTypeIndex("Inlinee", InlineSite.Inlinee); + + ListScope BinaryAnnotations(W, "BinaryAnnotations"); + for (auto &Annotation : InlineSite.annotations()) { + switch (Annotation.OpCode) { + case BinaryAnnotationsOpCode::Invalid: + W.printString("(Annotation Padding)"); + break; + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeLength: + W.printHex(Annotation.Name, Annotation.U1); + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetBase: + case BinaryAnnotationsOpCode::ChangeLineEndDelta: + case BinaryAnnotationsOpCode::ChangeRangeKind: + case BinaryAnnotationsOpCode::ChangeColumnStart: + case BinaryAnnotationsOpCode::ChangeColumnEnd: + W.printNumber(Annotation.Name, Annotation.U1); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + case BinaryAnnotationsOpCode::ChangeColumnEndDelta: + W.printNumber(Annotation.Name, Annotation.S1); + break; + case BinaryAnnotationsOpCode::ChangeFile: + if (ObjDelegate) { + W.printHex("ChangeFile", + ObjDelegate->getFileNameForFileOffset(Annotation.U1), + Annotation.U1); + } else { + W.printHex("ChangeFile", Annotation.U1); + } + + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: { + W.startLine() << "ChangeCodeOffsetAndLineOffset: {CodeOffset: " + << W.hex(Annotation.U1) << ", LineOffset: " << Annotation.S1 + << "}\n"; + break; + } + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: { + W.startLine() << "ChangeCodeLengthAndCodeOffset: {CodeOffset: " + << W.hex(Annotation.U2) + << ", Length: " << W.hex(Annotation.U1) << "}\n"; + break; + } + } + } + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + RegisterSym &Register) { + printTypeIndex("Type", Register.Index); + W.printEnum("Seg", uint16_t(Register.Register), + getRegisterNames(CompilationCPUType)); + W.printString("Name", Register.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, PublicSym32 &Public) { + W.printFlags("Flags", uint32_t(Public.Flags), getPublicSymFlagNames()); + W.printNumber("Seg", Public.Segment); + W.printNumber("Off", Public.Offset); + W.printString("Name", Public.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, ProcRefSym &ProcRef) { + W.printNumber("SumName", ProcRef.SumName); + W.printNumber("SymOffset", ProcRef.SymOffset); + W.printNumber("Mod", ProcRef.Module); + W.printString("Name", ProcRef.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, LabelSym &Label) { + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", Label.getRelocationOffset(), + Label.CodeOffset, &LinkageName); + } + W.printHex("Segment", Label.Segment); + W.printHex("Flags", uint8_t(Label.Flags)); + W.printFlags("Flags", uint8_t(Label.Flags), getProcSymFlagNames()); + W.printString("DisplayName", Label.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, LocalSym &Local) { + printTypeIndex("Type", Local.Type); + W.printFlags("Flags", uint16_t(Local.Flags), getLocalFlagNames()); + W.printString("VarName", Local.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, ObjNameSym &ObjName) { + W.printHex("Signature", ObjName.Signature); + W.printString("ObjectName", ObjName.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) { + if (InFunctionScope) + return llvm::make_error<CodeViewError>( + "Visiting a ProcSym while inside function scope!"); + + InFunctionScope = true; + + StringRef LinkageName; + W.printHex("PtrParent", Proc.Parent); + W.printHex("PtrEnd", Proc.End); + W.printHex("PtrNext", Proc.Next); + W.printHex("CodeSize", Proc.CodeSize); + W.printHex("DbgStart", Proc.DbgStart); + W.printHex("DbgEnd", Proc.DbgEnd); + printTypeIndex("FunctionType", Proc.FunctionType); + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", Proc.getRelocationOffset(), + Proc.CodeOffset, &LinkageName); + } + W.printHex("Segment", Proc.Segment); + W.printFlags("Flags", static_cast<uint8_t>(Proc.Flags), + getProcSymFlagNames()); + W.printString("DisplayName", Proc.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + ScopeEndSym &ScopeEnd) { + InFunctionScope = false; + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) { + ListScope S(W, CVR.kind() == S_CALLEES ? "Callees" : "Callers"); + for (auto FuncID : Caller.Indices) + printTypeIndex("FuncID", FuncID); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + RegRelativeSym &RegRel) { + W.printHex("Offset", RegRel.Offset); + printTypeIndex("Type", RegRel.Type); + W.printEnum("Register", uint16_t(RegRel.Register), + getRegisterNames(CompilationCPUType)); + W.printString("VarName", RegRel.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + ThreadLocalDataSym &Data) { + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("DataOffset", Data.getRelocationOffset(), + Data.DataOffset, &LinkageName); + } + printTypeIndex("Type", Data.Type); + W.printString("DisplayName", Data.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, UDTSym &UDT) { + printTypeIndex("Type", UDT.Type); + W.printString("UDTName", UDT.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + UsingNamespaceSym &UN) { + W.printString("Namespace", UN.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + AnnotationSym &Annot) { + W.printHex("Offset", Annot.CodeOffset); + W.printHex("Segment", Annot.Segment); + + ListScope S(W, "Strings"); + for (StringRef Str : Annot.Strings) + W.printString(Str); + + return Error::success(); +} + +Error CVSymbolDumperImpl::visitUnknownSymbol(CVSymbol &CVR) { + W.printNumber("Length", CVR.length()); + return Error::success(); +} + +Error CVSymbolDumper::dump(CVRecord<SymbolKind> &Record) { + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(ObjDelegate.get(), Container); + CVSymbolDumperImpl Dumper(Types, ObjDelegate.get(), W, CompilationCPUType, + PrintRecordBytes); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + auto Err = Visitor.visitSymbolRecord(Record); + CompilationCPUType = Dumper.getCompilationCPUType(); + return Err; +} + +Error CVSymbolDumper::dump(const CVSymbolArray &Symbols) { + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(ObjDelegate.get(), Container); + CVSymbolDumperImpl Dumper(Types, ObjDelegate.get(), W, CompilationCPUType, + PrintRecordBytes); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + auto Err = Visitor.visitSymbolStream(Symbols); + CompilationCPUType = Dumper.getCompilationCPUType(); + return Err; +} diff --git a/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp b/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp new file mode 100644 index 000000000000..51a5a9e9243e --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp @@ -0,0 +1,93 @@ +//===- SymbolRecordHelpers.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +template <typename RecordT> RecordT createRecord(const CVSymbol &sym) { + RecordT record(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record)); + return record; +} + +uint32_t llvm::codeview::getScopeEndOffset(const CVSymbol &Sym) { + assert(symbolOpensScope(Sym.kind())); + switch (Sym.kind()) { + 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: { + ProcSym Proc = createRecord<ProcSym>(Sym); + return Proc.End; + } + case SymbolKind::S_BLOCK32: { + BlockSym Block = createRecord<BlockSym>(Sym); + return Block.End; + } + case SymbolKind::S_THUNK32: { + Thunk32Sym Thunk = createRecord<Thunk32Sym>(Sym); + return Thunk.End; + } + case SymbolKind::S_INLINESITE: { + InlineSiteSym Site = createRecord<InlineSiteSym>(Sym); + return Site.End; + } + default: + assert(false && "Unknown record type"); + return 0; + } +} + +uint32_t +llvm::codeview::getScopeParentOffset(const llvm::codeview::CVSymbol &Sym) { + assert(symbolOpensScope(Sym.kind())); + switch (Sym.kind()) { + 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: { + ProcSym Proc = createRecord<ProcSym>(Sym); + return Proc.Parent; + } + case SymbolKind::S_BLOCK32: { + BlockSym Block = createRecord<BlockSym>(Sym); + return Block.Parent; + } + case SymbolKind::S_THUNK32: { + Thunk32Sym Thunk = createRecord<Thunk32Sym>(Sym); + return Thunk.Parent; + } + case SymbolKind::S_INLINESITE: { + InlineSiteSym Site = createRecord<InlineSiteSym>(Sym); + return Site.Parent; + } + default: + assert(false && "Unknown record type"); + return 0; + } +} + +CVSymbolArray +llvm::codeview::limitSymbolArrayToScope(const CVSymbolArray &Symbols, + uint32_t ScopeBegin) { + CVSymbol Opener = *Symbols.at(ScopeBegin); + assert(symbolOpensScope(Opener.kind())); + uint32_t EndOffset = getScopeEndOffset(Opener); + CVSymbol Closer = *Symbols.at(EndOffset); + EndOffset += Closer.RecordData.size(); + return Symbols.substream(ScopeBegin, EndOffset); +} diff --git a/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp new file mode 100644 index 000000000000..3b627930e271 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp @@ -0,0 +1,558 @@ +//===- SymbolRecordMapping.cpp -----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h" + +using namespace llvm; +using namespace llvm::codeview; + +#define error(X) \ + if (auto EC = X) \ + return EC; + +namespace { +struct MapGap { + Error operator()(CodeViewRecordIO &IO, LocalVariableAddrGap &Gap) const { + error(IO.mapInteger(Gap.GapStartOffset)); + error(IO.mapInteger(Gap.Range)); + return Error::success(); + } +}; +} + +static Error mapLocalVariableAddrRange(CodeViewRecordIO &IO, + LocalVariableAddrRange &Range) { + error(IO.mapInteger(Range.OffsetStart)); + error(IO.mapInteger(Range.ISectStart)); + error(IO.mapInteger(Range.Range)); + return Error::success(); +} + +Error SymbolRecordMapping::visitSymbolBegin(CVSymbol &Record) { + error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix))); + return Error::success(); +} + +Error SymbolRecordMapping::visitSymbolEnd(CVSymbol &Record) { + error(IO.padToAlignment(alignOf(Container))); + error(IO.endRecord()); + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) { + + error(IO.mapInteger(Block.Parent)); + error(IO.mapInteger(Block.End)); + error(IO.mapInteger(Block.CodeSize)); + error(IO.mapInteger(Block.CodeOffset)); + error(IO.mapInteger(Block.Segment)); + error(IO.mapStringZ(Block.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, Thunk32Sym &Thunk) { + + error(IO.mapInteger(Thunk.Parent)); + error(IO.mapInteger(Thunk.End)); + error(IO.mapInteger(Thunk.Next)); + error(IO.mapInteger(Thunk.Offset)); + error(IO.mapInteger(Thunk.Segment)); + error(IO.mapInteger(Thunk.Length)); + error(IO.mapEnum(Thunk.Thunk)); + error(IO.mapStringZ(Thunk.Name)); + error(IO.mapByteVectorTail(Thunk.VariantData)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + TrampolineSym &Tramp) { + + error(IO.mapEnum(Tramp.Type)); + error(IO.mapInteger(Tramp.Size)); + error(IO.mapInteger(Tramp.ThunkOffset)); + error(IO.mapInteger(Tramp.TargetOffset)); + error(IO.mapInteger(Tramp.ThunkSection)); + error(IO.mapInteger(Tramp.TargetSection)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + SectionSym &Section) { + uint8_t Padding = 0; + + error(IO.mapInteger(Section.SectionNumber)); + error(IO.mapInteger(Section.Alignment)); + error(IO.mapInteger(Padding)); + error(IO.mapInteger(Section.Rva)); + error(IO.mapInteger(Section.Length)); + error(IO.mapInteger(Section.Characteristics)); + error(IO.mapStringZ(Section.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + CoffGroupSym &CoffGroup) { + + error(IO.mapInteger(CoffGroup.Size)); + error(IO.mapInteger(CoffGroup.Characteristics)); + error(IO.mapInteger(CoffGroup.Offset)); + error(IO.mapInteger(CoffGroup.Segment)); + error(IO.mapStringZ(CoffGroup.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + BPRelativeSym &BPRel) { + + error(IO.mapInteger(BPRel.Offset)); + error(IO.mapInteger(BPRel.Type)); + error(IO.mapStringZ(BPRel.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + + error(IO.mapInteger(BuildInfo.BuildId)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + CallSiteInfoSym &CallSiteInfo) { + uint16_t Padding = 0; + + error(IO.mapInteger(CallSiteInfo.CodeOffset)); + error(IO.mapInteger(CallSiteInfo.Segment)); + error(IO.mapInteger(Padding)); + error(IO.mapInteger(CallSiteInfo.Type)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + EnvBlockSym &EnvBlock) { + + uint8_t Reserved = 0; + error(IO.mapInteger(Reserved)); + error(IO.mapStringZVectorZ(EnvBlock.Fields)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + FileStaticSym &FileStatic) { + + error(IO.mapInteger(FileStatic.Index)); + error(IO.mapInteger(FileStatic.ModFilenameOffset)); + error(IO.mapEnum(FileStatic.Flags)); + error(IO.mapStringZ(FileStatic.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, ExportSym &Export) { + + error(IO.mapInteger(Export.Ordinal)); + error(IO.mapEnum(Export.Flags)); + error(IO.mapStringZ(Export.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + Compile2Sym &Compile2) { + + error(IO.mapEnum(Compile2.Flags)); + error(IO.mapEnum(Compile2.Machine)); + error(IO.mapInteger(Compile2.VersionFrontendMajor)); + error(IO.mapInteger(Compile2.VersionFrontendMinor)); + error(IO.mapInteger(Compile2.VersionFrontendBuild)); + error(IO.mapInteger(Compile2.VersionBackendMajor)); + error(IO.mapInteger(Compile2.VersionBackendMinor)); + error(IO.mapInteger(Compile2.VersionBackendBuild)); + error(IO.mapStringZ(Compile2.Version)); + error(IO.mapStringZVectorZ(Compile2.ExtraStrings)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + Compile3Sym &Compile3) { + + error(IO.mapEnum(Compile3.Flags)); + error(IO.mapEnum(Compile3.Machine)); + error(IO.mapInteger(Compile3.VersionFrontendMajor)); + error(IO.mapInteger(Compile3.VersionFrontendMinor)); + error(IO.mapInteger(Compile3.VersionFrontendBuild)); + error(IO.mapInteger(Compile3.VersionFrontendQFE)); + error(IO.mapInteger(Compile3.VersionBackendMajor)); + error(IO.mapInteger(Compile3.VersionBackendMinor)); + error(IO.mapInteger(Compile3.VersionBackendBuild)); + error(IO.mapInteger(Compile3.VersionBackendQFE)); + error(IO.mapStringZ(Compile3.Version)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + ConstantSym &Constant) { + + error(IO.mapInteger(Constant.Type)); + error(IO.mapEncodedInteger(Constant.Value)); + error(IO.mapStringZ(Constant.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, DataSym &Data) { + + error(IO.mapInteger(Data.Type)); + error(IO.mapInteger(Data.DataOffset)); + error(IO.mapInteger(Data.Segment)); + error(IO.mapStringZ(Data.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + + error(IO.mapObject(DefRangeFramePointerRel.Hdr.Offset)); + error(mapLocalVariableAddrRange(IO, DefRangeFramePointerRel.Range)); + error(IO.mapVectorTail(DefRangeFramePointerRel.Gaps, MapGap())); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, + DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) { + + error(IO.mapInteger(DefRangeFramePointerRelFullScope.Offset)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterRelSym &DefRangeRegisterRel) { + + error(IO.mapObject(DefRangeRegisterRel.Hdr.Register)); + error(IO.mapObject(DefRangeRegisterRel.Hdr.Flags)); + error(IO.mapObject(DefRangeRegisterRel.Hdr.BasePointerOffset)); + error(mapLocalVariableAddrRange(IO, DefRangeRegisterRel.Range)); + error(IO.mapVectorTail(DefRangeRegisterRel.Gaps, MapGap())); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) { + + error(IO.mapObject(DefRangeRegister.Hdr.Register)); + error(IO.mapObject(DefRangeRegister.Hdr.MayHaveNoName)); + error(mapLocalVariableAddrRange(IO, DefRangeRegister.Range)); + error(IO.mapVectorTail(DefRangeRegister.Gaps, MapGap())); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + + error(IO.mapObject(DefRangeSubfieldRegister.Hdr.Register)); + error(IO.mapObject(DefRangeSubfieldRegister.Hdr.MayHaveNoName)); + error(IO.mapObject(DefRangeSubfieldRegister.Hdr.OffsetInParent)); + error(mapLocalVariableAddrRange(IO, DefRangeSubfieldRegister.Range)); + error(IO.mapVectorTail(DefRangeSubfieldRegister.Gaps, MapGap())); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, DefRangeSubfieldSym &DefRangeSubfield) { + + error(IO.mapInteger(DefRangeSubfield.Program)); + error(IO.mapInteger(DefRangeSubfield.OffsetInParent)); + error(mapLocalVariableAddrRange(IO, DefRangeSubfield.Range)); + error(IO.mapVectorTail(DefRangeSubfield.Gaps, MapGap())); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + DefRangeSym &DefRange) { + + error(IO.mapInteger(DefRange.Program)); + error(mapLocalVariableAddrRange(IO, DefRange.Range)); + error(IO.mapVectorTail(DefRange.Gaps, MapGap())); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + FrameCookieSym &FrameCookie) { + + error(IO.mapInteger(FrameCookie.CodeOffset)); + error(IO.mapInteger(FrameCookie.Register)); + error(IO.mapEnum(FrameCookie.CookieKind)); + error(IO.mapInteger(FrameCookie.Flags)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + FrameProcSym &FrameProc) { + error(IO.mapInteger(FrameProc.TotalFrameBytes)); + error(IO.mapInteger(FrameProc.PaddingFrameBytes)); + error(IO.mapInteger(FrameProc.OffsetToPadding)); + error(IO.mapInteger(FrameProc.BytesOfCalleeSavedRegisters)); + error(IO.mapInteger(FrameProc.OffsetOfExceptionHandler)); + error(IO.mapInteger(FrameProc.SectionIdOfExceptionHandler)); + error(IO.mapEnum(FrameProc.Flags)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord( + CVSymbol &CVR, HeapAllocationSiteSym &HeapAllocSite) { + + error(IO.mapInteger(HeapAllocSite.CodeOffset)); + error(IO.mapInteger(HeapAllocSite.Segment)); + error(IO.mapInteger(HeapAllocSite.CallInstructionSize)); + error(IO.mapInteger(HeapAllocSite.Type)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + InlineSiteSym &InlineSite) { + + error(IO.mapInteger(InlineSite.Parent)); + error(IO.mapInteger(InlineSite.End)); + error(IO.mapInteger(InlineSite.Inlinee)); + error(IO.mapByteVectorTail(InlineSite.AnnotationData)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + RegisterSym &Register) { + + error(IO.mapInteger(Register.Index)); + error(IO.mapEnum(Register.Register)); + error(IO.mapStringZ(Register.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + PublicSym32 &Public) { + + error(IO.mapEnum(Public.Flags)); + error(IO.mapInteger(Public.Offset)); + error(IO.mapInteger(Public.Segment)); + error(IO.mapStringZ(Public.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + ProcRefSym &ProcRef) { + + error(IO.mapInteger(ProcRef.SumName)); + error(IO.mapInteger(ProcRef.SymOffset)); + error(IO.mapInteger(ProcRef.Module)); + error(IO.mapStringZ(ProcRef.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, LabelSym &Label) { + + error(IO.mapInteger(Label.CodeOffset)); + error(IO.mapInteger(Label.Segment)); + error(IO.mapEnum(Label.Flags)); + error(IO.mapStringZ(Label.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, LocalSym &Local) { + error(IO.mapInteger(Local.Type)); + error(IO.mapEnum(Local.Flags)); + error(IO.mapStringZ(Local.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + ObjNameSym &ObjName) { + + error(IO.mapInteger(ObjName.Signature)); + error(IO.mapStringZ(ObjName.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) { + error(IO.mapInteger(Proc.Parent)); + error(IO.mapInteger(Proc.End)); + error(IO.mapInteger(Proc.Next)); + error(IO.mapInteger(Proc.CodeSize)); + error(IO.mapInteger(Proc.DbgStart)); + error(IO.mapInteger(Proc.DbgEnd)); + error(IO.mapInteger(Proc.FunctionType)); + error(IO.mapInteger(Proc.CodeOffset)); + error(IO.mapInteger(Proc.Segment)); + error(IO.mapEnum(Proc.Flags)); + error(IO.mapStringZ(Proc.Name)); + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + ScopeEndSym &ScopeEnd) { + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) { + error(IO.mapVectorN<uint32_t>( + Caller.Indices, + [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + RegRelativeSym &RegRel) { + + error(IO.mapInteger(RegRel.Offset)); + error(IO.mapInteger(RegRel.Type)); + error(IO.mapEnum(RegRel.Register)); + error(IO.mapStringZ(RegRel.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + ThreadLocalDataSym &Data) { + + error(IO.mapInteger(Data.Type)); + error(IO.mapInteger(Data.DataOffset)); + error(IO.mapInteger(Data.Segment)); + error(IO.mapStringZ(Data.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, UDTSym &UDT) { + + error(IO.mapInteger(UDT.Type)); + error(IO.mapStringZ(UDT.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + UsingNamespaceSym &UN) { + + error(IO.mapStringZ(UN.Name)); + + return Error::success(); +} + +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + AnnotationSym &Annot) { + + error(IO.mapInteger(Annot.CodeOffset)); + error(IO.mapInteger(Annot.Segment)); + error(IO.mapVectorN<uint16_t>( + Annot.Strings, + [](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S); })); + + return Error::success(); +} + +RegisterId codeview::decodeFramePtrReg(EncodedFramePtrReg EncodedReg, + CPUType CPU) { + assert(unsigned(EncodedReg) < 4); + switch (CPU) { + // FIXME: Add ARM and AArch64 variants here. + default: + break; + case CPUType::Intel8080: + case CPUType::Intel8086: + case CPUType::Intel80286: + case CPUType::Intel80386: + case CPUType::Intel80486: + case CPUType::Pentium: + case CPUType::PentiumPro: + case CPUType::Pentium3: + switch (EncodedReg) { + case EncodedFramePtrReg::None: return RegisterId::NONE; + case EncodedFramePtrReg::StackPtr: return RegisterId::VFRAME; + case EncodedFramePtrReg::FramePtr: return RegisterId::EBP; + case EncodedFramePtrReg::BasePtr: return RegisterId::EBX; + } + llvm_unreachable("bad encoding"); + case CPUType::X64: + switch (EncodedReg) { + case EncodedFramePtrReg::None: return RegisterId::NONE; + case EncodedFramePtrReg::StackPtr: return RegisterId::RSP; + case EncodedFramePtrReg::FramePtr: return RegisterId::RBP; + case EncodedFramePtrReg::BasePtr: return RegisterId::R13; + } + llvm_unreachable("bad encoding"); + } + return RegisterId::NONE; +} + +EncodedFramePtrReg codeview::encodeFramePtrReg(RegisterId Reg, CPUType CPU) { + switch (CPU) { + // FIXME: Add ARM and AArch64 variants here. + default: + break; + case CPUType::Intel8080: + case CPUType::Intel8086: + case CPUType::Intel80286: + case CPUType::Intel80386: + case CPUType::Intel80486: + case CPUType::Pentium: + case CPUType::PentiumPro: + case CPUType::Pentium3: + switch (Reg) { + case RegisterId::VFRAME: + return EncodedFramePtrReg::StackPtr; + case RegisterId::EBP: + return EncodedFramePtrReg::FramePtr; + case RegisterId::EBX: + return EncodedFramePtrReg::BasePtr; + default: + break; + } + break; + case CPUType::X64: + switch (Reg) { + case RegisterId::RSP: + return EncodedFramePtrReg::StackPtr; + case RegisterId::RBP: + return EncodedFramePtrReg::FramePtr; + case RegisterId::R13: + return EncodedFramePtrReg::BasePtr; + default: + break; + } + break; + } + return EncodedFramePtrReg::None; +} diff --git a/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp b/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp new file mode 100644 index 000000000000..de9bb42b1798 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp @@ -0,0 +1,59 @@ +//===- SymbolSerializer.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <cassert> +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +SymbolSerializer::SymbolSerializer(BumpPtrAllocator &Allocator, + CodeViewContainer Container) + : Storage(Allocator), Stream(RecordBuffer, support::little), Writer(Stream), + Mapping(Writer, Container) {} + +Error SymbolSerializer::visitSymbolBegin(CVSymbol &Record) { + assert(!CurrentSymbol.hasValue() && "Already in a symbol mapping!"); + + Writer.setOffset(0); + + if (auto EC = writeRecordPrefix(Record.kind())) + return EC; + + CurrentSymbol = Record.kind(); + if (auto EC = Mapping.visitSymbolBegin(Record)) + return EC; + + return Error::success(); +} + +Error SymbolSerializer::visitSymbolEnd(CVSymbol &Record) { + assert(CurrentSymbol.hasValue() && "Not in a symbol mapping!"); + + if (auto EC = Mapping.visitSymbolEnd(Record)) + return EC; + + uint32_t RecordEnd = Writer.getOffset(); + uint16_t Length = RecordEnd - 2; + Writer.setOffset(0); + if (auto EC = Writer.writeInteger(Length)) + return EC; + + uint8_t *StableStorage = Storage.Allocate<uint8_t>(RecordEnd); + ::memcpy(StableStorage, &RecordBuffer[0], RecordEnd); + Record.RecordData = ArrayRef<uint8_t>(StableStorage, RecordEnd); + CurrentSymbol.reset(); + + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp b/llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp new file mode 100644 index 000000000000..d5fea5ee5e29 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp @@ -0,0 +1,570 @@ +//===-- TypeDumpVisitor.cpp - CodeView type info dumper ----------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/TypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +static const EnumEntry<TypeLeafKind> LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; + +#define ENUM_ENTRY(enum_class, enum) \ + { #enum, std::underlying_type < enum_class > ::type(enum_class::enum) } + +static const EnumEntry<uint16_t> ClassOptionNames[] = { + ENUM_ENTRY(ClassOptions, Packed), + ENUM_ENTRY(ClassOptions, HasConstructorOrDestructor), + ENUM_ENTRY(ClassOptions, HasOverloadedOperator), + ENUM_ENTRY(ClassOptions, Nested), + ENUM_ENTRY(ClassOptions, ContainsNestedClass), + ENUM_ENTRY(ClassOptions, HasOverloadedAssignmentOperator), + ENUM_ENTRY(ClassOptions, HasConversionOperator), + ENUM_ENTRY(ClassOptions, ForwardReference), + ENUM_ENTRY(ClassOptions, Scoped), + ENUM_ENTRY(ClassOptions, HasUniqueName), + ENUM_ENTRY(ClassOptions, Sealed), + ENUM_ENTRY(ClassOptions, Intrinsic), +}; + +static const EnumEntry<uint8_t> MemberAccessNames[] = { + ENUM_ENTRY(MemberAccess, None), ENUM_ENTRY(MemberAccess, Private), + ENUM_ENTRY(MemberAccess, Protected), ENUM_ENTRY(MemberAccess, Public), +}; + +static const EnumEntry<uint16_t> MethodOptionNames[] = { + ENUM_ENTRY(MethodOptions, Pseudo), + ENUM_ENTRY(MethodOptions, NoInherit), + ENUM_ENTRY(MethodOptions, NoConstruct), + ENUM_ENTRY(MethodOptions, CompilerGenerated), + ENUM_ENTRY(MethodOptions, Sealed), +}; + +static const EnumEntry<uint16_t> MemberKindNames[] = { + ENUM_ENTRY(MethodKind, Vanilla), + ENUM_ENTRY(MethodKind, Virtual), + ENUM_ENTRY(MethodKind, Static), + ENUM_ENTRY(MethodKind, Friend), + ENUM_ENTRY(MethodKind, IntroducingVirtual), + ENUM_ENTRY(MethodKind, PureVirtual), + ENUM_ENTRY(MethodKind, PureIntroducingVirtual), +}; + +static const EnumEntry<uint8_t> PtrKindNames[] = { + ENUM_ENTRY(PointerKind, Near16), + ENUM_ENTRY(PointerKind, Far16), + ENUM_ENTRY(PointerKind, Huge16), + ENUM_ENTRY(PointerKind, BasedOnSegment), + ENUM_ENTRY(PointerKind, BasedOnValue), + ENUM_ENTRY(PointerKind, BasedOnSegmentValue), + ENUM_ENTRY(PointerKind, BasedOnAddress), + ENUM_ENTRY(PointerKind, BasedOnSegmentAddress), + ENUM_ENTRY(PointerKind, BasedOnType), + ENUM_ENTRY(PointerKind, BasedOnSelf), + ENUM_ENTRY(PointerKind, Near32), + ENUM_ENTRY(PointerKind, Far32), + ENUM_ENTRY(PointerKind, Near64), +}; + +static const EnumEntry<uint8_t> PtrModeNames[] = { + ENUM_ENTRY(PointerMode, Pointer), + ENUM_ENTRY(PointerMode, LValueReference), + ENUM_ENTRY(PointerMode, PointerToDataMember), + ENUM_ENTRY(PointerMode, PointerToMemberFunction), + ENUM_ENTRY(PointerMode, RValueReference), +}; + +static const EnumEntry<uint16_t> PtrMemberRepNames[] = { + ENUM_ENTRY(PointerToMemberRepresentation, Unknown), + ENUM_ENTRY(PointerToMemberRepresentation, SingleInheritanceData), + ENUM_ENTRY(PointerToMemberRepresentation, MultipleInheritanceData), + ENUM_ENTRY(PointerToMemberRepresentation, VirtualInheritanceData), + ENUM_ENTRY(PointerToMemberRepresentation, GeneralData), + ENUM_ENTRY(PointerToMemberRepresentation, SingleInheritanceFunction), + ENUM_ENTRY(PointerToMemberRepresentation, MultipleInheritanceFunction), + ENUM_ENTRY(PointerToMemberRepresentation, VirtualInheritanceFunction), + ENUM_ENTRY(PointerToMemberRepresentation, GeneralFunction), +}; + +static const EnumEntry<uint16_t> TypeModifierNames[] = { + ENUM_ENTRY(ModifierOptions, Const), ENUM_ENTRY(ModifierOptions, Volatile), + ENUM_ENTRY(ModifierOptions, Unaligned), +}; + +static const EnumEntry<uint8_t> CallingConventions[] = { + ENUM_ENTRY(CallingConvention, NearC), + ENUM_ENTRY(CallingConvention, FarC), + ENUM_ENTRY(CallingConvention, NearPascal), + ENUM_ENTRY(CallingConvention, FarPascal), + ENUM_ENTRY(CallingConvention, NearFast), + ENUM_ENTRY(CallingConvention, FarFast), + ENUM_ENTRY(CallingConvention, NearStdCall), + ENUM_ENTRY(CallingConvention, FarStdCall), + ENUM_ENTRY(CallingConvention, NearSysCall), + ENUM_ENTRY(CallingConvention, FarSysCall), + ENUM_ENTRY(CallingConvention, ThisCall), + ENUM_ENTRY(CallingConvention, MipsCall), + ENUM_ENTRY(CallingConvention, Generic), + ENUM_ENTRY(CallingConvention, AlphaCall), + ENUM_ENTRY(CallingConvention, PpcCall), + ENUM_ENTRY(CallingConvention, SHCall), + ENUM_ENTRY(CallingConvention, ArmCall), + ENUM_ENTRY(CallingConvention, AM33Call), + ENUM_ENTRY(CallingConvention, TriCall), + ENUM_ENTRY(CallingConvention, SH5Call), + ENUM_ENTRY(CallingConvention, M32RCall), + ENUM_ENTRY(CallingConvention, ClrCall), + ENUM_ENTRY(CallingConvention, Inline), + ENUM_ENTRY(CallingConvention, NearVector), +}; + +static const EnumEntry<uint8_t> FunctionOptionEnum[] = { + ENUM_ENTRY(FunctionOptions, CxxReturnUdt), + ENUM_ENTRY(FunctionOptions, Constructor), + ENUM_ENTRY(FunctionOptions, ConstructorWithVirtualBases), +}; + +static const EnumEntry<uint16_t> LabelTypeEnum[] = { + ENUM_ENTRY(LabelType, Near), ENUM_ENTRY(LabelType, Far), +}; + +#undef ENUM_ENTRY + +static StringRef getLeafTypeName(TypeLeafKind LT) { + switch (LT) { +#define TYPE_RECORD(ename, value, name) \ + case ename: \ + return #name; +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + default: + break; + } + return "UnknownLeaf"; +} + +void TypeDumpVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const { + codeview::printTypeIndex(*W, FieldName, TI, TpiTypes); +} + +void TypeDumpVisitor::printItemIndex(StringRef FieldName, TypeIndex TI) const { + codeview::printTypeIndex(*W, FieldName, TI, getSourceTypes()); +} + +Error TypeDumpVisitor::visitTypeBegin(CVType &Record) { + return visitTypeBegin(Record, TypeIndex::fromArrayIndex(TpiTypes.size())); +} + +Error TypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + W->startLine() << getLeafTypeName(Record.kind()); + W->getOStream() << " (" << HexNumber(Index.getIndex()) << ")"; + W->getOStream() << " {\n"; + W->indent(); + W->printEnum("TypeLeafKind", unsigned(Record.kind()), + makeArrayRef(LeafTypeNames)); + return Error::success(); +} + +Error TypeDumpVisitor::visitTypeEnd(CVType &Record) { + if (PrintRecordBytes) + W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.content())); + + W->unindent(); + W->startLine() << "}\n"; + return Error::success(); +} + +Error TypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) { + W->startLine() << getLeafTypeName(Record.Kind); + W->getOStream() << " {\n"; + W->indent(); + W->printEnum("TypeLeafKind", unsigned(Record.Kind), + makeArrayRef(LeafTypeNames)); + return Error::success(); +} + +Error TypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) { + if (PrintRecordBytes) + W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.Data)); + + W->unindent(); + W->startLine() << "}\n"; + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + FieldListRecord &FieldList) { + if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this)) + return EC; + + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, StringIdRecord &String) { + printItemIndex("Id", String.getId()); + W->printString("StringData", String.getString()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ArgListRecord &Args) { + auto Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + W->printNumber("NumArgs", Size); + ListScope Arguments(*W, "Arguments"); + for (uint32_t I = 0; I < Size; ++I) { + printTypeIndex("ArgType", Indices[I]); + } + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, StringListRecord &Strs) { + auto Indices = Strs.getIndices(); + uint32_t Size = Indices.size(); + W->printNumber("NumStrings", Size); + ListScope Arguments(*W, "Strings"); + for (uint32_t I = 0; I < Size; ++I) { + printItemIndex("String", Indices[I]); + } + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ClassRecord &Class) { + uint16_t Props = static_cast<uint16_t>(Class.getOptions()); + W->printNumber("MemberCount", Class.getMemberCount()); + W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames)); + printTypeIndex("FieldList", Class.getFieldList()); + printTypeIndex("DerivedFrom", Class.getDerivationList()); + printTypeIndex("VShape", Class.getVTableShape()); + W->printNumber("SizeOf", Class.getSize()); + W->printString("Name", Class.getName()); + if (Props & uint16_t(ClassOptions::HasUniqueName)) + W->printString("LinkageName", Class.getUniqueName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, UnionRecord &Union) { + uint16_t Props = static_cast<uint16_t>(Union.getOptions()); + W->printNumber("MemberCount", Union.getMemberCount()); + W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames)); + printTypeIndex("FieldList", Union.getFieldList()); + W->printNumber("SizeOf", Union.getSize()); + W->printString("Name", Union.getName()); + if (Props & uint16_t(ClassOptions::HasUniqueName)) + W->printString("LinkageName", Union.getUniqueName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { + uint16_t Props = static_cast<uint16_t>(Enum.getOptions()); + W->printNumber("NumEnumerators", Enum.getMemberCount()); + W->printFlags("Properties", uint16_t(Enum.getOptions()), + makeArrayRef(ClassOptionNames)); + printTypeIndex("UnderlyingType", Enum.getUnderlyingType()); + printTypeIndex("FieldListType", Enum.getFieldList()); + W->printString("Name", Enum.getName()); + if (Props & uint16_t(ClassOptions::HasUniqueName)) + W->printString("LinkageName", Enum.getUniqueName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { + printTypeIndex("ElementType", AT.getElementType()); + printTypeIndex("IndexType", AT.getIndexType()); + W->printNumber("SizeOf", AT.getSize()); + W->printString("Name", AT.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) { + printTypeIndex("CompleteClass", VFT.getCompleteClass()); + printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable()); + W->printHex("VFPtrOffset", VFT.getVFPtrOffset()); + W->printString("VFTableName", VFT.getName()); + for (auto N : VFT.getMethodNames()) + W->printString("MethodName", N); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) { + printTypeIndex("ClassType", Id.getClassType()); + printTypeIndex("FunctionType", Id.getFunctionType()); + W->printString("Name", Id.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) { + printTypeIndex("ReturnType", Proc.getReturnType()); + W->printEnum("CallingConvention", uint8_t(Proc.getCallConv()), + makeArrayRef(CallingConventions)); + W->printFlags("FunctionOptions", uint8_t(Proc.getOptions()), + makeArrayRef(FunctionOptionEnum)); + W->printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, MemberFunctionRecord &MF) { + printTypeIndex("ReturnType", MF.getReturnType()); + printTypeIndex("ClassType", MF.getClassType()); + printTypeIndex("ThisType", MF.getThisType()); + W->printEnum("CallingConvention", uint8_t(MF.getCallConv()), + makeArrayRef(CallingConventions)); + W->printFlags("FunctionOptions", uint8_t(MF.getOptions()), + makeArrayRef(FunctionOptionEnum)); + W->printNumber("NumParameters", MF.getParameterCount()); + printTypeIndex("ArgListType", MF.getArgumentList()); + W->printNumber("ThisAdjustment", MF.getThisPointerAdjustment()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + MethodOverloadListRecord &MethodList) { + for (auto &M : MethodList.getMethods()) { + ListScope S(*W, "Method"); + printMemberAttributes(M.getAccess(), M.getMethodKind(), M.getOptions()); + printTypeIndex("Type", M.getType()); + if (M.isIntroducingVirtual()) + W->printHex("VFTableOffset", M.getVFTableOffset()); + } + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) { + printItemIndex("ParentScope", Func.getParentScope()); + printTypeIndex("FunctionType", Func.getFunctionType()); + W->printString("Name", Func.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) { + W->printString("Guid", formatv("{0}", TS.getGuid()).str()); + W->printNumber("Age", TS.getAge()); + W->printString("Name", TS.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) { + printTypeIndex("PointeeType", Ptr.getReferentType()); + W->printEnum("PtrType", unsigned(Ptr.getPointerKind()), + makeArrayRef(PtrKindNames)); + W->printEnum("PtrMode", unsigned(Ptr.getMode()), makeArrayRef(PtrModeNames)); + + W->printNumber("IsFlat", Ptr.isFlat()); + W->printNumber("IsConst", Ptr.isConst()); + W->printNumber("IsVolatile", Ptr.isVolatile()); + W->printNumber("IsUnaligned", Ptr.isUnaligned()); + W->printNumber("IsRestrict", Ptr.isRestrict()); + W->printNumber("IsThisPtr&", Ptr.isLValueReferenceThisPtr()); + W->printNumber("IsThisPtr&&", Ptr.isRValueReferenceThisPtr()); + W->printNumber("SizeOf", Ptr.getSize()); + + if (Ptr.isPointerToMember()) { + const MemberPointerInfo &MI = Ptr.getMemberInfo(); + + printTypeIndex("ClassType", MI.getContainingType()); + W->printEnum("Representation", uint16_t(MI.getRepresentation()), + makeArrayRef(PtrMemberRepNames)); + } + + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) { + uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); + printTypeIndex("ModifiedType", Mod.getModifiedType()); + W->printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames)); + + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, BitFieldRecord &BitField) { + printTypeIndex("Type", BitField.getType()); + W->printNumber("BitSize", BitField.getBitSize()); + W->printNumber("BitOffset", BitField.getBitOffset()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Shape) { + W->printNumber("VFEntryCount", Shape.getEntryCount()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &Line) { + printTypeIndex("UDT", Line.getUDT()); + printItemIndex("SourceFile", Line.getSourceFile()); + W->printNumber("LineNumber", Line.getLineNumber()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &Line) { + printTypeIndex("UDT", Line.getUDT()); + printItemIndex("SourceFile", Line.getSourceFile()); + W->printNumber("LineNumber", Line.getLineNumber()); + W->printNumber("Module", Line.getModule()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, BuildInfoRecord &Args) { + W->printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size())); + + ListScope Arguments(*W, "Arguments"); + for (auto Arg : Args.getArgs()) { + printItemIndex("ArgType", Arg); + } + return Error::success(); +} + +void TypeDumpVisitor::printMemberAttributes(MemberAttributes Attrs) { + return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(), + Attrs.getFlags()); +} + +void TypeDumpVisitor::printMemberAttributes(MemberAccess Access, + MethodKind Kind, + MethodOptions Options) { + W->printEnum("AccessSpecifier", uint8_t(Access), + makeArrayRef(MemberAccessNames)); + // Data members will be vanilla. Don't try to print a method kind for them. + if (Kind != MethodKind::Vanilla) + W->printEnum("MethodKind", unsigned(Kind), makeArrayRef(MemberKindNames)); + if (Options != MethodOptions::None) { + W->printFlags("MethodOptions", unsigned(Options), + makeArrayRef(MethodOptionNames)); + } +} + +Error TypeDumpVisitor::visitUnknownMember(CVMemberRecord &Record) { + W->printHex("UnknownMember", unsigned(Record.Kind)); + return Error::success(); +} + +Error TypeDumpVisitor::visitUnknownType(CVType &Record) { + W->printEnum("Kind", uint16_t(Record.kind()), makeArrayRef(LeafTypeNames)); + W->printNumber("Length", uint32_t(Record.content().size())); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Nested) { + printTypeIndex("Type", Nested.getNestedType()); + W->printString("Name", Nested.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + OneMethodRecord &Method) { + MethodKind K = Method.getMethodKind(); + printMemberAttributes(Method.getAccess(), K, Method.getOptions()); + printTypeIndex("Type", Method.getType()); + // If virtual, then read the vftable offset. + if (Method.isIntroducingVirtual()) + W->printHex("VFTableOffset", Method.getVFTableOffset()); + W->printString("Name", Method.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + OverloadedMethodRecord &Method) { + W->printHex("MethodCount", Method.getNumOverloads()); + printTypeIndex("MethodListIndex", Method.getMethodList()); + W->printString("Name", Method.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + DataMemberRecord &Field) { + printMemberAttributes(Field.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("Type", Field.getType()); + W->printHex("FieldOffset", Field.getFieldOffset()); + W->printString("Name", Field.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + StaticDataMemberRecord &Field) { + printMemberAttributes(Field.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("Type", Field.getType()); + W->printString("Name", Field.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + VFPtrRecord &VFTable) { + printTypeIndex("Type", VFTable.getType()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + EnumeratorRecord &Enum) { + printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + W->printNumber("EnumValue", Enum.getValue()); + W->printString("Name", Enum.getName()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + BaseClassRecord &Base) { + printMemberAttributes(Base.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("BaseType", Base.getBaseType()); + W->printHex("BaseOffset", Base.getBaseOffset()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + VirtualBaseClassRecord &Base) { + printMemberAttributes(Base.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("BaseType", Base.getBaseType()); + printTypeIndex("VBPtrType", Base.getVBPtrType()); + W->printHex("VBPtrOffset", Base.getVBPtrOffset()); + W->printHex("VBTableIndex", Base.getVTableIndex()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + ListContinuationRecord &Cont) { + printTypeIndex("ContinuationIndex", Cont.getContinuationIndex()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &LR) { + W->printEnum("Mode", uint16_t(LR.Mode), makeArrayRef(LabelTypeEnum)); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + PrecompRecord &Precomp) { + W->printHex("StartIndex", Precomp.getStartTypeIndex()); + W->printHex("Count", Precomp.getTypesCount()); + W->printHex("Signature", Precomp.getSignature()); + W->printString("PrecompFile", Precomp.getPrecompFilePath()); + return Error::success(); +} + +Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, + EndPrecompRecord &EndPrecomp) { + W->printHex("Signature", EndPrecomp.getSignature()); + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeHashing.cpp b/llvm/lib/DebugInfo/CodeView/TypeHashing.cpp new file mode 100644 index 000000000000..2dbc11a84f0b --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeHashing.cpp @@ -0,0 +1,80 @@ +//===- TypeHashing.cpp -------------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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, 8> EmptyHash = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +static std::array<uint8_t, 8> TombstoneHash = { + {0xFF, 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; + S.update(RecordData.take_front(sizeof(RecordPrefix))); + 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()) { + const uint8_t *IndexBytes = reinterpret_cast<const uint8_t *>(&TI); + BytesToHash = makeArrayRef(IndexBytes, sizeof(TypeIndex)); + } else { + if (TI.toArrayIndex() >= Prev.size() || + Prev[TI.toArrayIndex()].empty()) { + // There are references to yet-unhashed records. Suspend hashing for + // this record until all the other records are processed. + return {}; + } + 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().take_back(8)}; +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndex.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndex.cpp new file mode 100644 index 000000000000..604d342448d3 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeIndex.cpp @@ -0,0 +1,106 @@ +//===-- TypeIndex.cpp - CodeView type index ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeIndex.h" + +#include "llvm/DebugInfo/CodeView/TypeCollection.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +struct SimpleTypeEntry { + StringRef Name; + SimpleTypeKind Kind; +}; + +/// The names here all end in "*". If the simple type is a pointer type, we +/// return the whole name. Otherwise we lop off the last character in our +/// StringRef. +static const SimpleTypeEntry SimpleTypeNames[] = { + {"void*", SimpleTypeKind::Void}, + {"<not translated>*", SimpleTypeKind::NotTranslated}, + {"HRESULT*", SimpleTypeKind::HResult}, + {"signed char*", SimpleTypeKind::SignedCharacter}, + {"unsigned char*", SimpleTypeKind::UnsignedCharacter}, + {"char*", SimpleTypeKind::NarrowCharacter}, + {"wchar_t*", SimpleTypeKind::WideCharacter}, + {"char16_t*", SimpleTypeKind::Character16}, + {"char32_t*", SimpleTypeKind::Character32}, + {"__int8*", SimpleTypeKind::SByte}, + {"unsigned __int8*", SimpleTypeKind::Byte}, + {"short*", SimpleTypeKind::Int16Short}, + {"unsigned short*", SimpleTypeKind::UInt16Short}, + {"__int16*", SimpleTypeKind::Int16}, + {"unsigned __int16*", SimpleTypeKind::UInt16}, + {"long*", SimpleTypeKind::Int32Long}, + {"unsigned long*", SimpleTypeKind::UInt32Long}, + {"int*", SimpleTypeKind::Int32}, + {"unsigned*", SimpleTypeKind::UInt32}, + {"__int64*", SimpleTypeKind::Int64Quad}, + {"unsigned __int64*", SimpleTypeKind::UInt64Quad}, + {"__int64*", SimpleTypeKind::Int64}, + {"unsigned __int64*", SimpleTypeKind::UInt64}, + {"__int128*", SimpleTypeKind::Int128}, + {"unsigned __int128*", SimpleTypeKind::UInt128}, + {"__half*", SimpleTypeKind::Float16}, + {"float*", SimpleTypeKind::Float32}, + {"float*", SimpleTypeKind::Float32PartialPrecision}, + {"__float48*", SimpleTypeKind::Float48}, + {"double*", SimpleTypeKind::Float64}, + {"long double*", SimpleTypeKind::Float80}, + {"__float128*", SimpleTypeKind::Float128}, + {"_Complex float*", SimpleTypeKind::Complex32}, + {"_Complex double*", SimpleTypeKind::Complex64}, + {"_Complex long double*", SimpleTypeKind::Complex80}, + {"_Complex __float128*", SimpleTypeKind::Complex128}, + {"bool*", SimpleTypeKind::Boolean8}, + {"__bool16*", SimpleTypeKind::Boolean16}, + {"__bool32*", SimpleTypeKind::Boolean32}, + {"__bool64*", SimpleTypeKind::Boolean64}, +}; +} // namespace + +StringRef TypeIndex::simpleTypeName(TypeIndex TI) { + assert(TI.isNoneType() || TI.isSimple()); + + if (TI.isNoneType()) + return "<no type>"; + + if (TI == TypeIndex::NullptrT()) + return "std::nullptr_t"; + + // This is a simple type. + for (const auto &SimpleTypeName : SimpleTypeNames) { + if (SimpleTypeName.Kind == TI.getSimpleKind()) { + if (TI.getSimpleMode() == SimpleTypeMode::Direct) + return SimpleTypeName.Name.drop_back(1); + // Otherwise, this is a pointer type. We gloss over the distinction + // between near, far, 64, 32, etc, and just give a pointer type. + return SimpleTypeName.Name; + } + } + return "<unknown simple type>"; +} + +void llvm::codeview::printTypeIndex(ScopedPrinter &Printer, StringRef FieldName, + TypeIndex TI, TypeCollection &Types) { + StringRef TypeName; + if (!TI.isNoneType()) { + if (TI.isSimple()) + TypeName = TypeIndex::simpleTypeName(TI); + else + TypeName = Types.getTypeName(TI); + } + + if (!TypeName.empty()) + Printer.printHex(FieldName, TypeName, TI.getIndex()); + else + Printer.printHex(FieldName, TI.getIndex()); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp new file mode 100644 index 000000000000..e84e1c9cea78 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -0,0 +1,522 @@ +//===- TypeIndexDiscovery.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::codeview; + +static inline MethodKind getMethodKind(uint16_t Attrs) { + Attrs &= uint16_t(MethodOptions::MethodKindMask); + Attrs >>= 2; + return MethodKind(Attrs); +} + +static inline bool isIntroVirtual(uint16_t Attrs) { + MethodKind MK = getMethodKind(Attrs); + return MK == MethodKind::IntroducingVirtual || + MK == MethodKind::PureIntroducingVirtual; +} + +static inline PointerMode getPointerMode(uint32_t Attrs) { + return static_cast<PointerMode>((Attrs >> PointerRecord::PointerModeShift) & + PointerRecord::PointerModeMask); +} + +static inline bool isMemberPointer(uint32_t Attrs) { + PointerMode Mode = getPointerMode(Attrs); + return Mode == PointerMode::PointerToDataMember || + Mode == PointerMode::PointerToMemberFunction; +} + +static inline uint32_t getEncodedIntegerLength(ArrayRef<uint8_t> Data) { + uint16_t N = support::endian::read16le(Data.data()); + if (N < LF_NUMERIC) + return 2; + + assert(N <= LF_UQUADWORD); + + constexpr uint32_t Sizes[] = { + 1, // LF_CHAR + 2, // LF_SHORT + 2, // LF_USHORT + 4, // LF_LONG + 4, // LF_ULONG + 4, // LF_REAL32 + 8, // LF_REAL64 + 10, // LF_REAL80 + 16, // LF_REAL128 + 8, // LF_QUADWORD + 8, // LF_UQUADWORD + }; + + return 2 + Sizes[N - LF_NUMERIC]; +} + +static inline uint32_t getCStringLength(ArrayRef<uint8_t> Data) { + const char *S = reinterpret_cast<const char *>(Data.data()); + return strlen(S) + 1; +} + +static void handleMethodOverloadList(ArrayRef<uint8_t> Content, + SmallVectorImpl<TiReference> &Refs) { + uint32_t Offset = 0; + + while (!Content.empty()) { + // Array of: + // 0: Attrs + // 2: Padding + // 4: TypeIndex + // if (isIntroVirtual()) + // 8: VFTableOffset + + // At least 8 bytes are guaranteed. 4 extra bytes come iff function is an + // intro virtual. + uint32_t Len = 8; + + uint16_t Attrs = support::endian::read16le(Content.data()); + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + + if (LLVM_UNLIKELY(isIntroVirtual(Attrs))) + Len += 4; + Offset += Len; + Content = Content.drop_front(Len); + } +} + +static uint32_t handleBaseClass(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Encoded Integer + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getEncodedIntegerLength(Data.drop_front(8)); +} + +static uint32_t handleEnumerator(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: Encoded Integer + // <next>: Name + uint32_t Size = 4 + getEncodedIntegerLength(Data.drop_front(4)); + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleDataMember(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Encoded Integer + // <next>: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + uint32_t Size = 8 + getEncodedIntegerLength(Data.drop_front(8)); + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleOverloadedMethod(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleOneMethod(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Attributes + // 4: Type + // if (isIntroVirtual) + // 8: VFTableOffset + // <next>: Name + uint32_t Size = 8; + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + + uint16_t Attrs = support::endian::read16le(Data.drop_front(2).data()); + if (LLVM_UNLIKELY(isIntroVirtual(Attrs))) + Size += 4; + + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleNestedType(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleStaticDataMember(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleVirtualBaseClass(ArrayRef<uint8_t> Data, uint32_t Offset, + bool IsIndirect, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Attrs + // 4: TypeIndex + // 8: TypeIndex + // 12: Encoded Integer + // <next>: Encoded Integer + uint32_t Size = 12; + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 2}); + Size += getEncodedIntegerLength(Data.drop_front(Size)); + Size += getEncodedIntegerLength(Data.drop_front(Size)); + return Size; +} + +static uint32_t handleVFPtr(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8; +} + +static uint32_t handleListContinuation(ArrayRef<uint8_t> Data, uint32_t Offset, + SmallVectorImpl<TiReference> &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8; +} + +static void handleFieldList(ArrayRef<uint8_t> Content, + SmallVectorImpl<TiReference> &Refs) { + uint32_t Offset = 0; + uint32_t ThisLen = 0; + while (!Content.empty()) { + TypeLeafKind Kind = + static_cast<TypeLeafKind>(support::endian::read16le(Content.data())); + switch (Kind) { + case LF_BCLASS: + ThisLen = handleBaseClass(Content, Offset, Refs); + break; + case LF_ENUMERATE: + ThisLen = handleEnumerator(Content, Offset, Refs); + break; + case LF_MEMBER: + ThisLen = handleDataMember(Content, Offset, Refs); + break; + case LF_METHOD: + ThisLen = handleOverloadedMethod(Content, Offset, Refs); + break; + case LF_ONEMETHOD: + ThisLen = handleOneMethod(Content, Offset, Refs); + break; + case LF_NESTTYPE: + ThisLen = handleNestedType(Content, Offset, Refs); + break; + case LF_STMEMBER: + ThisLen = handleStaticDataMember(Content, Offset, Refs); + break; + case LF_VBCLASS: + case LF_IVBCLASS: + ThisLen = + handleVirtualBaseClass(Content, Offset, Kind == LF_VBCLASS, Refs); + break; + case LF_VFUNCTAB: + ThisLen = handleVFPtr(Content, Offset, Refs); + break; + case LF_INDEX: + ThisLen = handleListContinuation(Content, Offset, Refs); + break; + default: + return; + } + Content = Content.drop_front(ThisLen); + Offset += ThisLen; + if (!Content.empty()) { + uint8_t Pad = Content.front(); + if (Pad >= LF_PAD0) { + uint32_t Skip = Pad & 0x0F; + Content = Content.drop_front(Skip); + Offset += Skip; + } + } + } +} + +static void handlePointer(ArrayRef<uint8_t> Content, + SmallVectorImpl<TiReference> &Refs) { + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + + uint32_t Attrs = support::endian::read32le(Content.drop_front(4).data()); + if (isMemberPointer(Attrs)) + Refs.push_back({TiRefKind::TypeRef, 8, 1}); +} + +static void discoverTypeIndices(ArrayRef<uint8_t> Content, TypeLeafKind Kind, + SmallVectorImpl<TiReference> &Refs) { + uint32_t Count; + // FIXME: In the future it would be nice if we could avoid hardcoding these + // values. One idea is to define some structures representing these types + // that would allow the use of offsetof(). + switch (Kind) { + case TypeLeafKind::LF_FUNC_ID: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); + Refs.push_back({TiRefKind::TypeRef, 4, 1}); + break; + case TypeLeafKind::LF_MFUNC_ID: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_STRING_ID: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); + break; + case TypeLeafKind::LF_SUBSTR_LIST: + Count = support::endian::read32le(Content.data()); + if (Count > 0) + Refs.push_back({TiRefKind::IndexRef, 4, Count}); + break; + case TypeLeafKind::LF_BUILDINFO: + Count = support::endian::read16le(Content.data()); + if (Count > 0) + Refs.push_back({TiRefKind::IndexRef, 2, Count}); + break; + case TypeLeafKind::LF_UDT_SRC_LINE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + Refs.push_back({TiRefKind::IndexRef, 4, 1}); + break; + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_MODIFIER: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_PROCEDURE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + Refs.push_back({TiRefKind::TypeRef, 8, 1}); + break; + case TypeLeafKind::LF_MFUNCTION: + Refs.push_back({TiRefKind::TypeRef, 0, 3}); + Refs.push_back({TiRefKind::TypeRef, 16, 1}); + break; + case TypeLeafKind::LF_ARGLIST: + Count = support::endian::read32le(Content.data()); + if (Count > 0) + Refs.push_back({TiRefKind::TypeRef, 4, Count}); + break; + case TypeLeafKind::LF_ARRAY: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_CLASS: + case TypeLeafKind::LF_STRUCTURE: + case TypeLeafKind::LF_INTERFACE: + Refs.push_back({TiRefKind::TypeRef, 4, 3}); + break; + case TypeLeafKind::LF_UNION: + Refs.push_back({TiRefKind::TypeRef, 4, 1}); + break; + case TypeLeafKind::LF_ENUM: + Refs.push_back({TiRefKind::TypeRef, 4, 2}); + break; + case TypeLeafKind::LF_BITFIELD: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_VFTABLE: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_VTSHAPE: + break; + case TypeLeafKind::LF_METHODLIST: + handleMethodOverloadList(Content, Refs); + break; + case TypeLeafKind::LF_FIELDLIST: + handleFieldList(Content, Refs); + break; + case TypeLeafKind::LF_POINTER: + handlePointer(Content, Refs); + break; + default: + break; + } +} + +static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, + SmallVectorImpl<TiReference> &Refs) { + uint32_t Count; + // FIXME: In the future it would be nice if we could avoid hardcoding these + // values. One idea is to define some structures representing these types + // that would allow the use of offsetof(). + switch (Kind) { + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_LPROC32_DPC: + case SymbolKind::S_LPROC32_DPC_ID: + Refs.push_back({TiRefKind::IndexRef, 24, 1}); // LF_FUNC_ID + break; + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: + Refs.push_back({TiRefKind::TypeRef, 24, 1}); // Type + break; + case SymbolKind::S_UDT: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // UDT + break; + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type + break; + case SymbolKind::S_BUILDINFO: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); // Compile flags + break; + case SymbolKind::S_LTHREAD32: + case SymbolKind::S_GTHREAD32: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type + break; + case SymbolKind::S_FILESTATIC: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); // Type + break; + 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; + case SymbolKind::S_CALLSITEINFO: + Refs.push_back({TiRefKind::TypeRef, 8, 1}); // Call signature + 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 + break; + case SymbolKind::S_INLINESITE: + Refs.push_back({TiRefKind::IndexRef, 8, 1}); // ID of inlinee + break; + case SymbolKind::S_HEAPALLOCSITE: + Refs.push_back({TiRefKind::TypeRef, 8, 1}); // UDT allocated + break; + + // Defranges don't have types, just registers and code offsets. + case SymbolKind::S_DEFRANGE_REGISTER: + case SymbolKind::S_DEFRANGE_REGISTER_REL: + case SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL: + case SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + case SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER: + case SymbolKind::S_DEFRANGE_SUBFIELD: + break; + + // No type references. + case SymbolKind::S_LABEL32: + case SymbolKind::S_OBJNAME: + case SymbolKind::S_COMPILE: + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + case SymbolKind::S_ENVBLOCK: + case SymbolKind::S_BLOCK32: + case SymbolKind::S_FRAMEPROC: + case SymbolKind::S_THUNK32: + case SymbolKind::S_FRAMECOOKIE: + case SymbolKind::S_UNAMESPACE: + break; + // Scope ending symbols. + case SymbolKind::S_END: + case SymbolKind::S_INLINESITE_END: + case SymbolKind::S_PROC_ID_END: + break; + default: + return false; // Unknown symbol. + } + return true; +} + +void llvm::codeview::discoverTypeIndices(const CVType &Type, + SmallVectorImpl<TiReference> &Refs) { + ::discoverTypeIndices(Type.content(), Type.kind(), Refs); +} + +static void resolveTypeIndexReferences(ArrayRef<uint8_t> RecordData, + ArrayRef<TiReference> Refs, + SmallVectorImpl<TypeIndex> &Indices) { + Indices.clear(); + + if (Refs.empty()) + return; + + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + + BinaryStreamReader Reader(RecordData, support::little); + for (const auto &Ref : Refs) { + Reader.setOffset(Ref.Offset); + FixedStreamArray<TypeIndex> Run; + cantFail(Reader.readArray(Run, Ref.Count)); + Indices.append(Run.begin(), Run.end()); + } +} + +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 = + reinterpret_cast<const RecordPrefix *>(RecordData.data()); + TypeLeafKind K = static_cast<TypeLeafKind>(uint16_t(P->RecordKind)); + ::discoverTypeIndices(RecordData.drop_front(sizeof(RecordPrefix)), K, 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/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp new file mode 100644 index 000000000000..8e632f3be460 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp @@ -0,0 +1,52 @@ +//===- TypeRecordHelpers.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +template <typename RecordT> static ClassOptions getUdtOptions(CVType CVT) { + RecordT Record; + if (auto EC = TypeDeserializer::deserializeAs<RecordT>(CVT, Record)) { + consumeError(std::move(EC)); + return ClassOptions::None; + } + return Record.getOptions(); +} + +bool llvm::codeview::isUdtForwardRef(CVType CVT) { + ClassOptions UdtOptions = ClassOptions::None; + switch (CVT.kind()) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT)); + break; + case LF_ENUM: + UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT)); + break; + case LF_UNION: + UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT)); + break; + default: + return false; + } + return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None; +} + +TypeIndex llvm::codeview::getModifiedType(const CVType &CVT) { + assert(CVT.kind() == LF_MODIFIER); + SmallVector<TypeIndex, 1> Refs; + discoverTypeIndices(CVT, Refs); + return Refs.front(); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp new file mode 100644 index 000000000000..1aded589e565 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -0,0 +1,709 @@ +//===- TypeRecordMapping.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { + +#define error(X) \ + if (auto EC = X) \ + return EC; + +static const EnumEntry<TypeLeafKind> LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; + +static StringRef getLeafTypeName(TypeLeafKind LT) { + switch (LT) { +#define TYPE_RECORD(ename, value, name) \ + case ename: \ + return #name; +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + default: + break; + } + return "UnknownLeaf"; +} + +template <typename T> +static bool compEnumNames(const EnumEntry<T> &lhs, const EnumEntry<T> &rhs) { + return lhs.Name < rhs.Name; +} + +template <typename T, typename TFlag> +static std::string getFlagNames(CodeViewRecordIO &IO, T Value, + ArrayRef<EnumEntry<TFlag>> Flags) { + if (!IO.isStreaming()) + return std::string(""); + typedef EnumEntry<TFlag> FlagEntry; + typedef SmallVector<FlagEntry, 10> FlagVector; + FlagVector SetFlags; + for (const auto &Flag : Flags) { + if (Flag.Value == 0) + continue; + if ((Value & Flag.Value) == Flag.Value) { + SetFlags.push_back(Flag); + } + } + + llvm::sort(SetFlags, &compEnumNames<TFlag>); + + std::string FlagLabel; + bool FirstOcc = true; + for (const auto &Flag : SetFlags) { + if (FirstOcc) + FirstOcc = false; + else + FlagLabel += (" | "); + + FlagLabel += (Flag.Name.str() + " (0x" + utohexstr(Flag.Value) + ")"); + } + + if (!FlagLabel.empty()) { + std::string LabelWithBraces(" ( "); + LabelWithBraces += FlagLabel + " )"; + return LabelWithBraces; + } else + return FlagLabel; +} + +template <typename T, typename TEnum> +static StringRef getEnumName(CodeViewRecordIO &IO, T Value, + ArrayRef<EnumEntry<TEnum>> EnumValues) { + if (!IO.isStreaming()) + return ""; + StringRef Name; + for (const auto &EnumItem : EnumValues) { + if (EnumItem.Value == Value) { + Name = EnumItem.Name; + break; + } + } + + return Name; +} + +static std::string getMemberAttributes(CodeViewRecordIO &IO, + MemberAccess Access, MethodKind Kind, + MethodOptions Options) { + if (!IO.isStreaming()) + return ""; + std::string AccessSpecifier = + getEnumName(IO, uint8_t(Access), makeArrayRef(getMemberAccessNames())); + std::string MemberAttrs(AccessSpecifier); + if (Kind != MethodKind::Vanilla) { + std::string MethodKind = + getEnumName(IO, unsigned(Kind), makeArrayRef(getMemberKindNames())); + MemberAttrs += ", " + MethodKind; + } + if (Options != MethodOptions::None) { + std::string MethodOptions = getFlagNames( + IO, unsigned(Options), makeArrayRef(getMethodOptionNames())); + MemberAttrs += ", " + MethodOptions; + } + return MemberAttrs; +} + +struct MapOneMethodRecord { + explicit MapOneMethodRecord(bool IsFromOverloadList) + : IsFromOverloadList(IsFromOverloadList) {} + + Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const { + std::string Attrs = getMemberAttributes( + IO, Method.getAccess(), Method.getMethodKind(), Method.getOptions()); + error(IO.mapInteger(Method.Attrs.Attrs, "Attrs: " + Attrs)); + if (IsFromOverloadList) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding)); + } + error(IO.mapInteger(Method.Type, "Type")); + if (Method.isIntroducingVirtual()) { + error(IO.mapInteger(Method.VFTableOffset, "VFTableOffset")); + } else if (IO.isReading()) + Method.VFTableOffset = -1; + + if (!IsFromOverloadList) + error(IO.mapStringZ(Method.Name, "Name")); + + return Error::success(); + } + +private: + bool IsFromOverloadList; +}; +} // namespace + +static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, + StringRef &UniqueName, bool HasUniqueName) { + if (IO.isWriting()) { + // Try to be smart about what we write here. We can't write anything too + // large, so if we're going to go over the limit, truncate both the name + // and unique name by the same amount. + size_t BytesLeft = IO.maxFieldLength(); + if (HasUniqueName) { + size_t BytesNeeded = Name.size() + UniqueName.size() + 2; + StringRef N = Name; + StringRef U = UniqueName; + if (BytesNeeded > BytesLeft) { + size_t BytesToDrop = (BytesNeeded - BytesLeft); + size_t DropN = std::min(N.size(), BytesToDrop / 2); + size_t DropU = std::min(U.size(), BytesToDrop - DropN); + + N = N.drop_back(DropN); + U = U.drop_back(DropU); + } + + error(IO.mapStringZ(N)); + error(IO.mapStringZ(U)); + } else { + // Cap the length of the string at however many bytes we have available, + // plus one for the required null terminator. + auto N = StringRef(Name).take_front(BytesLeft - 1); + error(IO.mapStringZ(N)); + } + } else { + // Reading & Streaming mode come after writing mode is executed for each + // record. Truncating large names are done during writing, so its not + // necessary to do it while reading or streaming. + error(IO.mapStringZ(Name, "Name")); + if (HasUniqueName) + error(IO.mapStringZ(UniqueName, "LinkageName")); + } + + return Error::success(); +} + +Error TypeRecordMapping::visitTypeBegin(CVType &CVR) { + assert(!TypeKind.hasValue() && "Already in a type mapping!"); + assert(!MemberKind.hasValue() && "Already in a member mapping!"); + + // FieldList and MethodList records can be any length because they can be + // split with continuation records. All other record types cannot be + // longer than the maximum record length. + Optional<uint32_t> MaxLen; + if (CVR.kind() != TypeLeafKind::LF_FIELDLIST && + CVR.kind() != TypeLeafKind::LF_METHODLIST) + MaxLen = MaxRecordLength - sizeof(RecordPrefix); + error(IO.beginRecord(MaxLen)); + TypeKind = CVR.kind(); + + if (IO.isStreaming()) { + auto RecordKind = CVR.kind(); + uint16_t RecordLen = CVR.length() - 2; + std::string RecordKindName = + getEnumName(IO, unsigned(RecordKind), makeArrayRef(LeafTypeNames)); + error(IO.mapInteger(RecordLen, "Record length")); + error(IO.mapEnum(RecordKind, "Record kind: " + RecordKindName)); + } + return Error::success(); +} + +Error TypeRecordMapping::visitTypeBegin(CVType &CVR, TypeIndex Index) { + if (IO.isStreaming()) + IO.emitRawComment(" " + getLeafTypeName(CVR.kind()) + " (0x" + + utohexstr(Index.getIndex()) + ")"); + return visitTypeBegin(CVR); +} + +Error TypeRecordMapping::visitTypeEnd(CVType &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + assert(!MemberKind.hasValue() && "Still in a member mapping!"); + + error(IO.endRecord()); + + TypeKind.reset(); + return Error::success(); +} + +Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + assert(!MemberKind.hasValue() && "Already in a member mapping!"); + + // The largest possible subrecord is one in which there is a record prefix, + // followed by the subrecord, followed by a continuation, and that entire + // sequence spaws `MaxRecordLength` bytes. So the record's length is + // calculated as follows. + + constexpr uint32_t ContinuationLength = 8; + error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) - + ContinuationLength)); + + MemberKind = Record.Kind; + if (IO.isStreaming()) { + std::string MemberKindName = getLeafTypeName(Record.Kind); + MemberKindName += + " ( " + + (getEnumName(IO, unsigned(Record.Kind), makeArrayRef(LeafTypeNames))) + .str() + + " )"; + error(IO.mapEnum(Record.Kind, "Member kind: " + MemberKindName)); + } + return Error::success(); +} + +Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + assert(MemberKind.hasValue() && "Not in a member mapping!"); + + if (IO.isReading()) { + if (auto EC = IO.skipPadding()) + return EC; + } + + MemberKind.reset(); + error(IO.endRecord()); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { + std::string ModifierNames = + getFlagNames(IO, static_cast<uint16_t>(Record.Modifiers), + makeArrayRef(getTypeModifierNames())); + error(IO.mapInteger(Record.ModifiedType, "ModifiedType")); + error(IO.mapEnum(Record.Modifiers, "Modifiers" + ModifierNames)); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + ProcedureRecord &Record) { + std::string CallingConvName = getEnumName( + IO, uint8_t(Record.CallConv), makeArrayRef(getCallingConventions())); + std::string FuncOptionNames = + getFlagNames(IO, static_cast<uint16_t>(Record.Options), + makeArrayRef(getFunctionOptionEnum())); + error(IO.mapInteger(Record.ReturnType, "ReturnType")); + error(IO.mapEnum(Record.CallConv, "CallingConvention: " + CallingConvName)); + error(IO.mapEnum(Record.Options, "FunctionOptions" + FuncOptionNames)); + error(IO.mapInteger(Record.ParameterCount, "NumParameters")); + error(IO.mapInteger(Record.ArgumentList, "ArgListType")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + MemberFunctionRecord &Record) { + std::string CallingConvName = getEnumName( + IO, uint8_t(Record.CallConv), makeArrayRef(getCallingConventions())); + std::string FuncOptionNames = + getFlagNames(IO, static_cast<uint16_t>(Record.Options), + makeArrayRef(getFunctionOptionEnum())); + error(IO.mapInteger(Record.ReturnType, "ReturnType")); + error(IO.mapInteger(Record.ClassType, "ClassType")); + error(IO.mapInteger(Record.ThisType, "ThisType")); + error(IO.mapEnum(Record.CallConv, "CallingConvention: " + CallingConvName)); + error(IO.mapEnum(Record.Options, "FunctionOptions" + FuncOptionNames)); + error(IO.mapInteger(Record.ParameterCount, "NumParameters")); + error(IO.mapInteger(Record.ArgumentList, "ArgListType")); + error(IO.mapInteger(Record.ThisPointerAdjustment, "ThisAdjustment")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) { + error(IO.mapVectorN<uint32_t>( + Record.ArgIndices, + [](CodeViewRecordIO &IO, TypeIndex &N) { + return IO.mapInteger(N, "Argument"); + }, + "NumArgs")); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + StringListRecord &Record) { + error(IO.mapVectorN<uint32_t>( + Record.StringIndices, + [](CodeViewRecordIO &IO, TypeIndex &N) { + return IO.mapInteger(N, "Strings"); + }, + "NumStrings")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) { + + SmallString<128> Attr("Attrs: "); + + if (IO.isStreaming()) { + std::string PtrType = getEnumName(IO, unsigned(Record.getPointerKind()), + makeArrayRef(getPtrKindNames())); + Attr += "[ Type: " + PtrType; + + std::string PtrMode = getEnumName(IO, unsigned(Record.getMode()), + makeArrayRef(getPtrModeNames())); + Attr += ", Mode: " + PtrMode; + + auto PtrSizeOf = Record.getSize(); + Attr += ", SizeOf: " + itostr(PtrSizeOf); + + if (Record.isFlat()) + Attr += ", isFlat"; + if (Record.isConst()) + Attr += ", isConst"; + if (Record.isVolatile()) + Attr += ", isVolatile"; + if (Record.isUnaligned()) + Attr += ", isUnaligned"; + if (Record.isRestrict()) + Attr += ", isRestricted"; + if (Record.isLValueReferenceThisPtr()) + Attr += ", isThisPtr&"; + if (Record.isRValueReferenceThisPtr()) + Attr += ", isThisPtr&&"; + Attr += " ]"; + } + + error(IO.mapInteger(Record.ReferentType, "PointeeType")); + error(IO.mapInteger(Record.Attrs, Attr)); + + if (Record.isPointerToMember()) { + if (IO.isReading()) + Record.MemberInfo.emplace(); + + MemberPointerInfo &M = *Record.MemberInfo; + error(IO.mapInteger(M.ContainingType, "ClassType")); + std::string PtrMemberGetRepresentation = getEnumName( + IO, uint16_t(M.Representation), makeArrayRef(getPtrMemberRepNames())); + error(IO.mapEnum(M.Representation, + "Representation: " + PtrMemberGetRepresentation)); + } + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) { + error(IO.mapInteger(Record.ElementType, "ElementType")); + error(IO.mapInteger(Record.IndexType, "IndexType")); + error(IO.mapEncodedInteger(Record.Size, "SizeOf")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) { + assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) || + (CVR.kind() == TypeLeafKind::LF_CLASS) || + (CVR.kind() == TypeLeafKind::LF_INTERFACE)); + + std::string PropertiesNames = + getFlagNames(IO, static_cast<uint16_t>(Record.Options), + makeArrayRef(getClassOptionNames())); + error(IO.mapInteger(Record.MemberCount, "MemberCount")); + error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); + error(IO.mapInteger(Record.FieldList, "FieldList")); + error(IO.mapInteger(Record.DerivationList, "DerivedFrom")); + error(IO.mapInteger(Record.VTableShape, "VShape")); + error(IO.mapEncodedInteger(Record.Size, "SizeOf")); + error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, + Record.hasUniqueName())); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) { + std::string PropertiesNames = + getFlagNames(IO, static_cast<uint16_t>(Record.Options), + makeArrayRef(getClassOptionNames())); + error(IO.mapInteger(Record.MemberCount, "MemberCount")); + error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); + error(IO.mapInteger(Record.FieldList, "FieldList")); + error(IO.mapEncodedInteger(Record.Size, "SizeOf")); + error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, + Record.hasUniqueName())); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) { + std::string PropertiesNames = + getFlagNames(IO, static_cast<uint16_t>(Record.Options), + makeArrayRef(getClassOptionNames())); + error(IO.mapInteger(Record.MemberCount, "NumEnumerators")); + error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); + error(IO.mapInteger(Record.UnderlyingType, "UnderlyingType")); + error(IO.mapInteger(Record.FieldList, "FieldListType")); + error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, + Record.hasUniqueName())); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) { + error(IO.mapInteger(Record.Type, "Type")); + error(IO.mapInteger(Record.BitSize, "BitSize")); + error(IO.mapInteger(Record.BitOffset, "BitOffset")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Record) { + uint16_t Size; + if (!IO.isReading()) { + ArrayRef<VFTableSlotKind> Slots = Record.getSlots(); + Size = Slots.size(); + error(IO.mapInteger(Size, "VFEntryCount")); + + for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { + uint8_t Byte = static_cast<uint8_t>(Slots[SlotIndex]) << 4; + if ((SlotIndex + 1) < Slots.size()) { + Byte |= static_cast<uint8_t>(Slots[SlotIndex + 1]); + } + error(IO.mapInteger(Byte)); + } + } else { + error(IO.mapInteger(Size)); + for (uint16_t I = 0; I < Size; I += 2) { + uint8_t Byte; + error(IO.mapInteger(Byte)); + Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF)); + if ((I + 1) < Size) + Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4)); + } + } + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) { + error(IO.mapInteger(Record.CompleteClass, "CompleteClass")); + error(IO.mapInteger(Record.OverriddenVFTable, "OverriddenVFTable")); + error(IO.mapInteger(Record.VFPtrOffset, "VFPtrOffset")); + uint32_t NamesLen = 0; + if (!IO.isReading()) { + for (auto Name : Record.MethodNames) + NamesLen += Name.size() + 1; + } + error(IO.mapInteger(NamesLen)); + error(IO.mapVectorTail( + Record.MethodNames, + [](CodeViewRecordIO &IO, StringRef &S) { + return IO.mapStringZ(S, "MethodName"); + }, + "VFTableName")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) { + error(IO.mapInteger(Record.Id, "Id")); + error(IO.mapStringZ(Record.String, "StringData")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &Record) { + error(IO.mapInteger(Record.UDT, "UDT")); + error(IO.mapInteger(Record.SourceFile, "SourceFile")); + error(IO.mapInteger(Record.LineNumber, "LineNumber")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &Record) { + error(IO.mapInteger(Record.UDT, "UDT")); + error(IO.mapInteger(Record.SourceFile, "SourceFile")); + error(IO.mapInteger(Record.LineNumber, "LineNumber")); + error(IO.mapInteger(Record.Module, "Module")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) { + error(IO.mapInteger(Record.ParentScope, "ParentScope")); + error(IO.mapInteger(Record.FunctionType, "FunctionType")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + MemberFuncIdRecord &Record) { + error(IO.mapInteger(Record.ClassType, "ClassType")); + error(IO.mapInteger(Record.FunctionType, "FunctionType")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + BuildInfoRecord &Record) { + error(IO.mapVectorN<uint16_t>( + Record.ArgIndices, + [](CodeViewRecordIO &IO, TypeIndex &N) { + return IO.mapInteger(N, "Argument"); + }, + "NumArgs")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + MethodOverloadListRecord &Record) { + // TODO: Split the list into multiple records if it's longer than 64KB, using + // a subrecord of TypeRecordKind::Index to chain the records together. + error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true), "Method")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + FieldListRecord &Record) { + if (IO.isStreaming()) { + if (auto EC = codeview::visitMemberRecordStream(Record.Data, *this)) + return EC; + } else + error(IO.mapByteVectorTail(Record.Data)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + TypeServer2Record &Record) { + error(IO.mapGuid(Record.Guid, "Guid")); + error(IO.mapInteger(Record.Age, "Age")); + error(IO.mapStringZ(Record.Name, "Name")); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) { + std::string ModeName = + getEnumName(IO, uint16_t(Record.Mode), makeArrayRef(getLabelTypeEnum())); + error(IO.mapEnum(Record.Mode, "Mode: " + ModeName)); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + BaseClassRecord &Record) { + std::string Attrs = getMemberAttributes( + IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); + error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); + error(IO.mapInteger(Record.Type, "BaseType")); + error(IO.mapEncodedInteger(Record.Offset, "BaseOffset")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + EnumeratorRecord &Record) { + std::string Attrs = getMemberAttributes( + IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); + error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); + + // FIXME: Handle full APInt such as __int128. + error(IO.mapEncodedInteger(Record.Value, "EnumValue")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + DataMemberRecord &Record) { + std::string Attrs = getMemberAttributes( + IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); + error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); + error(IO.mapInteger(Record.Type, "Type")); + error(IO.mapEncodedInteger(Record.FieldOffset, "FieldOffset")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + OverloadedMethodRecord &Record) { + error(IO.mapInteger(Record.NumOverloads, "MethodCount")); + error(IO.mapInteger(Record.MethodList, "MethodListIndex")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + OneMethodRecord &Record) { + const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); + MapOneMethodRecord Mapper(IsFromOverloadList); + return Mapper(IO, Record); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Record) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding, "Padding")); + error(IO.mapInteger(Record.Type, "Type")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + StaticDataMemberRecord &Record) { + + std::string Attrs = getMemberAttributes( + IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); + error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); + error(IO.mapInteger(Record.Type, "Type")); + error(IO.mapStringZ(Record.Name, "Name")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + VirtualBaseClassRecord &Record) { + + std::string Attrs = getMemberAttributes( + IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); + error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); + error(IO.mapInteger(Record.BaseType, "BaseType")); + error(IO.mapInteger(Record.VBPtrType, "VBPtrType")); + error(IO.mapEncodedInteger(Record.VBPtrOffset, "VBPtrOffset")); + error(IO.mapEncodedInteger(Record.VTableIndex, "VBTableIndex")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + VFPtrRecord &Record) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding, "Padding")); + error(IO.mapInteger(Record.Type, "Type")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + ListContinuationRecord &Record) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding, "Padding")); + error(IO.mapInteger(Record.ContinuationIndex, "ContinuationIndex")); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + PrecompRecord &Precomp) { + error(IO.mapInteger(Precomp.StartTypeIndex, "StartIndex")); + error(IO.mapInteger(Precomp.TypesCount, "Count")); + error(IO.mapInteger(Precomp.Signature, "Signature")); + error(IO.mapStringZ(Precomp.PrecompFilePath, "PrecompFile")); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + EndPrecompRecord &EndPrecomp) { + error(IO.mapInteger(EndPrecomp.Signature, "Signature")); + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp new file mode 100644 index 000000000000..aba0e96d606e --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -0,0 +1,493 @@ +//===-- TypeStreamMerger.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +using namespace llvm::codeview; + +static inline size_t slotForIndex(TypeIndex Idx) { + assert(!Idx.isSimple() && "simple type indices have no slots"); + return Idx.getIndex() - TypeIndex::FirstNonSimpleIndex; +} + +namespace { + +/// Implementation of CodeView type stream merging. +/// +/// A CodeView type stream is a series of records that reference each other +/// through type indices. A type index is either "simple", meaning it is less +/// than 0x1000 and refers to a builtin type, or it is complex, meaning it +/// refers to a prior type record in the current stream. The type index of a +/// record is equal to the number of records before it in the stream plus +/// 0x1000. +/// +/// Type records are only allowed to use type indices smaller than their own, so +/// a type stream is effectively a topologically sorted DAG. Cycles occuring in +/// the type graph of the source program are resolved with forward declarations +/// of composite types. This class implements the following type stream merging +/// algorithm, which relies on this DAG structure: +/// +/// - Begin with a new empty stream, and a new empty hash table that maps from +/// type record contents to new type index. +/// - For each new type stream, maintain a map from source type index to +/// destination type index. +/// - For each record, copy it and rewrite its type indices to be valid in the +/// destination type stream. +/// - If the new type record is not already present in the destination stream +/// hash table, append it to the destination type stream, assign it the next +/// type index, and update the two hash tables. +/// - If the type record already exists in the destination stream, discard it +/// and update the type index map to forward the source type index to the +/// existing destination type index. +/// +/// As an additional complication, type stream merging actually produces two +/// streams: an item (or IPI) stream and a type stream, as this is what is +/// actually stored in the final PDB. We choose which records go where by +/// looking at the record kind. +class TypeStreamMerger { +public: + explicit TypeStreamMerger(SmallVectorImpl<TypeIndex> &SourceToDest) + : IndexMap(SourceToDest) { + // When dealing with precompiled headers objects, all data in SourceToDest + // belongs to the precompiled headers object, and is assumed to be already + // remapped to the target PDB. Any forthcoming type that will be merged in + // might potentially back-reference this data. We also don't want to resolve + // twice the types in the precompiled object. + CurIndex += SourceToDest.size(); + } + + static const TypeIndex Untranslated; + + // Local hashing entry points + Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds, + MergingTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, Optional<uint32_t> &S); + Error mergeIdRecords(MergingTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids); + Error mergeTypeRecords(MergingTypeTableBuilder &Dest, + const CVTypeArray &Types); + + // Global hashing entry points + Error mergeTypesAndIds(GlobalTypeTableBuilder &DestIds, + GlobalTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes, + Optional<uint32_t> &S); + Error mergeIdRecords(GlobalTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids, + ArrayRef<GloballyHashedType> Hashes); + Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes, + Optional<uint32_t> &S); + +private: + Error doit(const CVTypeArray &Types); + + Error remapAllTypes(const CVTypeArray &Types); + + Error remapType(const CVType &Type); + + void addMapping(TypeIndex Idx); + + inline bool remapTypeIndex(TypeIndex &Idx) { + // If we're mapping a pure index stream, then IndexMap only contains + // mappings from OldIdStream -> NewIdStream, in which case we will need to + // use the 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 (!hasTypeStream()) + return remapIndex(Idx, TypeLookup); + + assert(TypeLookup.empty()); + return remapIndex(Idx, IndexMap); + } + inline bool remapItemIndex(TypeIndex &Idx) { + assert(hasIdStream()); + return remapIndex(Idx, IndexMap); + } + + bool hasTypeStream() const { + return (UseGlobalHashes) ? (!!DestGlobalTypeStream) : (!!DestTypeStream); + } + + bool hasIdStream() const { + return (UseGlobalHashes) ? (!!DestGlobalIdStream) : (!!DestIdStream); + } + + ArrayRef<uint8_t> remapIndices(const CVType &OriginalType, + MutableArrayRef<uint8_t> Storage); + + inline bool remapIndex(TypeIndex &Idx, ArrayRef<TypeIndex> Map) { + if (LLVM_LIKELY(remapIndexSimple(Idx, Map))) + return true; + + return remapIndexFallback(Idx, Map); + } + + inline bool remapIndexSimple(TypeIndex &Idx, ArrayRef<TypeIndex> Map) const { + // Simple types are unchanged. + if (Idx.isSimple()) + return true; + + // Check if this type index refers to a record we've already translated + // successfully. If it refers to a type later in the stream or a record we + // had to defer, defer it until later pass. + unsigned MapPos = slotForIndex(Idx); + if (LLVM_UNLIKELY(MapPos >= Map.size() || Map[MapPos] == Untranslated)) + return false; + + Idx = Map[MapPos]; + return true; + } + + bool remapIndexFallback(TypeIndex &Idx, ArrayRef<TypeIndex> Map); + + Error errorCorruptRecord() const { + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); + } + + Expected<bool> shouldRemapType(const CVType &Type); + + Optional<Error> LastError; + + bool UseGlobalHashes = false; + + bool IsSecondPass = false; + + unsigned NumBadIndices = 0; + + TypeIndex CurIndex{TypeIndex::FirstNonSimpleIndex}; + + 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. + ArrayRef<TypeIndex> TypeLookup; + + /// 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; + + Optional<uint32_t> PCHSignature; +}; + +} // end anonymous namespace + +const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); + +static bool isIdRecord(TypeLeafKind K) { + switch (K) { + case TypeLeafKind::LF_FUNC_ID: + case TypeLeafKind::LF_MFUNC_ID: + case TypeLeafKind::LF_STRING_ID: + case TypeLeafKind::LF_SUBSTR_LIST: + case TypeLeafKind::LF_BUILDINFO: + case TypeLeafKind::LF_UDT_SRC_LINE: + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + return true; + default: + return false; + } +} + +void TypeStreamMerger::addMapping(TypeIndex Idx) { + if (!IsSecondPass) { + assert(IndexMap.size() == slotForIndex(CurIndex) && + "visitKnownRecord should add one index map entry"); + IndexMap.push_back(Idx); + } else { + assert(slotForIndex(CurIndex) < IndexMap.size()); + IndexMap[slotForIndex(CurIndex)] = Idx; + } +} + +bool TypeStreamMerger::remapIndexFallback(TypeIndex &Idx, + ArrayRef<TypeIndex> Map) { + size_t MapPos = slotForIndex(Idx); + + // If this is the second pass and this index isn't in the map, then it points + // outside the current type stream, and this is a corrupt record. + if (IsSecondPass && MapPos >= Map.size()) { + // FIXME: Print a more useful error. We can give the current record and the + // index that we think its pointing to. + if (LastError) + LastError = joinErrors(std::move(*LastError), errorCorruptRecord()); + else + LastError = errorCorruptRecord(); + } + + ++NumBadIndices; + + // This type index is invalid. Remap this to "not translated by cvpack", + // and return failure. + Idx = Untranslated; + return false; +} + +// Local hashing entry points +Error TypeStreamMerger::mergeTypeRecords(MergingTypeTableBuilder &Dest, + const CVTypeArray &Types) { + DestTypeStream = &Dest; + UseGlobalHashes = false; + + return doit(Types); +} + +Error TypeStreamMerger::mergeIdRecords(MergingTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids) { + DestIdStream = &Dest; + TypeLookup = TypeSourceToDest; + UseGlobalHashes = false; + + return doit(Ids); +} + +Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds, + MergingTypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes, + Optional<uint32_t> &S) { + DestIdStream = &DestIds; + DestTypeStream = &DestTypes; + UseGlobalHashes = false; + auto Err = doit(IdsAndTypes); + S = PCHSignature; + return Err; +} + +// Global hashing entry points +Error TypeStreamMerger::mergeTypeRecords(GlobalTypeTableBuilder &Dest, + const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes, + Optional<uint32_t> &S) { + DestGlobalTypeStream = &Dest; + UseGlobalHashes = true; + GlobalHashes = Hashes; + auto Err = doit(Types); + S = PCHSignature; + return Err; +} + +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, + Optional<uint32_t> &S) { + DestGlobalIdStream = &DestIds; + DestGlobalTypeStream = &DestTypes; + UseGlobalHashes = true; + GlobalHashes = Hashes; + auto Err = doit(IdsAndTypes); + S = PCHSignature; + return Err; +} + +Error TypeStreamMerger::doit(const CVTypeArray &Types) { + if (auto EC = remapAllTypes(Types)) + return EC; + + // If we found bad indices but no other errors, try doing another pass and see + // if we can resolve the indices that weren't in the map on the first pass. + // This may require multiple passes, but we should always make progress. MASM + // is the only known CodeView producer that makes type streams that aren't + // topologically sorted. The standard library contains MASM-produced objects, + // so this is important to handle correctly, but we don't have to be too + // efficient. MASM type streams are usually very small. + while (!LastError && NumBadIndices > 0) { + unsigned BadIndicesRemaining = NumBadIndices; + IsSecondPass = true; + NumBadIndices = 0; + CurIndex = TypeIndex(TypeIndex::FirstNonSimpleIndex); + + if (auto EC = remapAllTypes(Types)) + return EC; + + assert(NumBadIndices <= BadIndicesRemaining && + "second pass found more bad indices"); + if (!LastError && NumBadIndices == BadIndicesRemaining) { + return llvm::make_error<CodeViewError>( + cv_error_code::corrupt_record, "Input type graph contains cycles"); + } + } + + if (LastError) + return std::move(*LastError); + return Error::success(); +} + +Error TypeStreamMerger::remapAllTypes(const CVTypeArray &Types) { + BinaryStreamRef Stream = Types.getUnderlyingStream(); + ArrayRef<uint8_t> Buffer; + cantFail(Stream.readBytes(0, Stream.getLength(), Buffer)); + + return forEachCodeViewRecord<CVType>( + Buffer, [this](const CVType &T) { return remapType(T); }); +} + +Error TypeStreamMerger::remapType(const CVType &Type) { + auto R = shouldRemapType(Type); + if (!R) + return R.takeError(); + + TypeIndex DestIdx = Untranslated; + if (*R) { + auto DoSerialize = + [this, Type](MutableArrayRef<uint8_t> Storage) -> ArrayRef<uint8_t> { + return remapIndices(Type, Storage); + }; + if (LLVM_LIKELY(UseGlobalHashes)) { + GlobalTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestGlobalIdStream : *DestGlobalTypeStream; + GloballyHashedType H = GlobalHashes[CurIndex.toArrayIndex()]; + DestIdx = Dest.insertRecordAs(H, Type.RecordData.size(), DoSerialize); + } else { + MergingTypeTableBuilder &Dest = + isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream; + + RemapStorage.resize(Type.RecordData.size()); + ArrayRef<uint8_t> Result = DoSerialize(RemapStorage); + if (!Result.empty()) + DestIdx = Dest.insertRecordBytes(Result); + } + } + addMapping(DestIdx); + + ++CurIndex; + assert((IsSecondPass || IndexMap.size() == slotForIndex(CurIndex)) && + "visitKnownRecord should add one index map entry"); + return Error::success(); +} + +ArrayRef<uint8_t> +TypeStreamMerger::remapIndices(const CVType &OriginalType, + MutableArrayRef<uint8_t> Storage) { + SmallVector<TiReference, 4> Refs; + discoverTypeIndices(OriginalType.RecordData, Refs); + if (Refs.empty()) + return OriginalType.RecordData; + + ::memcpy(Storage.data(), OriginalType.RecordData.data(), + OriginalType.RecordData.size()); + + uint8_t *DestContent = Storage.data() + sizeof(RecordPrefix); + + for (auto &Ref : Refs) { + TypeIndex *DestTIs = + reinterpret_cast<TypeIndex *>(DestContent + Ref.Offset); + + for (size_t I = 0; I < Ref.Count; ++I) { + TypeIndex &TI = DestTIs[I]; + bool Success = (Ref.Kind == TiRefKind::IndexRef) ? remapItemIndex(TI) + : remapTypeIndex(TI); + if (LLVM_UNLIKELY(!Success)) + return {}; + } + } + return Storage; +} + +Error llvm::codeview::mergeTypeRecords(MergingTypeTableBuilder &Dest, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Types) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypeRecords(Dest, Types); +} + +Error llvm::codeview::mergeIdRecords(MergingTypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Ids) { + TypeStreamMerger M(SourceToDest); + return M.mergeIdRecords(Dest, TypeSourceToDest, Ids); +} + +Error llvm::codeview::mergeTypeAndIdRecords( + MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, + SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, + Optional<uint32_t> &PCHSignature) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, PCHSignature); +} + +Error llvm::codeview::mergeTypeAndIdRecords( + GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, + SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes, + ArrayRef<GloballyHashedType> Hashes, Optional<uint32_t> &PCHSignature) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes, + PCHSignature); +} + +Error llvm::codeview::mergeTypeRecords(GlobalTypeTableBuilder &Dest, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Types, + ArrayRef<GloballyHashedType> Hashes, + Optional<uint32_t> &PCHSignature) { + TypeStreamMerger M(SourceToDest); + return M.mergeTypeRecords(Dest, Types, Hashes, PCHSignature); +} + +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); +} + +Expected<bool> TypeStreamMerger::shouldRemapType(const CVType &Type) { + // For object files containing precompiled types, we need to extract the + // signature, through EndPrecompRecord. This is done here for performance + // reasons, to avoid re-parsing the Types stream. + if (Type.kind() == LF_ENDPRECOMP) { + EndPrecompRecord EP; + if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), + EP)) + return joinErrors(std::move(EC), errorCorruptRecord()); + if (PCHSignature.hasValue()) + return errorCorruptRecord(); + PCHSignature.emplace(EP.getSignature()); + return false; + } + return true; +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp b/llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp new file mode 100644 index 000000000000..e13068b5b1eb --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp @@ -0,0 +1,60 @@ +//===- TypeTableCollection.cpp -------------------------------- *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeTableCollection.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/Support/BinaryStreamReader.h" + +using namespace llvm; +using namespace llvm::codeview; + +TypeTableCollection::TypeTableCollection(ArrayRef<ArrayRef<uint8_t>> Records) + : NameStorage(Allocator), Records(Records) { + Names.resize(Records.size()); +} + +Optional<TypeIndex> TypeTableCollection::getFirst() { + if (empty()) + return None; + return TypeIndex::fromArrayIndex(0); +} + +Optional<TypeIndex> TypeTableCollection::getNext(TypeIndex Prev) { + assert(contains(Prev)); + ++Prev; + if (Prev.toArrayIndex() == size()) + return None; + return Prev; +} + +CVType TypeTableCollection::getType(TypeIndex Index) { + assert(Index.toArrayIndex() < Records.size()); + return CVType(Records[Index.toArrayIndex()]); +} + +StringRef TypeTableCollection::getTypeName(TypeIndex Index) { + if (Index.isNoneType() || Index.isSimple()) + return TypeIndex::simpleTypeName(Index); + + uint32_t I = Index.toArrayIndex(); + if (Names[I].data() == nullptr) { + StringRef Result = NameStorage.save(computeTypeName(*this, Index)); + Names[I] = Result; + } + return Names[I]; +} + +bool TypeTableCollection::contains(TypeIndex Index) { + return Index.toArrayIndex() <= size(); +} + +uint32_t TypeTableCollection::size() { return Records.size(); } + +uint32_t TypeTableCollection::capacity() { return Records.size(); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 000000000000..abbea3a868c8 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,215 @@ +//===- DWARFAbbreviationDeclaration.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" + +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +void DWARFAbbreviationDeclaration::clear() { + Code = 0; + Tag = DW_TAG_null; + CodeByteSize = 0; + HasChildren = false; + AttributeSpecs.clear(); + FixedAttributeSize.reset(); +} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() { + clear(); +} + +bool +DWARFAbbreviationDeclaration::extract(DataExtractor Data, + uint64_t* OffsetPtr) { + clear(); + const uint64_t Offset = *OffsetPtr; + Code = Data.getULEB128(OffsetPtr); + if (Code == 0) { + return false; + } + CodeByteSize = *OffsetPtr - Offset; + Tag = static_cast<llvm::dwarf::Tag>(Data.getULEB128(OffsetPtr)); + if (Tag == DW_TAG_null) { + clear(); + return false; + } + uint8_t ChildrenByte = Data.getU8(OffsetPtr); + HasChildren = (ChildrenByte == DW_CHILDREN_yes); + // Assign a value to our optional FixedAttributeSize member variable. If + // this member variable still has a value after the while loop below, then + // all attribute data in this abbreviation declaration has a fixed byte size. + FixedAttributeSize = FixedSizeInfo(); + + // Read all of the abbreviation attributes and forms. + while (true) { + auto A = static_cast<Attribute>(Data.getULEB128(OffsetPtr)); + auto F = static_cast<Form>(Data.getULEB128(OffsetPtr)); + if (A && F) { + bool IsImplicitConst = (F == DW_FORM_implicit_const); + if (IsImplicitConst) { + int64_t V = Data.getSLEB128(OffsetPtr); + AttributeSpecs.push_back(AttributeSpec(A, F, V)); + continue; + } + Optional<uint8_t> ByteSize; + // If this abbrevation still has a fixed byte size, then update the + // FixedAttributeSize as needed. + switch (F) { + case DW_FORM_addr: + if (FixedAttributeSize) + ++FixedAttributeSize->NumAddrs; + break; + + case DW_FORM_ref_addr: + if (FixedAttributeSize) + ++FixedAttributeSize->NumRefAddrs; + break; + + case DW_FORM_strp: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + case DW_FORM_line_strp: + case DW_FORM_sec_offset: + case DW_FORM_strp_sup: + if (FixedAttributeSize) + ++FixedAttributeSize->NumDwarfOffsets; + break; + + default: + // The form has a byte size that doesn't depend on Params. + // If it's a fixed size, keep track of it. + if ((ByteSize = dwarf::getFixedFormByteSize(F, dwarf::FormParams()))) { + if (FixedAttributeSize) + FixedAttributeSize->NumBytes += *ByteSize; + break; + } + // Indicate we no longer have a fixed byte size for this + // abbreviation by clearing the FixedAttributeSize optional value + // so it doesn't have a value. + FixedAttributeSize.reset(); + break; + } + // Record this attribute and its fixed size if it has one. + AttributeSpecs.push_back(AttributeSpec(A, F, ByteSize)); + } else if (A == 0 && F == 0) { + // We successfully reached the end of this abbreviation declaration + // since both attribute and form are zero. + break; + } else { + // Attribute and form pairs must either both be non-zero, in which case + // they are added to the abbreviation declaration, or both be zero to + // terminate the abbrevation declaration. In this case only one was + // zero which is an error. + clear(); + return false; + } + } + return true; +} + +void DWARFAbbreviationDeclaration::dump(raw_ostream &OS) const { + OS << '[' << getCode() << "] "; + OS << formatv("{0}", getTag()); + OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no") << '\n'; + for (const AttributeSpec &Spec : AttributeSpecs) { + OS << formatv("\t{0}\t{1}", Spec.Attr, Spec.Form); + if (Spec.isImplicitConst()) + OS << '\t' << Spec.getImplicitConstValue(); + OS << '\n'; + } + OS << '\n'; +} + +Optional<uint32_t> +DWARFAbbreviationDeclaration::findAttributeIndex(dwarf::Attribute Attr) const { + for (uint32_t i = 0, e = AttributeSpecs.size(); i != e; ++i) { + if (AttributeSpecs[i].Attr == Attr) + return i; + } + return None; +} + +Optional<DWARFFormValue> DWARFAbbreviationDeclaration::getAttributeValue( + const uint64_t DIEOffset, const dwarf::Attribute Attr, + const DWARFUnit &U) const { + Optional<uint32_t> MatchAttrIndex = findAttributeIndex(Attr); + if (!MatchAttrIndex) + return None; + + auto DebugInfoData = U.getDebugInfoExtractor(); + + // Add the byte size of ULEB that for the abbrev Code so we can start + // skipping the attribute data. + uint64_t Offset = DIEOffset + CodeByteSize; + uint32_t AttrIndex = 0; + for (const auto &Spec : AttributeSpecs) { + if (*MatchAttrIndex == AttrIndex) { + // We have arrived at the attribute to extract, extract if from Offset. + if (Spec.isImplicitConst()) + return DWARFFormValue::createFromSValue(Spec.Form, + Spec.getImplicitConstValue()); + + DWARFFormValue FormValue(Spec.Form); + if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U)) + return FormValue; + } + // March Offset along until we get to the attribute we want. + if (auto FixedSize = Spec.getByteSize(U)) + Offset += *FixedSize; + else + DWARFFormValue::skipValue(Spec.Form, DebugInfoData, &Offset, + U.getFormParams()); + ++AttrIndex; + } + return None; +} + +size_t DWARFAbbreviationDeclaration::FixedSizeInfo::getByteSize( + const DWARFUnit &U) const { + size_t ByteSize = NumBytes; + if (NumAddrs) + ByteSize += NumAddrs * U.getAddressByteSize(); + if (NumRefAddrs) + ByteSize += NumRefAddrs * U.getRefAddrByteSize(); + if (NumDwarfOffsets) + ByteSize += NumDwarfOffsets * U.getDwarfOffsetByteSize(); + return ByteSize; +} + +Optional<int64_t> DWARFAbbreviationDeclaration::AttributeSpec::getByteSize( + const DWARFUnit &U) const { + if (isImplicitConst()) + return 0; + if (ByteSize.HasByteSize) + return ByteSize.ByteSize; + Optional<int64_t> S; + auto FixedByteSize = dwarf::getFixedFormByteSize(Form, U.getFormParams()); + if (FixedByteSize) + S = *FixedByteSize; + return S; +} + +Optional<size_t> DWARFAbbreviationDeclaration::getFixedAttributesByteSize( + const DWARFUnit &U) const { + if (FixedAttributeSize) + return FixedAttributeSize->getByteSize(U); + return None; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp new file mode 100644 index 000000000000..875f5e9989a0 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -0,0 +1,889 @@ +//===- DWARFAcceleratorTable.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" +#include <cstddef> +#include <cstdint> +#include <utility> + +using namespace llvm; + +namespace { +struct Atom { + unsigned Value; +}; + +static raw_ostream &operator<<(raw_ostream &OS, const Atom &A) { + StringRef Str = dwarf::AtomTypeString(A.Value); + if (!Str.empty()) + return OS << Str; + return OS << "DW_ATOM_unknown_" << format("%x", A.Value); +} +} // namespace + +static Atom formatAtom(unsigned Atom) { return {Atom}; } + +DWARFAcceleratorTable::~DWARFAcceleratorTable() = default; + +Error AppleAcceleratorTable::extract() { + uint64_t Offset = 0; + + // Check that we can at least read the header. + if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength) + 4)) + return createStringError(errc::illegal_byte_sequence, + "Section too small: cannot read header."); + + Hdr.Magic = AccelSection.getU32(&Offset); + Hdr.Version = AccelSection.getU16(&Offset); + Hdr.HashFunction = AccelSection.getU16(&Offset); + Hdr.BucketCount = AccelSection.getU32(&Offset); + Hdr.HashCount = AccelSection.getU32(&Offset); + Hdr.HeaderDataLength = AccelSection.getU32(&Offset); + + // Check that we can read all the hashes and offsets from the + // section (see SourceLevelDebugging.rst for the structure of the index). + // We need to substract one because we're checking for an *offset* which is + // equal to the size for an empty table and hence pointer after the section. + if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength + + Hdr.BucketCount * 4 + Hdr.HashCount * 8 - 1)) + return createStringError( + errc::illegal_byte_sequence, + "Section too small: cannot read buckets and hashes."); + + HdrData.DIEOffsetBase = AccelSection.getU32(&Offset); + uint32_t NumAtoms = AccelSection.getU32(&Offset); + + for (unsigned i = 0; i < NumAtoms; ++i) { + uint16_t AtomType = AccelSection.getU16(&Offset); + auto AtomForm = static_cast<dwarf::Form>(AccelSection.getU16(&Offset)); + HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm)); + } + + IsValid = true; + return Error::success(); +} + +uint32_t AppleAcceleratorTable::getNumBuckets() { return Hdr.BucketCount; } +uint32_t AppleAcceleratorTable::getNumHashes() { return Hdr.HashCount; } +uint32_t AppleAcceleratorTable::getSizeHdr() { return sizeof(Hdr); } +uint32_t AppleAcceleratorTable::getHeaderDataLength() { + return Hdr.HeaderDataLength; +} + +ArrayRef<std::pair<AppleAcceleratorTable::HeaderData::AtomType, + AppleAcceleratorTable::HeaderData::Form>> +AppleAcceleratorTable::getAtomsDesc() { + return HdrData.Atoms; +} + +bool AppleAcceleratorTable::validateForms() { + for (auto Atom : getAtomsDesc()) { + DWARFFormValue FormValue(Atom.second); + switch (Atom.first) { + case dwarf::DW_ATOM_die_offset: + case dwarf::DW_ATOM_die_tag: + case dwarf::DW_ATOM_type_flags: + if ((!FormValue.isFormClass(DWARFFormValue::FC_Constant) && + !FormValue.isFormClass(DWARFFormValue::FC_Flag)) || + FormValue.getForm() == dwarf::DW_FORM_sdata) + return false; + break; + default: + break; + } + } + return true; +} + +std::pair<uint64_t, dwarf::Tag> +AppleAcceleratorTable::readAtoms(uint64_t *HashDataOffset) { + uint64_t DieOffset = dwarf::DW_INVALID_OFFSET; + dwarf::Tag DieTag = dwarf::DW_TAG_null; + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + + for (auto Atom : getAtomsDesc()) { + DWARFFormValue FormValue(Atom.second); + FormValue.extractValue(AccelSection, HashDataOffset, FormParams); + switch (Atom.first) { + case dwarf::DW_ATOM_die_offset: + DieOffset = *FormValue.getAsUnsignedConstant(); + break; + case dwarf::DW_ATOM_die_tag: + DieTag = (dwarf::Tag)*FormValue.getAsUnsignedConstant(); + break; + default: + break; + } + } + return {DieOffset, DieTag}; +} + +void AppleAcceleratorTable::Header::dump(ScopedPrinter &W) const { + DictScope HeaderScope(W, "Header"); + W.printHex("Magic", Magic); + W.printHex("Version", Version); + W.printHex("Hash function", HashFunction); + W.printNumber("Bucket count", BucketCount); + W.printNumber("Hashes count", HashCount); + W.printNumber("HeaderData length", HeaderDataLength); +} + +Optional<uint64_t> AppleAcceleratorTable::HeaderData::extractOffset( + Optional<DWARFFormValue> Value) const { + if (!Value) + return None; + + switch (Value->getForm()) { + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + case dwarf::DW_FORM_ref_udata: + return Value->getRawUValue() + DIEOffsetBase; + default: + return Value->getAsSectionOffset(); + } +} + +bool AppleAcceleratorTable::dumpName(ScopedPrinter &W, + SmallVectorImpl<DWARFFormValue> &AtomForms, + uint64_t *DataOffset) const { + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + uint64_t NameOffset = *DataOffset; + if (!AccelSection.isValidOffsetForDataOfSize(*DataOffset, 4)) { + W.printString("Incorrectly terminated list."); + return false; + } + uint64_t StringOffset = AccelSection.getRelocatedValue(4, DataOffset); + if (!StringOffset) + return false; // End of list + + DictScope NameScope(W, ("Name@0x" + Twine::utohexstr(NameOffset)).str()); + W.startLine() << format("String: 0x%08" PRIx64, StringOffset); + W.getOStream() << " \"" << StringSection.getCStr(&StringOffset) << "\"\n"; + + unsigned NumData = AccelSection.getU32(DataOffset); + for (unsigned Data = 0; Data < NumData; ++Data) { + ListScope DataScope(W, ("Data " + Twine(Data)).str()); + unsigned i = 0; + for (auto &Atom : AtomForms) { + W.startLine() << format("Atom[%d]: ", i); + if (Atom.extractValue(AccelSection, DataOffset, FormParams)) { + Atom.dump(W.getOStream()); + if (Optional<uint64_t> Val = Atom.getAsUnsignedConstant()) { + StringRef Str = dwarf::AtomValueString(HdrData.Atoms[i].first, *Val); + if (!Str.empty()) + W.getOStream() << " (" << Str << ")"; + } + } else + W.getOStream() << "Error extracting the value"; + W.getOStream() << "\n"; + i++; + } + } + return true; // more entries follow +} + +LLVM_DUMP_METHOD void AppleAcceleratorTable::dump(raw_ostream &OS) const { + if (!IsValid) + return; + + ScopedPrinter W(OS); + + Hdr.dump(W); + + W.printNumber("DIE offset base", HdrData.DIEOffsetBase); + W.printNumber("Number of atoms", uint64_t(HdrData.Atoms.size())); + SmallVector<DWARFFormValue, 3> AtomForms; + { + ListScope AtomsScope(W, "Atoms"); + unsigned i = 0; + for (const auto &Atom : HdrData.Atoms) { + DictScope AtomScope(W, ("Atom " + Twine(i++)).str()); + W.startLine() << "Type: " << formatAtom(Atom.first) << '\n'; + W.startLine() << "Form: " << formatv("{0}", Atom.second) << '\n'; + AtomForms.push_back(DWARFFormValue(Atom.second)); + } + } + + // Now go through the actual tables and dump them. + uint64_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength; + uint64_t HashesBase = Offset + Hdr.BucketCount * 4; + uint64_t OffsetsBase = HashesBase + Hdr.HashCount * 4; + + for (unsigned Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) { + unsigned Index = AccelSection.getU32(&Offset); + + ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); + if (Index == UINT32_MAX) { + W.printString("EMPTY"); + continue; + } + + for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) { + uint64_t HashOffset = HashesBase + HashIdx*4; + uint64_t OffsetsOffset = OffsetsBase + HashIdx*4; + uint32_t Hash = AccelSection.getU32(&HashOffset); + + if (Hash % Hdr.BucketCount != Bucket) + break; + + uint64_t DataOffset = AccelSection.getU32(&OffsetsOffset); + ListScope HashScope(W, ("Hash 0x" + Twine::utohexstr(Hash)).str()); + if (!AccelSection.isValidOffset(DataOffset)) { + W.printString("Invalid section offset"); + continue; + } + while (dumpName(W, AtomForms, &DataOffset)) + /*empty*/; + } + } +} + +AppleAcceleratorTable::Entry::Entry( + const AppleAcceleratorTable::HeaderData &HdrData) + : HdrData(&HdrData) { + Values.reserve(HdrData.Atoms.size()); + for (const auto &Atom : HdrData.Atoms) + Values.push_back(DWARFFormValue(Atom.second)); +} + +void AppleAcceleratorTable::Entry::extract( + const AppleAcceleratorTable &AccelTable, uint64_t *Offset) { + + dwarf::FormParams FormParams = {AccelTable.Hdr.Version, 0, + dwarf::DwarfFormat::DWARF32}; + for (auto &Atom : Values) + Atom.extractValue(AccelTable.AccelSection, Offset, FormParams); +} + +Optional<DWARFFormValue> +AppleAcceleratorTable::Entry::lookup(HeaderData::AtomType Atom) const { + assert(HdrData && "Dereferencing end iterator?"); + assert(HdrData->Atoms.size() == Values.size()); + for (const auto &Tuple : zip_first(HdrData->Atoms, Values)) { + if (std::get<0>(Tuple).first == Atom) + return std::get<1>(Tuple); + } + return None; +} + +Optional<uint64_t> AppleAcceleratorTable::Entry::getDIESectionOffset() const { + return HdrData->extractOffset(lookup(dwarf::DW_ATOM_die_offset)); +} + +Optional<uint64_t> AppleAcceleratorTable::Entry::getCUOffset() const { + return HdrData->extractOffset(lookup(dwarf::DW_ATOM_cu_offset)); +} + +Optional<dwarf::Tag> AppleAcceleratorTable::Entry::getTag() const { + Optional<DWARFFormValue> Tag = lookup(dwarf::DW_ATOM_die_tag); + if (!Tag) + return None; + if (Optional<uint64_t> Value = Tag->getAsUnsignedConstant()) + return dwarf::Tag(*Value); + return None; +} + +AppleAcceleratorTable::ValueIterator::ValueIterator( + const AppleAcceleratorTable &AccelTable, uint64_t Offset) + : AccelTable(&AccelTable), Current(AccelTable.HdrData), DataOffset(Offset) { + if (!AccelTable.AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) + return; + + // Read the first entry. + NumData = AccelTable.AccelSection.getU32(&DataOffset); + Next(); +} + +void AppleAcceleratorTable::ValueIterator::Next() { + assert(NumData > 0 && "attempted to increment iterator past the end"); + auto &AccelSection = AccelTable->AccelSection; + if (Data >= NumData || + !AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) { + NumData = 0; + DataOffset = 0; + return; + } + Current.extract(*AccelTable, &DataOffset); + ++Data; +} + +iterator_range<AppleAcceleratorTable::ValueIterator> +AppleAcceleratorTable::equal_range(StringRef Key) const { + if (!IsValid) + return make_range(ValueIterator(), ValueIterator()); + + // Find the bucket. + unsigned HashValue = djbHash(Key); + unsigned Bucket = HashValue % Hdr.BucketCount; + uint64_t BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength; + uint64_t HashesBase = BucketBase + Hdr.BucketCount * 4; + uint64_t OffsetsBase = HashesBase + Hdr.HashCount * 4; + + uint64_t BucketOffset = BucketBase + Bucket * 4; + unsigned Index = AccelSection.getU32(&BucketOffset); + + // Search through all hashes in the bucket. + for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) { + uint64_t HashOffset = HashesBase + HashIdx * 4; + uint64_t OffsetsOffset = OffsetsBase + HashIdx * 4; + uint32_t Hash = AccelSection.getU32(&HashOffset); + + if (Hash % Hdr.BucketCount != Bucket) + // We are already in the next bucket. + break; + + uint64_t DataOffset = AccelSection.getU32(&OffsetsOffset); + uint64_t StringOffset = AccelSection.getRelocatedValue(4, &DataOffset); + if (!StringOffset) + break; + + // Finally, compare the key. + if (Key == StringSection.getCStr(&StringOffset)) + return make_range({*this, DataOffset}, ValueIterator()); + } + return make_range(ValueIterator(), ValueIterator()); +} + +void DWARFDebugNames::Header::dump(ScopedPrinter &W) const { + DictScope HeaderScope(W, "Header"); + W.printHex("Length", UnitLength); + W.printNumber("Version", Version); + W.printHex("Padding", Padding); + W.printNumber("CU count", CompUnitCount); + W.printNumber("Local TU count", LocalTypeUnitCount); + W.printNumber("Foreign TU count", ForeignTypeUnitCount); + W.printNumber("Bucket count", BucketCount); + W.printNumber("Name count", NameCount); + W.printHex("Abbreviations table size", AbbrevTableSize); + W.startLine() << "Augmentation: '" << AugmentationString << "'\n"; +} + +Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS, + uint64_t *Offset) { + // Check that we can read the fixed-size part. + if (!AS.isValidOffset(*Offset + sizeof(HeaderPOD) - 1)) + return createStringError(errc::illegal_byte_sequence, + "Section too small: cannot read header."); + + UnitLength = AS.getU32(Offset); + Version = AS.getU16(Offset); + Padding = AS.getU16(Offset); + CompUnitCount = AS.getU32(Offset); + LocalTypeUnitCount = AS.getU32(Offset); + ForeignTypeUnitCount = AS.getU32(Offset); + BucketCount = AS.getU32(Offset); + NameCount = AS.getU32(Offset); + AbbrevTableSize = AS.getU32(Offset); + AugmentationStringSize = alignTo(AS.getU32(Offset), 4); + + if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize)) + return createStringError( + errc::illegal_byte_sequence, + "Section too small: cannot read header augmentation."); + AugmentationString.resize(AugmentationStringSize); + AS.getU8(Offset, reinterpret_cast<uint8_t *>(AugmentationString.data()), + AugmentationStringSize); + return Error::success(); +} + +void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const { + DictScope AbbrevScope(W, ("Abbreviation 0x" + Twine::utohexstr(Code)).str()); + W.startLine() << formatv("Tag: {0}\n", Tag); + + for (const auto &Attr : Attributes) + W.startLine() << formatv("{0}: {1}\n", Attr.Index, Attr.Form); +} + +static constexpr DWARFDebugNames::AttributeEncoding sentinelAttrEnc() { + return {dwarf::Index(0), dwarf::Form(0)}; +} + +static bool isSentinel(const DWARFDebugNames::AttributeEncoding &AE) { + return AE == sentinelAttrEnc(); +} + +static DWARFDebugNames::Abbrev sentinelAbbrev() { + return DWARFDebugNames::Abbrev(0, dwarf::Tag(0), {}); +} + +static bool isSentinel(const DWARFDebugNames::Abbrev &Abbr) { + return Abbr.Code == 0; +} + +DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getEmptyKey() { + return sentinelAbbrev(); +} + +DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getTombstoneKey() { + return DWARFDebugNames::Abbrev(~0, dwarf::Tag(0), {}); +} + +Expected<DWARFDebugNames::AttributeEncoding> +DWARFDebugNames::NameIndex::extractAttributeEncoding(uint64_t *Offset) { + if (*Offset >= EntriesBase) { + return createStringError(errc::illegal_byte_sequence, + "Incorrectly terminated abbreviation table."); + } + + uint32_t Index = Section.AccelSection.getULEB128(Offset); + uint32_t Form = Section.AccelSection.getULEB128(Offset); + return AttributeEncoding(dwarf::Index(Index), dwarf::Form(Form)); +} + +Expected<std::vector<DWARFDebugNames::AttributeEncoding>> +DWARFDebugNames::NameIndex::extractAttributeEncodings(uint64_t *Offset) { + std::vector<AttributeEncoding> Result; + for (;;) { + auto AttrEncOr = extractAttributeEncoding(Offset); + if (!AttrEncOr) + return AttrEncOr.takeError(); + if (isSentinel(*AttrEncOr)) + return std::move(Result); + + Result.emplace_back(*AttrEncOr); + } +} + +Expected<DWARFDebugNames::Abbrev> +DWARFDebugNames::NameIndex::extractAbbrev(uint64_t *Offset) { + if (*Offset >= EntriesBase) { + return createStringError(errc::illegal_byte_sequence, + "Incorrectly terminated abbreviation table."); + } + + uint32_t Code = Section.AccelSection.getULEB128(Offset); + if (Code == 0) + return sentinelAbbrev(); + + uint32_t Tag = Section.AccelSection.getULEB128(Offset); + auto AttrEncOr = extractAttributeEncodings(Offset); + if (!AttrEncOr) + return AttrEncOr.takeError(); + return Abbrev(Code, dwarf::Tag(Tag), std::move(*AttrEncOr)); +} + +Error DWARFDebugNames::NameIndex::extract() { + const DWARFDataExtractor &AS = Section.AccelSection; + uint64_t Offset = Base; + if (Error E = Hdr.extract(AS, &Offset)) + return E; + + CUsBase = Offset; + Offset += Hdr.CompUnitCount * 4; + Offset += Hdr.LocalTypeUnitCount * 4; + Offset += Hdr.ForeignTypeUnitCount * 8; + BucketsBase = Offset; + Offset += Hdr.BucketCount * 4; + HashesBase = Offset; + if (Hdr.BucketCount > 0) + Offset += Hdr.NameCount * 4; + StringOffsetsBase = Offset; + Offset += Hdr.NameCount * 4; + EntryOffsetsBase = Offset; + Offset += Hdr.NameCount * 4; + + if (!AS.isValidOffsetForDataOfSize(Offset, Hdr.AbbrevTableSize)) + return createStringError(errc::illegal_byte_sequence, + "Section too small: cannot read abbreviations."); + + EntriesBase = Offset + Hdr.AbbrevTableSize; + + for (;;) { + auto AbbrevOr = extractAbbrev(&Offset); + if (!AbbrevOr) + return AbbrevOr.takeError(); + if (isSentinel(*AbbrevOr)) + return Error::success(); + + if (!Abbrevs.insert(std::move(*AbbrevOr)).second) + return createStringError(errc::invalid_argument, + "Duplicate abbreviation code."); + } +} + +DWARFDebugNames::Entry::Entry(const NameIndex &NameIdx, const Abbrev &Abbr) + : NameIdx(&NameIdx), Abbr(&Abbr) { + // This merely creates form values. It is up to the caller + // (NameIndex::getEntry) to populate them. + Values.reserve(Abbr.Attributes.size()); + for (const auto &Attr : Abbr.Attributes) + Values.emplace_back(Attr.Form); +} + +Optional<DWARFFormValue> +DWARFDebugNames::Entry::lookup(dwarf::Index Index) const { + assert(Abbr->Attributes.size() == Values.size()); + for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { + if (std::get<0>(Tuple).Index == Index) + return std::get<1>(Tuple); + } + return None; +} + +Optional<uint64_t> DWARFDebugNames::Entry::getDIEUnitOffset() const { + if (Optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_die_offset)) + return Off->getAsReferenceUVal(); + return None; +} + +Optional<uint64_t> DWARFDebugNames::Entry::getCUIndex() const { + if (Optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_compile_unit)) + return Off->getAsUnsignedConstant(); + // In a per-CU index, the entries without a DW_IDX_compile_unit attribute + // implicitly refer to the single CU. + if (NameIdx->getCUCount() == 1) + return 0; + return None; +} + +Optional<uint64_t> DWARFDebugNames::Entry::getCUOffset() const { + Optional<uint64_t> Index = getCUIndex(); + if (!Index || *Index >= NameIdx->getCUCount()) + return None; + return NameIdx->getCUOffset(*Index); +} + +void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const { + W.printHex("Abbrev", Abbr->Code); + W.startLine() << formatv("Tag: {0}\n", Abbr->Tag); + assert(Abbr->Attributes.size() == Values.size()); + for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { + W.startLine() << formatv("{0}: ", std::get<0>(Tuple).Index); + std::get<1>(Tuple).dump(W.getOStream()); + W.getOStream() << '\n'; + } +} + +char DWARFDebugNames::SentinelError::ID; +std::error_code DWARFDebugNames::SentinelError::convertToErrorCode() const { + return inconvertibleErrorCode(); +} + +uint64_t DWARFDebugNames::NameIndex::getCUOffset(uint32_t CU) const { + assert(CU < Hdr.CompUnitCount); + uint64_t Offset = CUsBase + 4 * CU; + return Section.AccelSection.getRelocatedValue(4, &Offset); +} + +uint64_t DWARFDebugNames::NameIndex::getLocalTUOffset(uint32_t TU) const { + assert(TU < Hdr.LocalTypeUnitCount); + uint64_t Offset = CUsBase + 4 * (Hdr.CompUnitCount + TU); + return Section.AccelSection.getRelocatedValue(4, &Offset); +} + +uint64_t DWARFDebugNames::NameIndex::getForeignTUSignature(uint32_t TU) const { + assert(TU < Hdr.ForeignTypeUnitCount); + uint64_t Offset = + CUsBase + 4 * (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) + 8 * TU; + return Section.AccelSection.getU64(&Offset); +} + +Expected<DWARFDebugNames::Entry> +DWARFDebugNames::NameIndex::getEntry(uint64_t *Offset) const { + const DWARFDataExtractor &AS = Section.AccelSection; + if (!AS.isValidOffset(*Offset)) + return createStringError(errc::illegal_byte_sequence, + "Incorrectly terminated entry list."); + + uint32_t AbbrevCode = AS.getULEB128(Offset); + if (AbbrevCode == 0) + return make_error<SentinelError>(); + + const auto AbbrevIt = Abbrevs.find_as(AbbrevCode); + if (AbbrevIt == Abbrevs.end()) + return createStringError(errc::invalid_argument, "Invalid abbreviation."); + + Entry E(*this, *AbbrevIt); + + dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + for (auto &Value : E.Values) { + if (!Value.extractValue(AS, Offset, FormParams)) + return createStringError(errc::io_error, + "Error extracting index attribute values."); + } + return std::move(E); +} + +DWARFDebugNames::NameTableEntry +DWARFDebugNames::NameIndex::getNameTableEntry(uint32_t Index) const { + assert(0 < Index && Index <= Hdr.NameCount); + uint64_t StringOffsetOffset = StringOffsetsBase + 4 * (Index - 1); + uint64_t EntryOffsetOffset = EntryOffsetsBase + 4 * (Index - 1); + const DWARFDataExtractor &AS = Section.AccelSection; + + uint64_t StringOffset = AS.getRelocatedValue(4, &StringOffsetOffset); + uint64_t EntryOffset = AS.getU32(&EntryOffsetOffset); + EntryOffset += EntriesBase; + return {Section.StringSection, Index, StringOffset, EntryOffset}; +} + +uint32_t +DWARFDebugNames::NameIndex::getBucketArrayEntry(uint32_t Bucket) const { + assert(Bucket < Hdr.BucketCount); + uint64_t BucketOffset = BucketsBase + 4 * Bucket; + return Section.AccelSection.getU32(&BucketOffset); +} + +uint32_t DWARFDebugNames::NameIndex::getHashArrayEntry(uint32_t Index) const { + assert(0 < Index && Index <= Hdr.NameCount); + uint64_t HashOffset = HashesBase + 4 * (Index - 1); + return Section.AccelSection.getU32(&HashOffset); +} + +// Returns true if we should continue scanning for entries, false if this is the +// last (sentinel) entry). In case of a parsing error we also return false, as +// it's not possible to recover this entry list (but the other lists may still +// parse OK). +bool DWARFDebugNames::NameIndex::dumpEntry(ScopedPrinter &W, + uint64_t *Offset) const { + uint64_t EntryId = *Offset; + auto EntryOr = getEntry(Offset); + if (!EntryOr) { + handleAllErrors(EntryOr.takeError(), [](const SentinelError &) {}, + [&W](const ErrorInfoBase &EI) { EI.log(W.startLine()); }); + return false; + } + + DictScope EntryScope(W, ("Entry @ 0x" + Twine::utohexstr(EntryId)).str()); + EntryOr->dump(W); + return true; +} + +void DWARFDebugNames::NameIndex::dumpName(ScopedPrinter &W, + const NameTableEntry &NTE, + Optional<uint32_t> Hash) const { + DictScope NameScope(W, ("Name " + Twine(NTE.getIndex())).str()); + if (Hash) + W.printHex("Hash", *Hash); + + W.startLine() << format("String: 0x%08" PRIx64, NTE.getStringOffset()); + W.getOStream() << " \"" << NTE.getString() << "\"\n"; + + uint64_t EntryOffset = NTE.getEntryOffset(); + while (dumpEntry(W, &EntryOffset)) + /*empty*/; +} + +void DWARFDebugNames::NameIndex::dumpCUs(ScopedPrinter &W) const { + ListScope CUScope(W, "Compilation Unit offsets"); + for (uint32_t CU = 0; CU < Hdr.CompUnitCount; ++CU) + W.startLine() << format("CU[%u]: 0x%08" PRIx64 "\n", CU, getCUOffset(CU)); +} + +void DWARFDebugNames::NameIndex::dumpLocalTUs(ScopedPrinter &W) const { + if (Hdr.LocalTypeUnitCount == 0) + return; + + ListScope TUScope(W, "Local Type Unit offsets"); + for (uint32_t TU = 0; TU < Hdr.LocalTypeUnitCount; ++TU) + W.startLine() << format("LocalTU[%u]: 0x%08" PRIx64 "\n", TU, + getLocalTUOffset(TU)); +} + +void DWARFDebugNames::NameIndex::dumpForeignTUs(ScopedPrinter &W) const { + if (Hdr.ForeignTypeUnitCount == 0) + return; + + ListScope TUScope(W, "Foreign Type Unit signatures"); + for (uint32_t TU = 0; TU < Hdr.ForeignTypeUnitCount; ++TU) { + W.startLine() << format("ForeignTU[%u]: 0x%016" PRIx64 "\n", TU, + getForeignTUSignature(TU)); + } +} + +void DWARFDebugNames::NameIndex::dumpAbbreviations(ScopedPrinter &W) const { + ListScope AbbrevsScope(W, "Abbreviations"); + for (const auto &Abbr : Abbrevs) + Abbr.dump(W); +} + +void DWARFDebugNames::NameIndex::dumpBucket(ScopedPrinter &W, + uint32_t Bucket) const { + ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); + uint32_t Index = getBucketArrayEntry(Bucket); + if (Index == 0) { + W.printString("EMPTY"); + return; + } + if (Index > Hdr.NameCount) { + W.printString("Name index is invalid"); + return; + } + + for (; Index <= Hdr.NameCount; ++Index) { + uint32_t Hash = getHashArrayEntry(Index); + if (Hash % Hdr.BucketCount != Bucket) + break; + + dumpName(W, getNameTableEntry(Index), Hash); + } +} + +LLVM_DUMP_METHOD void DWARFDebugNames::NameIndex::dump(ScopedPrinter &W) const { + DictScope UnitScope(W, ("Name Index @ 0x" + Twine::utohexstr(Base)).str()); + Hdr.dump(W); + dumpCUs(W); + dumpLocalTUs(W); + dumpForeignTUs(W); + dumpAbbreviations(W); + + if (Hdr.BucketCount > 0) { + for (uint32_t Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) + dumpBucket(W, Bucket); + return; + } + + W.startLine() << "Hash table not present\n"; + for (NameTableEntry NTE : *this) + dumpName(W, NTE, None); +} + +Error DWARFDebugNames::extract() { + uint64_t Offset = 0; + while (AccelSection.isValidOffset(Offset)) { + NameIndex Next(*this, Offset); + if (Error E = Next.extract()) + return E; + Offset = Next.getNextUnitOffset(); + NameIndices.push_back(std::move(Next)); + } + return Error::success(); +} + +iterator_range<DWARFDebugNames::ValueIterator> +DWARFDebugNames::NameIndex::equal_range(StringRef Key) const { + return make_range(ValueIterator(*this, Key), ValueIterator()); +} + +LLVM_DUMP_METHOD void DWARFDebugNames::dump(raw_ostream &OS) const { + ScopedPrinter W(OS); + for (const NameIndex &NI : NameIndices) + NI.dump(W); +} + +Optional<uint64_t> +DWARFDebugNames::ValueIterator::findEntryOffsetInCurrentIndex() { + const Header &Hdr = CurrentIndex->Hdr; + if (Hdr.BucketCount == 0) { + // No Hash Table, We need to search through all names in the Name Index. + for (NameTableEntry NTE : *CurrentIndex) { + if (NTE.getString() == Key) + return NTE.getEntryOffset(); + } + return None; + } + + // The Name Index has a Hash Table, so use that to speed up the search. + // Compute the Key Hash, if it has not been done already. + if (!Hash) + Hash = caseFoldingDjbHash(Key); + uint32_t Bucket = *Hash % Hdr.BucketCount; + uint32_t Index = CurrentIndex->getBucketArrayEntry(Bucket); + if (Index == 0) + return None; // Empty bucket + + for (; Index <= Hdr.NameCount; ++Index) { + uint32_t Hash = CurrentIndex->getHashArrayEntry(Index); + if (Hash % Hdr.BucketCount != Bucket) + return None; // End of bucket + + NameTableEntry NTE = CurrentIndex->getNameTableEntry(Index); + if (NTE.getString() == Key) + return NTE.getEntryOffset(); + } + return None; +} + +bool DWARFDebugNames::ValueIterator::getEntryAtCurrentOffset() { + auto EntryOr = CurrentIndex->getEntry(&DataOffset); + if (!EntryOr) { + consumeError(EntryOr.takeError()); + return false; + } + CurrentEntry = std::move(*EntryOr); + return true; +} + +bool DWARFDebugNames::ValueIterator::findInCurrentIndex() { + Optional<uint64_t> Offset = findEntryOffsetInCurrentIndex(); + if (!Offset) + return false; + DataOffset = *Offset; + return getEntryAtCurrentOffset(); +} + +void DWARFDebugNames::ValueIterator::searchFromStartOfCurrentIndex() { + for (const NameIndex *End = CurrentIndex->Section.NameIndices.end(); + CurrentIndex != End; ++CurrentIndex) { + if (findInCurrentIndex()) + return; + } + setEnd(); +} + +void DWARFDebugNames::ValueIterator::next() { + assert(CurrentIndex && "Incrementing an end() iterator?"); + + // First try the next entry in the current Index. + if (getEntryAtCurrentOffset()) + return; + + // If we're a local iterator or we have reached the last Index, we're done. + if (IsLocal || CurrentIndex == &CurrentIndex->Section.NameIndices.back()) { + setEnd(); + return; + } + + // Otherwise, try the next index. + ++CurrentIndex; + searchFromStartOfCurrentIndex(); +} + +DWARFDebugNames::ValueIterator::ValueIterator(const DWARFDebugNames &AccelTable, + StringRef Key) + : CurrentIndex(AccelTable.NameIndices.begin()), IsLocal(false), Key(Key) { + searchFromStartOfCurrentIndex(); +} + +DWARFDebugNames::ValueIterator::ValueIterator( + const DWARFDebugNames::NameIndex &NI, StringRef Key) + : CurrentIndex(&NI), IsLocal(true), Key(Key) { + if (!findInCurrentIndex()) + setEnd(); +} + +iterator_range<DWARFDebugNames::ValueIterator> +DWARFDebugNames::equal_range(StringRef Key) const { + if (NameIndices.empty()) + return make_range(ValueIterator(), ValueIterator()); + return make_range(ValueIterator(*this, Key), ValueIterator()); +} + +const DWARFDebugNames::NameIndex * +DWARFDebugNames::getCUNameIndex(uint64_t CUOffset) { + if (CUToNameIndex.size() == 0 && NameIndices.size() > 0) { + for (const auto &NI : *this) { + for (uint32_t CU = 0; CU < NI.getCUCount(); ++CU) + CUToNameIndex.try_emplace(NI.getCUOffset(CU), &NI); + } + } + return CUToNameIndex.lookup(CUOffset); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp new file mode 100644 index 000000000000..ef6da08d34aa --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp @@ -0,0 +1,28 @@ +//===- DWARFDebugAranges.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void DWARFAddressRange::dump(raw_ostream &OS, uint32_t AddressSize, + DIDumpOptions DumpOpts) const { + + OS << (DumpOpts.DisplayRawContents ? " " : "["); + OS << format("0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, LowPC) + << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, HighPC); + OS << (DumpOpts.DisplayRawContents ? "" : ")"); +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, const DWARFAddressRange &R) { + R.dump(OS, /* AddressSize */ 8); + return OS; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp new file mode 100644 index 000000000000..f59e49268288 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,38 @@ +//===-- DWARFCompileUnit.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void DWARFCompileUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + OS << format("0x%08" PRIx64, getOffset()) << ": Compile Unit:" + << " length = " << format("0x%08" PRIx64, getLength()) + << " version = " << format("0x%04x", getVersion()); + if (getVersion() >= 5) + OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << " abbr_offset = " + << format("0x%04" PRIx64, getAbbreviations()->getOffset()) + << " addr_size = " << format("0x%02x", getAddressByteSize()); + if (getVersion() >= 5 && getUnitType() != dwarf::DW_UT_compile) + OS << " DWO_id = " << format("0x%016" PRIx64, *getDWOId()); + OS << " (next unit at " << format("0x%08" PRIx64, getNextUnitOffset()) + << ")\n"; + + if (DWARFDie CUDie = getUnitDIE(false)) + CUDie.dump(OS, 0, DumpOpts); + else + OS << "<compile unit can't be parsed!>\n\n"; +} + +// VTable anchor. +DWARFCompileUnit::~DWARFCompileUnit() = default; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp new file mode 100644 index 000000000000..c06d85d50609 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -0,0 +1,1866 @@ +//===- DWARFContext.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Object/Decompressor.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/RelocationResolver.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cstdint> +#include <deque> +#include <map> +#include <string> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +#define DEBUG_TYPE "dwarf" + +using DWARFLineTable = DWARFDebugLine::LineTable; +using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; +using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; + +DWARFContext::DWARFContext(std::unique_ptr<const DWARFObject> DObj, + std::string DWPName) + : DIContext(CK_DWARF), DWPName(std::move(DWPName)), DObj(std::move(DObj)) {} + +DWARFContext::~DWARFContext() = default; + +/// Dump the UUID load command. +static void dumpUUID(raw_ostream &OS, const ObjectFile &Obj) { + auto *MachO = dyn_cast<MachOObjectFile>(&Obj); + if (!MachO) + return; + for (auto LC : MachO->load_commands()) { + raw_ostream::uuid_t UUID; + if (LC.C.cmd == MachO::LC_UUID) { + if (LC.C.cmdsize < sizeof(UUID) + sizeof(LC.C)) { + OS << "error: UUID load command is too short.\n"; + return; + } + OS << "UUID: "; + memcpy(&UUID, LC.Ptr+sizeof(LC.C), sizeof(UUID)); + OS.write_uuid(UUID); + Triple T = MachO->getArchTriple(); + OS << " (" << T.getArchName() << ')'; + OS << ' ' << MachO->getFileName() << '\n'; + } + } +} + +using ContributionCollection = + std::vector<Optional<StrOffsetsContributionDescriptor>>; + +// Collect all the contributions to the string offsets table from all units, +// sort them by their starting offsets and remove duplicates. +static ContributionCollection +collectContributionData(DWARFContext::unit_iterator_range Units) { + ContributionCollection Contributions; + for (const auto &U : Units) + if (const auto &C = U->getStringOffsetsTableContribution()) + Contributions.push_back(C); + // Sort the contributions so that any invalid ones are placed at + // the start of the contributions vector. This way they are reported + // first. + llvm::sort(Contributions, + [](const Optional<StrOffsetsContributionDescriptor> &L, + const Optional<StrOffsetsContributionDescriptor> &R) { + if (L && R) + return L->Base < R->Base; + return R.hasValue(); + }); + + // Uniquify contributions, as it is possible that units (specifically + // type units in dwo or dwp files) share contributions. We don't want + // to report them more than once. + Contributions.erase( + std::unique(Contributions.begin(), Contributions.end(), + [](const Optional<StrOffsetsContributionDescriptor> &L, + const Optional<StrOffsetsContributionDescriptor> &R) { + if (L && R) + return L->Base == R->Base && L->Size == R->Size; + return false; + }), + Contributions.end()); + return Contributions; +} + +static void dumpDWARFv5StringOffsetsSection( + raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, + const DWARFSection &StringOffsetsSection, StringRef StringSection, + DWARFContext::unit_iterator_range Units, bool LittleEndian) { + auto Contributions = collectContributionData(Units); + DWARFDataExtractor StrOffsetExt(Obj, StringOffsetsSection, LittleEndian, 0); + DataExtractor StrData(StringSection, LittleEndian, 0); + uint64_t SectionSize = StringOffsetsSection.Data.size(); + uint64_t Offset = 0; + for (auto &Contribution : Contributions) { + // Report an ill-formed contribution. + if (!Contribution) { + OS << "error: invalid contribution to string offsets table in section ." + << SectionName << ".\n"; + return; + } + + dwarf::DwarfFormat Format = Contribution->getFormat(); + uint16_t Version = Contribution->getVersion(); + uint64_t ContributionHeader = Contribution->Base; + // In DWARF v5 there is a contribution header that immediately precedes + // the string offsets base (the location we have previously retrieved from + // the CU DIE's DW_AT_str_offsets attribute). The header is located either + // 8 or 16 bytes before the base, depending on the contribution's format. + if (Version >= 5) + ContributionHeader -= Format == DWARF32 ? 8 : 16; + + // Detect overlapping contributions. + if (Offset > ContributionHeader) { + WithColor::error() + << "overlapping contributions to string offsets table in section ." + << SectionName << ".\n"; + return; + } + // Report a gap in the table. + if (Offset < ContributionHeader) { + OS << format("0x%8.8" PRIx64 ": Gap, length = ", Offset); + OS << (ContributionHeader - Offset) << "\n"; + } + OS << format("0x%8.8" PRIx64 ": ", ContributionHeader); + // In DWARF v5 the contribution size in the descriptor does not equal + // the originally encoded length (it does not contain the length of the + // version field and the padding, a total of 4 bytes). Add them back in + // for reporting. + OS << "Contribution size = " << (Contribution->Size + (Version < 5 ? 0 : 4)) + << ", Format = " << (Format == DWARF32 ? "DWARF32" : "DWARF64") + << ", Version = " << Version << "\n"; + + Offset = Contribution->Base; + unsigned EntrySize = Contribution->getDwarfOffsetByteSize(); + while (Offset - Contribution->Base < Contribution->Size) { + OS << format("0x%8.8" PRIx64 ": ", Offset); + uint64_t StringOffset = + StrOffsetExt.getRelocatedValue(EntrySize, &Offset); + OS << format("%8.8" PRIx64 " ", StringOffset); + const char *S = StrData.getCStr(&StringOffset); + if (S) + OS << format("\"%s\"", S); + OS << "\n"; + } + } + // Report a gap at the end of the table. + if (Offset < SectionSize) { + OS << format("0x%8.8" PRIx64 ": Gap, length = ", Offset); + OS << (SectionSize - Offset) << "\n"; + } +} + +// Dump a DWARF string offsets section. This may be a DWARF v5 formatted +// string offsets section, where each compile or type unit contributes a +// number of entries (string offsets), with each contribution preceded by +// a header containing size and version number. Alternatively, it may be a +// monolithic series of string offsets, as generated by the pre-DWARF v5 +// implementation of split DWARF. +static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, + const DWARFObject &Obj, + const DWARFSection &StringOffsetsSection, + StringRef StringSection, + DWARFContext::unit_iterator_range Units, + bool LittleEndian, unsigned MaxVersion) { + // If we have at least one (compile or type) unit with DWARF v5 or greater, + // we assume that the section is formatted like a DWARF v5 string offsets + // section. + if (MaxVersion >= 5) + dumpDWARFv5StringOffsetsSection(OS, SectionName, Obj, StringOffsetsSection, + StringSection, Units, LittleEndian); + else { + DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0); + uint64_t offset = 0; + uint64_t size = StringOffsetsSection.Data.size(); + // Ensure that size is a multiple of the size of an entry. + if (size & ((uint64_t)(sizeof(uint32_t) - 1))) { + OS << "error: size of ." << SectionName << " is not a multiple of " + << sizeof(uint32_t) << ".\n"; + size &= -(uint64_t)sizeof(uint32_t); + } + DataExtractor StrData(StringSection, LittleEndian, 0); + while (offset < size) { + OS << format("0x%8.8" PRIx64 ": ", offset); + uint64_t StringOffset = strOffsetExt.getU32(&offset); + OS << format("%8.8" PRIx64 " ", StringOffset); + const char *S = StrData.getCStr(&StringOffset); + if (S) + OS << format("\"%s\"", S); + OS << "\n"; + } + } +} + +// Dump the .debug_addr section. +static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, + DIDumpOptions DumpOpts, uint16_t Version, + uint8_t AddrSize) { + uint64_t Offset = 0; + while (AddrData.isValidOffset(Offset)) { + DWARFDebugAddrTable AddrTable; + uint64_t TableOffset = Offset; + if (Error Err = AddrTable.extract(AddrData, &Offset, Version, AddrSize, + DWARFContext::dumpWarning)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + // Keep going after an error, if we can, assuming that the length field + // could be read. If it couldn't, stop reading the section. + if (!AddrTable.hasValidLength()) + break; + Offset = TableOffset + AddrTable.getLength(); + } else { + AddrTable.dump(OS, DumpOpts); + } + } +} + +// Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). +static void dumpRnglistsSection( + raw_ostream &OS, DWARFDataExtractor &rnglistData, + llvm::function_ref<Optional<object::SectionedAddress>(uint32_t)> + LookupPooledAddress, + DIDumpOptions DumpOpts) { + uint64_t Offset = 0; + while (rnglistData.isValidOffset(Offset)) { + llvm::DWARFDebugRnglistTable Rnglists; + uint64_t TableOffset = Offset; + if (Error Err = Rnglists.extract(rnglistData, &Offset)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + uint64_t Length = Rnglists.length(); + // Keep going after an error, if we can, assuming that the length field + // could be read. If it couldn't, stop reading the section. + if (Length == 0) + break; + Offset = TableOffset + Length; + } else { + Rnglists.dump(OS, LookupPooledAddress, DumpOpts); + } + } +} + +static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, + DWARFDataExtractor Data, + const MCRegisterInfo *MRI, + Optional<uint64_t> DumpOffset) { + uint64_t Offset = 0; + + while (Data.isValidOffset(Offset)) { + DWARFListTableHeader Header(".debug_loclists", "locations"); + if (Error E = Header.extract(Data, &Offset)) { + WithColor::error() << toString(std::move(E)) << '\n'; + return; + } + + Header.dump(OS, DumpOpts); + DataExtractor LocData(Data.getData(), + Data.isLittleEndian(), Header.getAddrSize()); + + DWARFDebugLoclists Loclists; + uint64_t EndOffset = Header.length() + Header.getHeaderOffset(); + Loclists.parse(LocData, Offset, EndOffset, Header.getVersion()); + Loclists.dump(OS, 0, MRI, DumpOpts, DumpOffset); + Offset = EndOffset; + } +} + +void DWARFContext::dump( + raw_ostream &OS, DIDumpOptions DumpOpts, + std::array<Optional<uint64_t>, DIDT_ID_Count> DumpOffsets) { + + uint64_t DumpType = DumpOpts.DumpType; + + StringRef Extension = sys::path::extension(DObj->getFileName()); + bool IsDWO = (Extension == ".dwo") || (Extension == ".dwp"); + + // Print UUID header. + const auto *ObjFile = DObj->getFile(); + if (DumpType & DIDT_UUID) + dumpUUID(OS, *ObjFile); + + // Print a header for each explicitly-requested section. + // Otherwise just print one for non-empty sections. + // Only print empty .dwo section headers when dumping a .dwo file. + bool Explicit = DumpType != DIDT_All && !IsDWO; + bool ExplicitDWO = Explicit && IsDWO; + auto shouldDump = [&](bool Explicit, const char *Name, unsigned ID, + StringRef Section) -> Optional<uint64_t> * { + unsigned Mask = 1U << ID; + bool Should = (DumpType & Mask) && (Explicit || !Section.empty()); + if (!Should) + return nullptr; + OS << "\n" << Name << " contents:\n"; + return &DumpOffsets[ID]; + }; + + // Dump individual sections. + if (shouldDump(Explicit, ".debug_abbrev", DIDT_ID_DebugAbbrev, + DObj->getAbbrevSection())) + getDebugAbbrev()->dump(OS); + if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_ID_DebugAbbrev, + DObj->getAbbrevDWOSection())) + getDebugAbbrevDWO()->dump(OS); + + auto dumpDebugInfo = [&](const char *Name, unit_iterator_range Units) { + OS << '\n' << Name << " contents:\n"; + if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugInfo]) + for (const auto &U : Units) + U->getDIEForOffset(DumpOffset.getValue()) + .dump(OS, 0, DumpOpts.noImplicitRecursion()); + else + for (const auto &U : Units) + U->dump(OS, DumpOpts); + }; + if ((DumpType & DIDT_DebugInfo)) { + if (Explicit || getNumCompileUnits()) + dumpDebugInfo(".debug_info", info_section_units()); + if (ExplicitDWO || getNumDWOCompileUnits()) + dumpDebugInfo(".debug_info.dwo", dwo_info_section_units()); + } + + auto dumpDebugType = [&](const char *Name, unit_iterator_range Units) { + OS << '\n' << Name << " contents:\n"; + for (const auto &U : Units) + if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugTypes]) + U->getDIEForOffset(*DumpOffset) + .dump(OS, 0, DumpOpts.noImplicitRecursion()); + else + U->dump(OS, DumpOpts); + }; + if ((DumpType & DIDT_DebugTypes)) { + if (Explicit || getNumTypeUnits()) + dumpDebugType(".debug_types", types_section_units()); + if (ExplicitDWO || getNumDWOTypeUnits()) + dumpDebugType(".debug_types.dwo", dwo_types_section_units()); + } + + if (const auto *Off = shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, + DObj->getLocSection().Data)) { + getDebugLoc()->dump(OS, getRegisterInfo(), DumpOpts, *Off); + } + if (const auto *Off = + shouldDump(Explicit, ".debug_loclists", DIDT_ID_DebugLoclists, + DObj->getLoclistsSection().Data)) { + DWARFDataExtractor Data(*DObj, DObj->getLoclistsSection(), isLittleEndian(), + 0); + dumpLoclistsSection(OS, DumpOpts, Data, getRegisterInfo(), *Off); + } + if (const auto *Off = + shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, + DObj->getLocDWOSection().Data)) { + getDebugLocDWO()->dump(OS, 0, getRegisterInfo(), DumpOpts, *Off); + } + + if (const auto *Off = shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, + DObj->getFrameSection().Data)) + getDebugFrame()->dump(OS, getRegisterInfo(), *Off); + + if (const auto *Off = shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, + DObj->getEHFrameSection().Data)) + getEHFrame()->dump(OS, getRegisterInfo(), *Off); + + if (DumpType & DIDT_DebugMacro) { + if (Explicit || !getDebugMacro()->empty()) { + OS << "\n.debug_macinfo contents:\n"; + getDebugMacro()->dump(OS); + } + } + + if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, + DObj->getArangesSection())) { + uint64_t offset = 0; + DataExtractor arangesData(DObj->getArangesSection(), isLittleEndian(), 0); + DWARFDebugArangeSet set; + while (set.extract(arangesData, &offset)) + set.dump(OS); + } + + auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser, + DIDumpOptions DumpOpts, + Optional<uint64_t> DumpOffset) { + while (!Parser.done()) { + if (DumpOffset && Parser.getOffset() != *DumpOffset) { + Parser.skip(dumpWarning); + continue; + } + OS << "debug_line[" << format("0x%8.8" PRIx64, Parser.getOffset()) + << "]\n"; + if (DumpOpts.Verbose) { + Parser.parseNext(dumpWarning, dumpWarning, &OS); + } else { + DWARFDebugLine::LineTable LineTable = + Parser.parseNext(dumpWarning, dumpWarning); + LineTable.dump(OS, DumpOpts); + } + } + }; + + if (const auto *Off = shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, + DObj->getLineSection().Data)) { + DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(), + 0); + DWARFDebugLine::SectionParser Parser(LineData, *this, compile_units(), + type_units()); + DumpLineSection(Parser, DumpOpts, *Off); + } + + if (const auto *Off = + shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, + DObj->getLineDWOSection().Data)) { + DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(), + isLittleEndian(), 0); + DWARFDebugLine::SectionParser Parser(LineData, *this, dwo_compile_units(), + dwo_type_units()); + DumpLineSection(Parser, DumpOpts, *Off); + } + + if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, + DObj->getCUIndexSection())) { + getCUIndex().dump(OS); + } + + if (shouldDump(Explicit, ".debug_tu_index", DIDT_ID_DebugTUIndex, + DObj->getTUIndexSection())) { + getTUIndex().dump(OS); + } + + if (shouldDump(Explicit, ".debug_str", DIDT_ID_DebugStr, + DObj->getStrSection())) { + DataExtractor strData(DObj->getStrSection(), isLittleEndian(), 0); + uint64_t offset = 0; + uint64_t strOffset = 0; + while (const char *s = strData.getCStr(&offset)) { + OS << format("0x%8.8" PRIx64 ": \"%s\"\n", strOffset, s); + strOffset = offset; + } + } + if (shouldDump(ExplicitDWO, ".debug_str.dwo", DIDT_ID_DebugStr, + DObj->getStrDWOSection())) { + DataExtractor strDWOData(DObj->getStrDWOSection(), isLittleEndian(), 0); + uint64_t offset = 0; + uint64_t strDWOOffset = 0; + while (const char *s = strDWOData.getCStr(&offset)) { + OS << format("0x%8.8" PRIx64 ": \"%s\"\n", strDWOOffset, s); + strDWOOffset = offset; + } + } + if (shouldDump(Explicit, ".debug_line_str", DIDT_ID_DebugLineStr, + DObj->getLineStrSection())) { + DataExtractor strData(DObj->getLineStrSection(), isLittleEndian(), 0); + uint64_t offset = 0; + uint64_t strOffset = 0; + while (const char *s = strData.getCStr(&offset)) { + OS << format("0x%8.8" PRIx64 ": \"", strOffset); + OS.write_escaped(s); + OS << "\"\n"; + strOffset = offset; + } + } + + if (shouldDump(Explicit, ".debug_addr", DIDT_ID_DebugAddr, + DObj->getAddrSection().Data)) { + DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(), + isLittleEndian(), 0); + dumpAddrSection(OS, AddrData, DumpOpts, getMaxVersion(), getCUAddrSize()); + } + + if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, + DObj->getRangesSection().Data)) { + uint8_t savedAddressByteSize = getCUAddrSize(); + DWARFDataExtractor rangesData(*DObj, DObj->getRangesSection(), + isLittleEndian(), savedAddressByteSize); + uint64_t offset = 0; + DWARFDebugRangeList rangeList; + while (rangesData.isValidOffset(offset)) { + if (Error E = rangeList.extract(rangesData, &offset)) { + WithColor::error() << toString(std::move(E)) << '\n'; + break; + } + rangeList.dump(OS); + } + } + + auto LookupPooledAddress = [&](uint32_t Index) -> Optional<SectionedAddress> { + const auto &CUs = compile_units(); + auto I = CUs.begin(); + if (I == CUs.end()) + return None; + return (*I)->getAddrOffsetSectionItem(Index); + }; + + if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, + DObj->getRnglistsSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); + } + + if (shouldDump(ExplicitDWO, ".debug_rnglists.dwo", DIDT_ID_DebugRnglists, + DObj->getRnglistsDWOSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsDWOSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); + } + + if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, + DObj->getPubnamesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getPubnamesSection(), isLittleEndian(), false) + .dump(OS); + + if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, + DObj->getPubtypesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getPubtypesSection(), isLittleEndian(), false) + .dump(OS); + + if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, + DObj->getGnuPubnamesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getGnuPubnamesSection(), isLittleEndian(), + true /* GnuStyle */) + .dump(OS); + + if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, + DObj->getGnuPubtypesSection().Data)) + DWARFDebugPubTable(*DObj, DObj->getGnuPubtypesSection(), isLittleEndian(), + true /* GnuStyle */) + .dump(OS); + + if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, + DObj->getStrOffsetsSection().Data)) + dumpStringOffsetsSection(OS, "debug_str_offsets", *DObj, + DObj->getStrOffsetsSection(), + DObj->getStrSection(), normal_units(), + isLittleEndian(), getMaxVersion()); + if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_ID_DebugStrOffsets, + DObj->getStrOffsetsDWOSection().Data)) + dumpStringOffsetsSection(OS, "debug_str_offsets.dwo", *DObj, + DObj->getStrOffsetsDWOSection(), + DObj->getStrDWOSection(), dwo_units(), + isLittleEndian(), getMaxDWOVersion()); + + if (shouldDump(Explicit, ".gdb_index", DIDT_ID_GdbIndex, + DObj->getGdbIndexSection())) { + getGdbIndex().dump(OS); + } + + if (shouldDump(Explicit, ".apple_names", DIDT_ID_AppleNames, + DObj->getAppleNamesSection().Data)) + getAppleNames().dump(OS); + + if (shouldDump(Explicit, ".apple_types", DIDT_ID_AppleTypes, + DObj->getAppleTypesSection().Data)) + getAppleTypes().dump(OS); + + if (shouldDump(Explicit, ".apple_namespaces", DIDT_ID_AppleNamespaces, + DObj->getAppleNamespacesSection().Data)) + getAppleNamespaces().dump(OS); + + if (shouldDump(Explicit, ".apple_objc", DIDT_ID_AppleObjC, + DObj->getAppleObjCSection().Data)) + getAppleObjC().dump(OS); + if (shouldDump(Explicit, ".debug_names", DIDT_ID_DebugNames, + DObj->getNamesSection().Data)) + getDebugNames().dump(OS); +} + +DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { + parseDWOUnits(LazyParse); + + if (const auto &CUI = getCUIndex()) { + if (const auto *R = CUI.getFromHash(Hash)) + return dyn_cast_or_null<DWARFCompileUnit>( + DWOUnits.getUnitForIndexEntry(*R)); + return nullptr; + } + + // If there's no index, just search through the CUs in the DWO - there's + // probably only one unless this is something like LTO - though an in-process + // built/cached lookup table could be used in that case to improve repeated + // lookups of different CUs in the DWO. + for (const auto &DWOCU : dwo_compile_units()) { + // Might not have parsed DWO ID yet. + if (!DWOCU->getDWOId()) { + if (Optional<uint64_t> DWOId = + toUnsigned(DWOCU->getUnitDIE().find(DW_AT_GNU_dwo_id))) + DWOCU->setDWOId(*DWOId); + else + // No DWO ID? + continue; + } + if (DWOCU->getDWOId() == Hash) + return dyn_cast<DWARFCompileUnit>(DWOCU.get()); + } + return nullptr; +} + +DWARFDie DWARFContext::getDIEForOffset(uint64_t Offset) { + parseNormalUnits(); + if (auto *CU = NormalUnits.getUnitForOffset(Offset)) + return CU->getDIEForOffset(Offset); + return DWARFDie(); +} + +bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { + bool Success = true; + DWARFVerifier verifier(OS, *this, DumpOpts); + + Success &= verifier.handleDebugAbbrev(); + if (DumpOpts.DumpType & DIDT_DebugInfo) + Success &= verifier.handleDebugInfo(); + if (DumpOpts.DumpType & DIDT_DebugLine) + Success &= verifier.handleDebugLine(); + Success &= verifier.handleAccelTables(); + return Success; +} + +const DWARFUnitIndex &DWARFContext::getCUIndex() { + if (CUIndex) + return *CUIndex; + + DataExtractor CUIndexData(DObj->getCUIndexSection(), isLittleEndian(), 0); + + CUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_INFO); + CUIndex->parse(CUIndexData); + return *CUIndex; +} + +const DWARFUnitIndex &DWARFContext::getTUIndex() { + if (TUIndex) + return *TUIndex; + + DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); + + TUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_TYPES); + TUIndex->parse(TUIndexData); + return *TUIndex; +} + +DWARFGdbIndex &DWARFContext::getGdbIndex() { + if (GdbIndex) + return *GdbIndex; + + DataExtractor GdbIndexData(DObj->getGdbIndexSection(), true /*LE*/, 0); + GdbIndex = std::make_unique<DWARFGdbIndex>(); + GdbIndex->parse(GdbIndexData); + return *GdbIndex; +} + +const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { + if (Abbrev) + return Abbrev.get(); + + DataExtractor abbrData(DObj->getAbbrevSection(), isLittleEndian(), 0); + + Abbrev.reset(new DWARFDebugAbbrev()); + Abbrev->extract(abbrData); + return Abbrev.get(); +} + +const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { + if (AbbrevDWO) + return AbbrevDWO.get(); + + DataExtractor abbrData(DObj->getAbbrevDWOSection(), isLittleEndian(), 0); + AbbrevDWO.reset(new DWARFDebugAbbrev()); + AbbrevDWO->extract(abbrData); + return AbbrevDWO.get(); +} + +const DWARFDebugLoc *DWARFContext::getDebugLoc() { + if (Loc) + return Loc.get(); + + Loc.reset(new DWARFDebugLoc); + // Assume all units have the same address byte size. + if (getNumCompileUnits()) { + DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), + getUnitAtIndex(0)->getAddressByteSize()); + Loc->parse(LocData); + } + return Loc.get(); +} + +const DWARFDebugLoclists *DWARFContext::getDebugLocDWO() { + if (LocDWO) + return LocDWO.get(); + + LocDWO.reset(new DWARFDebugLoclists()); + // Assume all compile units have the same address byte size. + // FIXME: We don't need AddressSize for split DWARF since relocatable + // addresses cannot appear there. At the moment DWARFExpression requires it. + DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), 4); + // Use version 4. DWO does not support the DWARF v5 .debug_loclists yet and + // that means we are parsing the new style .debug_loc (pre-standatized version + // of the .debug_loclists). + LocDWO->parse(LocData, 0, LocData.getData().size(), 4 /* Version */); + return LocDWO.get(); +} + +const DWARFDebugAranges *DWARFContext::getDebugAranges() { + if (Aranges) + return Aranges.get(); + + Aranges.reset(new DWARFDebugAranges()); + Aranges->generate(this); + return Aranges.get(); +} + +const DWARFDebugFrame *DWARFContext::getDebugFrame() { + if (DebugFrame) + return DebugFrame.get(); + + // There's a "bug" in the DWARFv3 standard with respect to the target address + // size within debug frame sections. While DWARF is supposed to be independent + // of its container, FDEs have fields with size being "target address size", + // which isn't specified in DWARF in general. It's only specified for CUs, but + // .eh_frame can appear without a .debug_info section. Follow the example of + // other tools (libdwarf) and extract this from the container (ObjectFile + // provides this information). This problem is fixed in DWARFv4 + // See this dwarf-discuss discussion for more details: + // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html + DWARFDataExtractor debugFrameData(*DObj, DObj->getFrameSection(), + isLittleEndian(), DObj->getAddressSize()); + DebugFrame.reset(new DWARFDebugFrame(getArch(), false /* IsEH */)); + DebugFrame->parse(debugFrameData); + return DebugFrame.get(); +} + +const DWARFDebugFrame *DWARFContext::getEHFrame() { + if (EHFrame) + return EHFrame.get(); + + DWARFDataExtractor debugFrameData(*DObj, DObj->getEHFrameSection(), + isLittleEndian(), DObj->getAddressSize()); + DebugFrame.reset(new DWARFDebugFrame(getArch(), true /* IsEH */)); + DebugFrame->parse(debugFrameData); + return DebugFrame.get(); +} + +const DWARFDebugMacro *DWARFContext::getDebugMacro() { + if (Macro) + return Macro.get(); + + DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); + Macro.reset(new DWARFDebugMacro()); + Macro->parse(MacinfoData); + return Macro.get(); +} + +template <typename T> +static T &getAccelTable(std::unique_ptr<T> &Cache, const DWARFObject &Obj, + const DWARFSection &Section, StringRef StringSection, + bool IsLittleEndian) { + if (Cache) + return *Cache; + DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); + DataExtractor StrData(StringSection, IsLittleEndian, 0); + Cache.reset(new T(AccelSection, StrData)); + if (Error E = Cache->extract()) + llvm::consumeError(std::move(E)); + return *Cache; +} + +const DWARFDebugNames &DWARFContext::getDebugNames() { + return getAccelTable(Names, *DObj, DObj->getNamesSection(), + DObj->getStrSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleNames() { + return getAccelTable(AppleNames, *DObj, DObj->getAppleNamesSection(), + DObj->getStrSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleTypes() { + return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), + DObj->getStrSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleNamespaces() { + return getAccelTable(AppleNamespaces, *DObj, + DObj->getAppleNamespacesSection(), + DObj->getStrSection(), isLittleEndian()); +} + +const AppleAcceleratorTable &DWARFContext::getAppleObjC() { + return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), + DObj->getStrSection(), isLittleEndian()); +} + +const DWARFDebugLine::LineTable * +DWARFContext::getLineTableForUnit(DWARFUnit *U) { + Expected<const DWARFDebugLine::LineTable *> ExpectedLineTable = + getLineTableForUnit(U, dumpWarning); + if (!ExpectedLineTable) { + dumpWarning(ExpectedLineTable.takeError()); + return nullptr; + } + return *ExpectedLineTable; +} + +Expected<const DWARFDebugLine::LineTable *> DWARFContext::getLineTableForUnit( + DWARFUnit *U, std::function<void(Error)> RecoverableErrorCallback) { + if (!Line) + Line.reset(new DWARFDebugLine); + + auto UnitDIE = U->getUnitDIE(); + if (!UnitDIE) + return nullptr; + + auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); + if (!Offset) + return nullptr; // No line table for this compile unit. + + uint64_t stmtOffset = *Offset + U->getLineTableOffset(); + // See if the line table is cached. + if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) + return lt; + + // Make sure the offset is good before we try to parse. + if (stmtOffset >= U->getLineSection().Data.size()) + return nullptr; + + // We have to parse it first. + DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), + U->getAddressByteSize()); + return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, + RecoverableErrorCallback); +} + +void DWARFContext::parseNormalUnits() { + if (!NormalUnits.empty()) + return; + DObj->forEachInfoSections([&](const DWARFSection &S) { + NormalUnits.addUnitsForSection(*this, S, DW_SECT_INFO); + }); + NormalUnits.finishedInfoUnits(); + DObj->forEachTypesSections([&](const DWARFSection &S) { + NormalUnits.addUnitsForSection(*this, S, DW_SECT_TYPES); + }); +} + +void DWARFContext::parseDWOUnits(bool Lazy) { + if (!DWOUnits.empty()) + return; + DObj->forEachInfoDWOSections([&](const DWARFSection &S) { + DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_INFO, Lazy); + }); + DWOUnits.finishedInfoUnits(); + DObj->forEachTypesDWOSections([&](const DWARFSection &S) { + DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_TYPES, Lazy); + }); +} + +DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint64_t Offset) { + parseNormalUnits(); + return dyn_cast_or_null<DWARFCompileUnit>( + NormalUnits.getUnitForOffset(Offset)); +} + +DWARFCompileUnit *DWARFContext::getCompileUnitForAddress(uint64_t Address) { + // First, get the offset of the compile unit. + uint64_t CUOffset = getDebugAranges()->findAddress(Address); + // Retrieve the compile unit. + return getCompileUnitForOffset(CUOffset); +} + +DWARFContext::DIEsForAddress DWARFContext::getDIEsForAddress(uint64_t Address) { + DIEsForAddress Result; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return Result; + + Result.CompileUnit = CU; + Result.FunctionDIE = CU->getSubroutineForAddress(Address); + + std::vector<DWARFDie> Worklist; + Worklist.push_back(Result.FunctionDIE); + while (!Worklist.empty()) { + DWARFDie DIE = Worklist.back(); + Worklist.pop_back(); + + if (!DIE.isValid()) + continue; + + if (DIE.getTag() == DW_TAG_lexical_block && + DIE.addressRangeContainsAddress(Address)) { + Result.BlockDIE = DIE; + break; + } + + for (auto Child : DIE) + Worklist.push_back(Child); + } + + return Result; +} + +/// TODO: change input parameter from "uint64_t Address" +/// into "SectionedAddress Address" +static bool getFunctionNameAndStartLineForAddress(DWARFCompileUnit *CU, + uint64_t Address, + FunctionNameKind Kind, + std::string &FunctionName, + uint32_t &StartLine) { + // The address may correspond to instruction in some inlined function, + // so we have to build the chain of inlined functions and take the + // name of the topmost function in it. + SmallVector<DWARFDie, 4> InlinedChain; + CU->getInlinedChainForAddress(Address, InlinedChain); + if (InlinedChain.empty()) + return false; + + const DWARFDie &DIE = InlinedChain[0]; + bool FoundResult = false; + const char *Name = nullptr; + if (Kind != FunctionNameKind::None && (Name = DIE.getSubroutineName(Kind))) { + FunctionName = Name; + FoundResult = true; + } + if (auto DeclLineResult = DIE.getDeclLine()) { + StartLine = DeclLineResult; + FoundResult = true; + } + + return FoundResult; +} + +static Optional<uint64_t> getTypeSize(DWARFDie Type, uint64_t PointerSize) { + if (auto SizeAttr = Type.find(DW_AT_byte_size)) + if (Optional<uint64_t> Size = SizeAttr->getAsUnsignedConstant()) + return Size; + + switch (Type.getTag()) { + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + return PointerSize; + case DW_TAG_ptr_to_member_type: { + if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type)) + if (BaseType.getTag() == DW_TAG_subroutine_type) + return 2 * PointerSize; + return PointerSize; + } + case DW_TAG_const_type: + case DW_TAG_volatile_type: + case DW_TAG_restrict_type: + case DW_TAG_typedef: { + if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type)) + return getTypeSize(BaseType, PointerSize); + break; + } + case DW_TAG_array_type: { + DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type); + if (!BaseType) + return Optional<uint64_t>(); + Optional<uint64_t> BaseSize = getTypeSize(BaseType, PointerSize); + if (!BaseSize) + return Optional<uint64_t>(); + uint64_t Size = *BaseSize; + for (DWARFDie Child : Type) { + if (Child.getTag() != DW_TAG_subrange_type) + continue; + + if (auto ElemCountAttr = Child.find(DW_AT_count)) + if (Optional<uint64_t> ElemCount = + ElemCountAttr->getAsUnsignedConstant()) + Size *= *ElemCount; + if (auto UpperBoundAttr = Child.find(DW_AT_upper_bound)) + if (Optional<int64_t> UpperBound = + UpperBoundAttr->getAsSignedConstant()) { + int64_t LowerBound = 0; + if (auto LowerBoundAttr = Child.find(DW_AT_lower_bound)) + LowerBound = LowerBoundAttr->getAsSignedConstant().getValueOr(0); + Size *= *UpperBound - LowerBound + 1; + } + } + return Size; + } + default: + break; + } + return Optional<uint64_t>(); +} + +void DWARFContext::addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram, + DWARFDie Die, std::vector<DILocal> &Result) { + if (Die.getTag() == DW_TAG_variable || + Die.getTag() == DW_TAG_formal_parameter) { + DILocal Local; + if (auto NameAttr = Subprogram.find(DW_AT_name)) + if (Optional<const char *> Name = NameAttr->getAsCString()) + Local.FunctionName = *Name; + if (auto LocationAttr = Die.find(DW_AT_location)) + if (Optional<ArrayRef<uint8_t>> Location = LocationAttr->getAsBlock()) + if (!Location->empty() && (*Location)[0] == DW_OP_fbreg) + Local.FrameOffset = + decodeSLEB128(Location->data() + 1, nullptr, Location->end()); + if (auto TagOffsetAttr = Die.find(DW_AT_LLVM_tag_offset)) + Local.TagOffset = TagOffsetAttr->getAsUnsignedConstant(); + + if (auto Origin = + Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) + Die = Origin; + if (auto NameAttr = Die.find(DW_AT_name)) + if (Optional<const char *> Name = NameAttr->getAsCString()) + Local.Name = *Name; + if (auto Type = Die.getAttributeValueAsReferencedDie(DW_AT_type)) + Local.Size = getTypeSize(Type, getCUAddrSize()); + if (auto DeclFileAttr = Die.find(DW_AT_decl_file)) { + if (const auto *LT = CU->getContext().getLineTableForUnit(CU)) + LT->getFileNameByIndex( + DeclFileAttr->getAsUnsignedConstant().getValue(), + CU->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + Local.DeclFile); + } + if (auto DeclLineAttr = Die.find(DW_AT_decl_line)) + Local.DeclLine = DeclLineAttr->getAsUnsignedConstant().getValue(); + + Result.push_back(Local); + return; + } + + if (Die.getTag() == DW_TAG_inlined_subroutine) + if (auto Origin = + Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) + Subprogram = Origin; + + for (auto Child : Die) + addLocalsForDie(CU, Subprogram, Child, Result); +} + +std::vector<DILocal> +DWARFContext::getLocalsForAddress(object::SectionedAddress Address) { + std::vector<DILocal> Result; + DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); + if (!CU) + return Result; + + DWARFDie Subprogram = CU->getSubroutineForAddress(Address.Address); + if (Subprogram.isValid()) + addLocalsForDie(CU, Subprogram, Subprogram, Result); + return Result; +} + +DILineInfo DWARFContext::getLineInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Spec) { + DILineInfo Result; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); + if (!CU) + return Result; + + getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, + Result.FunctionName, Result.StartLine); + if (Spec.FLIKind != FileLineInfoKind::None) { + if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) { + LineTable->getFileLineInfoForAddress( + {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), + Spec.FLIKind, Result); + } + } + return Result; +} + +DILineInfoTable DWARFContext::getLineInfoForAddressRange( + object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Spec) { + DILineInfoTable Lines; + DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); + if (!CU) + return Lines; + + uint32_t StartLine = 0; + std::string FunctionName(DILineInfo::BadString); + getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, + FunctionName, StartLine); + + // If the Specifier says we don't need FileLineInfo, just + // return the top-most function at the starting address. + if (Spec.FLIKind == FileLineInfoKind::None) { + DILineInfo Result; + Result.FunctionName = FunctionName; + Result.StartLine = StartLine; + Lines.push_back(std::make_pair(Address.Address, Result)); + return Lines; + } + + const DWARFLineTable *LineTable = getLineTableForUnit(CU); + + // Get the index of row we're looking for in the line table. + std::vector<uint32_t> RowVector; + if (!LineTable->lookupAddressRange({Address.Address, Address.SectionIndex}, + Size, RowVector)) { + return Lines; + } + + for (uint32_t RowIndex : RowVector) { + // Take file number and line/column from the row. + const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex]; + DILineInfo Result; + LineTable->getFileNameByIndex(Row.File, CU->getCompilationDir(), + Spec.FLIKind, Result.FileName); + Result.FunctionName = FunctionName; + Result.Line = Row.Line; + Result.Column = Row.Column; + Result.StartLine = StartLine; + Lines.push_back(std::make_pair(Row.Address.Address, Result)); + } + + return Lines; +} + +DIInliningInfo +DWARFContext::getInliningInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Spec) { + DIInliningInfo InliningInfo; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); + if (!CU) + return InliningInfo; + + const DWARFLineTable *LineTable = nullptr; + SmallVector<DWARFDie, 4> InlinedChain; + CU->getInlinedChainForAddress(Address.Address, InlinedChain); + if (InlinedChain.size() == 0) { + // If there is no DIE for address (e.g. it is in unavailable .dwo file), + // try to at least get file/line info from symbol table. + if (Spec.FLIKind != FileLineInfoKind::None) { + DILineInfo Frame; + LineTable = getLineTableForUnit(CU); + if (LineTable && LineTable->getFileLineInfoForAddress( + {Address.Address, Address.SectionIndex}, + CU->getCompilationDir(), Spec.FLIKind, Frame)) + InliningInfo.addFrame(Frame); + } + return InliningInfo; + } + + uint32_t CallFile = 0, CallLine = 0, CallColumn = 0, CallDiscriminator = 0; + for (uint32_t i = 0, n = InlinedChain.size(); i != n; i++) { + DWARFDie &FunctionDIE = InlinedChain[i]; + DILineInfo Frame; + // Get function name if necessary. + if (const char *Name = FunctionDIE.getSubroutineName(Spec.FNKind)) + Frame.FunctionName = Name; + if (auto DeclLineResult = FunctionDIE.getDeclLine()) + Frame.StartLine = DeclLineResult; + if (Spec.FLIKind != FileLineInfoKind::None) { + if (i == 0) { + // For the topmost frame, initialize the line table of this + // compile unit and fetch file/line info from it. + LineTable = getLineTableForUnit(CU); + // For the topmost routine, get file/line info from line table. + if (LineTable) + LineTable->getFileLineInfoForAddress( + {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), + Spec.FLIKind, Frame); + } else { + // Otherwise, use call file, call line and call column from + // previous DIE in inlined chain. + if (LineTable) + LineTable->getFileNameByIndex(CallFile, CU->getCompilationDir(), + Spec.FLIKind, Frame.FileName); + Frame.Line = CallLine; + Frame.Column = CallColumn; + Frame.Discriminator = CallDiscriminator; + } + // Get call file/line/column of a current DIE. + if (i + 1 < n) { + FunctionDIE.getCallerFrame(CallFile, CallLine, CallColumn, + CallDiscriminator); + } + } + InliningInfo.addFrame(Frame); + } + return InliningInfo; +} + +std::shared_ptr<DWARFContext> +DWARFContext::getDWOContext(StringRef AbsolutePath) { + if (auto S = DWP.lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + std::weak_ptr<DWOFile> *Entry = &DWOFiles[AbsolutePath]; + + if (auto S = Entry->lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + Expected<OwningBinary<ObjectFile>> Obj = [&] { + if (!CheckedForDWP) { + SmallString<128> DWPName; + auto Obj = object::ObjectFile::createObjectFile( + this->DWPName.empty() + ? (DObj->getFileName() + ".dwp").toStringRef(DWPName) + : StringRef(this->DWPName)); + if (Obj) { + Entry = &DWP; + return Obj; + } else { + CheckedForDWP = true; + // TODO: Should this error be handled (maybe in a high verbosity mode) + // before falling back to .dwo files? + consumeError(Obj.takeError()); + } + } + + return object::ObjectFile::createObjectFile(AbsolutePath); + }(); + + if (!Obj) { + // TODO: Actually report errors helpfully. + consumeError(Obj.takeError()); + return nullptr; + } + + auto S = std::make_shared<DWOFile>(); + S->File = std::move(Obj.get()); + S->Context = DWARFContext::create(*S->File.getBinary()); + *Entry = S; + auto *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); +} + +static Error createError(const Twine &Reason, llvm::Error E) { + return make_error<StringError>(Reason + toString(std::move(E)), + inconvertibleErrorCode()); +} + +/// SymInfo contains information about symbol: it's address +/// and section index which is -1LL for absolute symbols. +struct SymInfo { + uint64_t Address; + uint64_t SectionIndex; +}; + +/// Returns the address of symbol relocation used against and a section index. +/// Used for futher relocations computation. Symbol's section load address is +static Expected<SymInfo> getSymbolInfo(const object::ObjectFile &Obj, + const RelocationRef &Reloc, + const LoadedObjectInfo *L, + std::map<SymbolRef, SymInfo> &Cache) { + SymInfo Ret = {0, (uint64_t)-1LL}; + object::section_iterator RSec = Obj.section_end(); + object::symbol_iterator Sym = Reloc.getSymbol(); + + std::map<SymbolRef, SymInfo>::iterator CacheIt = Cache.end(); + // First calculate the address of the symbol or section as it appears + // in the object file + if (Sym != Obj.symbol_end()) { + bool New; + std::tie(CacheIt, New) = Cache.insert({*Sym, {0, 0}}); + if (!New) + return CacheIt->second; + + Expected<uint64_t> SymAddrOrErr = Sym->getAddress(); + if (!SymAddrOrErr) + return createError("failed to compute symbol address: ", + SymAddrOrErr.takeError()); + + // Also remember what section this symbol is in for later + auto SectOrErr = Sym->getSection(); + if (!SectOrErr) + return createError("failed to get symbol section: ", + SectOrErr.takeError()); + + RSec = *SectOrErr; + Ret.Address = *SymAddrOrErr; + } else if (auto *MObj = dyn_cast<MachOObjectFile>(&Obj)) { + RSec = MObj->getRelocationSection(Reloc.getRawDataRefImpl()); + Ret.Address = RSec->getAddress(); + } + + if (RSec != Obj.section_end()) + Ret.SectionIndex = RSec->getIndex(); + + // If we are given load addresses for the sections, we need to adjust: + // SymAddr = (Address of Symbol Or Section in File) - + // (Address of Section in File) + + // (Load Address of Section) + // RSec is now either the section being targeted or the section + // containing the symbol being targeted. In either case, + // we need to perform the same computation. + if (L && RSec != Obj.section_end()) + if (uint64_t SectionLoadAddress = L->getSectionLoadAddress(*RSec)) + Ret.Address += SectionLoadAddress - RSec->getAddress(); + + if (CacheIt != Cache.end()) + CacheIt->second = Ret; + + return Ret; +} + +static bool isRelocScattered(const object::ObjectFile &Obj, + const RelocationRef &Reloc) { + const MachOObjectFile *MachObj = dyn_cast<MachOObjectFile>(&Obj); + if (!MachObj) + return false; + // MachO also has relocations that point to sections and + // scattered relocations. + auto RelocInfo = MachObj->getRelocation(Reloc.getRawDataRefImpl()); + return MachObj->isRelocationScattered(RelocInfo); +} + +ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { + WithColor::error() << toString(std::move(E)) << '\n'; + return ErrorPolicy::Continue; +} + +namespace { +struct DWARFSectionMap final : public DWARFSection { + RelocAddrMap Relocs; +}; + +class DWARFObjInMemory final : public DWARFObject { + bool IsLittleEndian; + uint8_t AddressSize; + StringRef FileName; + const object::ObjectFile *Obj = nullptr; + std::vector<SectionName> SectionNames; + + using InfoSectionMap = MapVector<object::SectionRef, DWARFSectionMap, + std::map<object::SectionRef, unsigned>>; + + InfoSectionMap InfoSections; + InfoSectionMap TypesSections; + InfoSectionMap InfoDWOSections; + InfoSectionMap TypesDWOSections; + + DWARFSectionMap LocSection; + DWARFSectionMap LoclistsSection; + DWARFSectionMap LineSection; + DWARFSectionMap RangesSection; + DWARFSectionMap RnglistsSection; + DWARFSectionMap StrOffsetsSection; + DWARFSectionMap LineDWOSection; + DWARFSectionMap FrameSection; + DWARFSectionMap EHFrameSection; + DWARFSectionMap LocDWOSection; + DWARFSectionMap StrOffsetsDWOSection; + DWARFSectionMap RangesDWOSection; + DWARFSectionMap RnglistsDWOSection; + DWARFSectionMap AddrSection; + DWARFSectionMap AppleNamesSection; + DWARFSectionMap AppleTypesSection; + DWARFSectionMap AppleNamespacesSection; + DWARFSectionMap AppleObjCSection; + DWARFSectionMap NamesSection; + DWARFSectionMap PubnamesSection; + DWARFSectionMap PubtypesSection; + DWARFSectionMap GnuPubnamesSection; + DWARFSectionMap GnuPubtypesSection; + + DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { + return StringSwitch<DWARFSectionMap *>(Name) + .Case("debug_loc", &LocSection) + .Case("debug_loclists", &LoclistsSection) + .Case("debug_line", &LineSection) + .Case("debug_frame", &FrameSection) + .Case("eh_frame", &EHFrameSection) + .Case("debug_str_offsets", &StrOffsetsSection) + .Case("debug_ranges", &RangesSection) + .Case("debug_rnglists", &RnglistsSection) + .Case("debug_loc.dwo", &LocDWOSection) + .Case("debug_line.dwo", &LineDWOSection) + .Case("debug_names", &NamesSection) + .Case("debug_rnglists.dwo", &RnglistsDWOSection) + .Case("debug_str_offsets.dwo", &StrOffsetsDWOSection) + .Case("debug_addr", &AddrSection) + .Case("apple_names", &AppleNamesSection) + .Case("debug_pubnames", &PubnamesSection) + .Case("debug_pubtypes", &PubtypesSection) + .Case("debug_gnu_pubnames", &GnuPubnamesSection) + .Case("debug_gnu_pubtypes", &GnuPubtypesSection) + .Case("apple_types", &AppleTypesSection) + .Case("apple_namespaces", &AppleNamespacesSection) + .Case("apple_namespac", &AppleNamespacesSection) + .Case("apple_objc", &AppleObjCSection) + .Default(nullptr); + } + + StringRef AbbrevSection; + StringRef ArangesSection; + StringRef StrSection; + StringRef MacinfoSection; + StringRef AbbrevDWOSection; + StringRef StrDWOSection; + StringRef CUIndexSection; + StringRef GdbIndexSection; + StringRef TUIndexSection; + StringRef LineStrSection; + + // A deque holding section data whose iterators are not invalidated when + // new decompressed sections are inserted at the end. + std::deque<SmallString<0>> UncompressedSections; + + StringRef *mapSectionToMember(StringRef Name) { + if (DWARFSection *Sec = mapNameToDWARFSection(Name)) + return &Sec->Data; + return StringSwitch<StringRef *>(Name) + .Case("debug_abbrev", &AbbrevSection) + .Case("debug_aranges", &ArangesSection) + .Case("debug_str", &StrSection) + .Case("debug_macinfo", &MacinfoSection) + .Case("debug_abbrev.dwo", &AbbrevDWOSection) + .Case("debug_str.dwo", &StrDWOSection) + .Case("debug_cu_index", &CUIndexSection) + .Case("debug_tu_index", &TUIndexSection) + .Case("gdb_index", &GdbIndexSection) + .Case("debug_line_str", &LineStrSection) + // Any more debug info sections go here. + .Default(nullptr); + } + + /// If Sec is compressed section, decompresses and updates its contents + /// provided by Data. Otherwise leaves it unchanged. + Error maybeDecompress(const object::SectionRef &Sec, StringRef Name, + StringRef &Data) { + if (!Decompressor::isCompressed(Sec)) + return Error::success(); + + Expected<Decompressor> Decompressor = + Decompressor::create(Name, Data, IsLittleEndian, AddressSize == 8); + if (!Decompressor) + return Decompressor.takeError(); + + SmallString<0> Out; + if (auto Err = Decompressor->resizeAndDecompress(Out)) + return Err; + + UncompressedSections.push_back(std::move(Out)); + Data = UncompressedSections.back(); + + return Error::success(); + } + +public: + DWARFObjInMemory(const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, + uint8_t AddrSize, bool IsLittleEndian) + : IsLittleEndian(IsLittleEndian) { + for (const auto &SecIt : Sections) { + if (StringRef *SectionData = mapSectionToMember(SecIt.first())) + *SectionData = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_info") + // Find debug_info and debug_types data by section rather than name as + // there are multiple, comdat grouped, of these sections. + InfoSections[SectionRef()].Data = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_info.dwo") + InfoDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_types") + TypesSections[SectionRef()].Data = SecIt.second->getBuffer(); + else if (SecIt.first() == "debug_types.dwo") + TypesDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); + } + } + DWARFObjInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L, + function_ref<ErrorPolicy(Error)> HandleError) + : IsLittleEndian(Obj.isLittleEndian()), + AddressSize(Obj.getBytesInAddress()), FileName(Obj.getFileName()), + Obj(&Obj) { + + StringMap<unsigned> SectionAmountMap; + for (const SectionRef &Section : Obj.sections()) { + StringRef Name; + if (auto NameOrErr = Section.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + ++SectionAmountMap[Name]; + SectionNames.push_back({ Name, true }); + + // Skip BSS and Virtual sections, they aren't interesting. + if (Section.isBSS() || Section.isVirtual()) + continue; + + // Skip sections stripped by dsymutil. + if (Section.isStripped()) + continue; + + StringRef Data; + Expected<section_iterator> SecOrErr = Section.getRelocatedSection(); + if (!SecOrErr) { + ErrorPolicy EP = HandleError(createError( + "failed to get relocated section: ", SecOrErr.takeError())); + if (EP == ErrorPolicy::Halt) + return; + continue; + } + + // 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. + section_iterator RelocatedSection = *SecOrErr; + if (!L || !L->getLoadedSectionContents(*RelocatedSection, Data)) { + Expected<StringRef> E = Section.getContents(); + if (E) + Data = *E; + else + // maybeDecompress below will error. + consumeError(E.takeError()); + } + + if (auto Err = maybeDecompress(Section, Name, Data)) { + ErrorPolicy EP = HandleError(createError( + "failed to decompress '" + Name + "', ", std::move(Err))); + if (EP == ErrorPolicy::Halt) + return; + continue; + } + + // Compressed sections names in GNU style starts from ".z", + // at this point section is decompressed and we drop compression prefix. + Name = Name.substr( + Name.find_first_not_of("._z")); // Skip ".", "z" and "_" prefixes. + + // Map platform specific debug section names to DWARF standard section + // names. + Name = Obj.mapDebugSectionName(Name); + + if (StringRef *SectionData = mapSectionToMember(Name)) { + *SectionData = Data; + if (Name == "debug_ranges") { + // FIXME: Use the other dwo range section when we emit it. + RangesDWOSection.Data = Data; + } + } else if (Name == "debug_info") { + // Find debug_info and debug_types data by section rather than name as + // there are multiple, comdat grouped, of these sections. + InfoSections[Section].Data = Data; + } else if (Name == "debug_info.dwo") { + InfoDWOSections[Section].Data = Data; + } else if (Name == "debug_types") { + TypesSections[Section].Data = Data; + } else if (Name == "debug_types.dwo") { + TypesDWOSections[Section].Data = Data; + } + + if (RelocatedSection == Obj.section_end()) + continue; + + StringRef RelSecName; + if (auto NameOrErr = RelocatedSection->getName()) + RelSecName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + // 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. + StringRef RelSecData; + if (L && L->getLoadedSectionContents(*RelocatedSection, RelSecData)) + continue; + + // In Mach-o files, the relocations do not need to be applied if + // there is no load offset to apply. The value read at the + // relocation point already factors in the section address + // (actually applying the relocations will produce wrong results + // as the section address will be added twice). + if (!L && isa<MachOObjectFile>(&Obj)) + continue; + + RelSecName = RelSecName.substr( + RelSecName.find_first_not_of("._z")); // Skip . and _ prefixes. + + // TODO: Add support for relocations in other sections as needed. + // Record relocations for the debug_info and debug_line sections. + DWARFSectionMap *Sec = mapNameToDWARFSection(RelSecName); + RelocAddrMap *Map = Sec ? &Sec->Relocs : nullptr; + if (!Map) { + // Find debug_info and debug_types relocs by section rather than name + // as there are multiple, comdat grouped, of these sections. + if (RelSecName == "debug_info") + Map = &static_cast<DWARFSectionMap &>(InfoSections[*RelocatedSection]) + .Relocs; + else if (RelSecName == "debug_info.dwo") + Map = &static_cast<DWARFSectionMap &>( + InfoDWOSections[*RelocatedSection]) + .Relocs; + else if (RelSecName == "debug_types") + Map = + &static_cast<DWARFSectionMap &>(TypesSections[*RelocatedSection]) + .Relocs; + else if (RelSecName == "debug_types.dwo") + Map = &static_cast<DWARFSectionMap &>( + TypesDWOSections[*RelocatedSection]) + .Relocs; + else + continue; + } + + if (Section.relocation_begin() == Section.relocation_end()) + continue; + + // Symbol to [address, section index] cache mapping. + std::map<SymbolRef, SymInfo> AddrCache; + bool (*Supports)(uint64_t); + RelocationResolver Resolver; + std::tie(Supports, Resolver) = getRelocationResolver(Obj); + 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; + } + + // Check if Resolver can handle this relocation type early so as not to + // handle invalid cases in DWARFDataExtractor. + // + // TODO Don't store Resolver in every RelocAddrEntry. + if (Supports && Supports(Reloc.getType())) { + auto I = Map->try_emplace( + Reloc.getOffset(), + RelocAddrEntry{SymInfoOrErr->SectionIndex, Reloc, + SymInfoOrErr->Address, + Optional<object::RelocationRef>(), 0, Resolver}); + // If we didn't successfully insert that's because we already had a + // relocation for that offset. Store it as a second relocation in the + // same RelocAddrEntry instead. + if (!I.second) { + RelocAddrEntry &entry = I.first->getSecond(); + if (entry.Reloc2) { + ErrorPolicy EP = HandleError(createError( + "At most two relocations per offset are supported")); + if (EP == ErrorPolicy::Halt) + return; + } + entry.Reloc2 = Reloc; + entry.SymbolValue2 = SymInfoOrErr->Address; + } + } else { + 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; + } + } + } + + for (SectionName &S : SectionNames) + if (SectionAmountMap[S.Name] > 1) + S.IsNameUnique = false; + } + + Optional<RelocAddrEntry> find(const DWARFSection &S, + uint64_t Pos) const override { + auto &Sec = static_cast<const DWARFSectionMap &>(S); + RelocAddrMap::const_iterator AI = Sec.Relocs.find(Pos); + if (AI == Sec.Relocs.end()) + return None; + return AI->second; + } + + const object::ObjectFile *getFile() const override { return Obj; } + + ArrayRef<SectionName> getSectionNames() const override { + return SectionNames; + } + + bool isLittleEndian() const override { return IsLittleEndian; } + StringRef getAbbrevDWOSection() const override { return AbbrevDWOSection; } + const DWARFSection &getLineDWOSection() const override { + return LineDWOSection; + } + const DWARFSection &getLocDWOSection() const override { + return LocDWOSection; + } + StringRef getStrDWOSection() const override { return StrDWOSection; } + const DWARFSection &getStrOffsetsDWOSection() const override { + return StrOffsetsDWOSection; + } + const DWARFSection &getRangesDWOSection() const override { + return RangesDWOSection; + } + const DWARFSection &getRnglistsDWOSection() const override { + return RnglistsDWOSection; + } + const DWARFSection &getAddrSection() const override { return AddrSection; } + StringRef getCUIndexSection() const override { return CUIndexSection; } + StringRef getGdbIndexSection() const override { return GdbIndexSection; } + StringRef getTUIndexSection() const override { return TUIndexSection; } + + // DWARF v5 + const DWARFSection &getStrOffsetsSection() const override { + return StrOffsetsSection; + } + StringRef getLineStrSection() const override { return LineStrSection; } + + // Sections for DWARF5 split dwarf proposal. + void forEachInfoDWOSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : InfoDWOSections) + F(P.second); + } + void forEachTypesDWOSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : TypesDWOSections) + F(P.second); + } + + StringRef getAbbrevSection() const override { return AbbrevSection; } + const DWARFSection &getLocSection() const override { return LocSection; } + const DWARFSection &getLoclistsSection() const override { return LoclistsSection; } + StringRef getArangesSection() const override { return ArangesSection; } + const DWARFSection &getFrameSection() const override { + return FrameSection; + } + const DWARFSection &getEHFrameSection() const override { + return EHFrameSection; + } + const DWARFSection &getLineSection() const override { return LineSection; } + StringRef getStrSection() const override { return StrSection; } + const DWARFSection &getRangesSection() const override { return RangesSection; } + const DWARFSection &getRnglistsSection() const override { + return RnglistsSection; + } + StringRef getMacinfoSection() const override { return MacinfoSection; } + const DWARFSection &getPubnamesSection() const override { return PubnamesSection; } + const DWARFSection &getPubtypesSection() const override { return PubtypesSection; } + const DWARFSection &getGnuPubnamesSection() const override { + return GnuPubnamesSection; + } + const DWARFSection &getGnuPubtypesSection() const override { + return GnuPubtypesSection; + } + const DWARFSection &getAppleNamesSection() const override { + return AppleNamesSection; + } + const DWARFSection &getAppleTypesSection() const override { + return AppleTypesSection; + } + const DWARFSection &getAppleNamespacesSection() const override { + return AppleNamespacesSection; + } + const DWARFSection &getAppleObjCSection() const override { + return AppleObjCSection; + } + const DWARFSection &getNamesSection() const override { + return NamesSection; + } + + StringRef getFileName() const override { return FileName; } + uint8_t getAddressSize() const override { return AddressSize; } + void forEachInfoSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : InfoSections) + F(P.second); + } + void forEachTypesSections( + function_ref<void(const DWARFSection &)> F) const override { + for (auto &P : TypesSections) + F(P.second); + } +}; +} // namespace + +std::unique_ptr<DWARFContext> +DWARFContext::create(const object::ObjectFile &Obj, const LoadedObjectInfo *L, + function_ref<ErrorPolicy(Error)> HandleError, + std::string DWPName) { + auto DObj = std::make_unique<DWARFObjInMemory>(Obj, L, HandleError); + return std::make_unique<DWARFContext>(std::move(DObj), std::move(DWPName)); +} + +std::unique_ptr<DWARFContext> +DWARFContext::create(const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, + uint8_t AddrSize, bool isLittleEndian) { + auto DObj = + std::make_unique<DWARFObjInMemory>(Sections, AddrSize, isLittleEndian); + return std::make_unique<DWARFContext>(std::move(DObj), ""); +} + +Error DWARFContext::loadRegisterInfo(const object::ObjectFile &Obj) { + // Detect the architecture from the object file. We usually don't need OS + // info to lookup a target and create register info. + Triple TT; + TT.setArch(Triple::ArchType(Obj.getArch())); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::UnknownOS); + std::string TargetLookupError; + const Target *TheTarget = + TargetRegistry::lookupTarget(TT.str(), TargetLookupError); + if (!TargetLookupError.empty()) + return createStringError(errc::invalid_argument, + TargetLookupError.c_str()); + RegInfo.reset(TheTarget->createMCRegInfo(TT.str())); + return Error::success(); +} + +uint8_t DWARFContext::getCUAddrSize() { + // In theory, different compile units may have different address byte + // sizes, but for simplicity we just use the address byte size of the + // last compile unit. In practice the address size field is repeated across + // various DWARF headers (at least in version 5) to make it easier to dump + // them independently, not to enable varying the address size. + uint8_t Addr = 0; + for (const auto &CU : compile_units()) { + Addr = CU->getAddressByteSize(); + break; + } + return Addr; +} + +void DWARFContext::dumpWarning(Error Warning) { + handleAllErrors(std::move(Warning), [](ErrorInfoBase &Info) { + WithColor::warning() << Info.message() << '\n'; + }); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp new file mode 100644 index 000000000000..53e676bc7031 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -0,0 +1,100 @@ +//===- DWARFDataExtractor.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" + +using namespace llvm; + +uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off, + uint64_t *SecNdx, + Error *Err) const { + if (SecNdx) + *SecNdx = object::SectionedAddress::UndefSection; + if (!Section) + return getUnsigned(Off, Size, Err); + Optional<RelocAddrEntry> E = Obj->find(*Section, *Off); + uint64_t A = getUnsigned(Off, Size, Err); + if (!E) + return A; + if (SecNdx) + *SecNdx = E->SectionIndex; + uint64_t R = E->Resolver(E->Reloc, E->SymbolValue, A); + if (E->Reloc2) + R = E->Resolver(*E->Reloc2, E->SymbolValue2, R); + return R; +} + +Optional<uint64_t> +DWARFDataExtractor::getEncodedPointer(uint64_t *Offset, uint8_t Encoding, + uint64_t PCRelOffset) const { + if (Encoding == dwarf::DW_EH_PE_omit) + return None; + + uint64_t Result = 0; + uint64_t OldOffset = *Offset; + // First get value + switch (Encoding & 0x0F) { + case dwarf::DW_EH_PE_absptr: + switch (getAddressSize()) { + case 2: + case 4: + case 8: + Result = getUnsigned(Offset, getAddressSize()); + break; + default: + return None; + } + break; + case dwarf::DW_EH_PE_uleb128: + Result = getULEB128(Offset); + break; + case dwarf::DW_EH_PE_sleb128: + Result = getSLEB128(Offset); + break; + case dwarf::DW_EH_PE_udata2: + Result = getUnsigned(Offset, 2); + break; + case dwarf::DW_EH_PE_udata4: + Result = getUnsigned(Offset, 4); + break; + case dwarf::DW_EH_PE_udata8: + Result = getUnsigned(Offset, 8); + break; + case dwarf::DW_EH_PE_sdata2: + Result = getSigned(Offset, 2); + break; + case dwarf::DW_EH_PE_sdata4: + Result = getSigned(Offset, 4); + break; + case dwarf::DW_EH_PE_sdata8: + Result = getSigned(Offset, 8); + break; + default: + return None; + } + // Then add relative offset, if required + switch (Encoding & 0x70) { + case dwarf::DW_EH_PE_absptr: + // do nothing + break; + case dwarf::DW_EH_PE_pcrel: + Result += PCRelOffset; + break; + case dwarf::DW_EH_PE_datarel: + case dwarf::DW_EH_PE_textrel: + case dwarf::DW_EH_PE_funcrel: + case dwarf::DW_EH_PE_aligned: + default: + *Offset = OldOffset; + return None; + } + + return Result; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 000000000000..4afac2f99503 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,138 @@ +//===- DWARFDebugAbbrev.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +DWARFAbbreviationDeclarationSet::DWARFAbbreviationDeclarationSet() { + clear(); +} + +void DWARFAbbreviationDeclarationSet::clear() { + Offset = 0; + FirstAbbrCode = 0; + Decls.clear(); +} + +bool DWARFAbbreviationDeclarationSet::extract(DataExtractor Data, + uint64_t *OffsetPtr) { + clear(); + const uint64_t BeginOffset = *OffsetPtr; + Offset = BeginOffset; + DWARFAbbreviationDeclaration AbbrDecl; + uint32_t PrevAbbrCode = 0; + while (AbbrDecl.extract(Data, OffsetPtr)) { + if (FirstAbbrCode == 0) { + FirstAbbrCode = AbbrDecl.getCode(); + } else { + if (PrevAbbrCode + 1 != AbbrDecl.getCode()) { + // Codes are not consecutive, can't do O(1) lookups. + FirstAbbrCode = UINT32_MAX; + } + } + PrevAbbrCode = AbbrDecl.getCode(); + Decls.push_back(std::move(AbbrDecl)); + } + return BeginOffset != *OffsetPtr; +} + +void DWARFAbbreviationDeclarationSet::dump(raw_ostream &OS) const { + for (const auto &Decl : Decls) + Decl.dump(OS); +} + +const DWARFAbbreviationDeclaration * +DWARFAbbreviationDeclarationSet::getAbbreviationDeclaration( + uint32_t AbbrCode) const { + if (FirstAbbrCode == UINT32_MAX) { + for (const auto &Decl : Decls) { + if (Decl.getCode() == AbbrCode) + return &Decl; + } + return nullptr; + } + if (AbbrCode < FirstAbbrCode || AbbrCode >= FirstAbbrCode + Decls.size()) + return nullptr; + return &Decls[AbbrCode - FirstAbbrCode]; +} + +DWARFDebugAbbrev::DWARFDebugAbbrev() { clear(); } + +void DWARFDebugAbbrev::clear() { + AbbrDeclSets.clear(); + PrevAbbrOffsetPos = AbbrDeclSets.end(); +} + +void DWARFDebugAbbrev::extract(DataExtractor Data) { + clear(); + this->Data = Data; +} + +void DWARFDebugAbbrev::parse() const { + if (!Data) + return; + uint64_t Offset = 0; + auto I = AbbrDeclSets.begin(); + while (Data->isValidOffset(Offset)) { + while (I != AbbrDeclSets.end() && I->first < Offset) + ++I; + uint64_t CUAbbrOffset = Offset; + DWARFAbbreviationDeclarationSet AbbrDecls; + if (!AbbrDecls.extract(*Data, &Offset)) + break; + AbbrDeclSets.insert(I, std::make_pair(CUAbbrOffset, std::move(AbbrDecls))); + } + Data = None; +} + +void DWARFDebugAbbrev::dump(raw_ostream &OS) const { + parse(); + + if (AbbrDeclSets.empty()) { + OS << "< EMPTY >\n"; + return; + } + + for (const auto &I : AbbrDeclSets) { + OS << format("Abbrev table for offset: 0x%8.8" PRIx64 "\n", I.first); + I.second.dump(OS); + } +} + +const DWARFAbbreviationDeclarationSet* +DWARFDebugAbbrev::getAbbreviationDeclarationSet(uint64_t CUAbbrOffset) const { + const auto End = AbbrDeclSets.end(); + if (PrevAbbrOffsetPos != End && PrevAbbrOffsetPos->first == CUAbbrOffset) { + return &(PrevAbbrOffsetPos->second); + } + + const auto Pos = AbbrDeclSets.find(CUAbbrOffset); + if (Pos != End) { + PrevAbbrOffsetPos = Pos; + return &(Pos->second); + } + + if (Data && CUAbbrOffset < Data->getData().size()) { + uint64_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/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp new file mode 100644 index 000000000000..f71543799e28 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp @@ -0,0 +1,182 @@ +//===- DWARFDebugAddr.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +using namespace llvm; + +void DWARFDebugAddrTable::clear() { + HeaderData = {}; + Addrs.clear(); + invalidateLength(); +} + +Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data, + uint64_t *OffsetPtr, + uint16_t Version, + uint8_t AddrSize, + std::function<void(Error)> WarnCallback) { + clear(); + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createStringError(errc::invalid_argument, + "section is not large enough to contain a " + ".debug_addr table length at offset 0x%" + PRIx64, *OffsetPtr); + uint16_t UnitVersion; + if (Version == 0) { + WarnCallback(createStringError(errc::invalid_argument, + "DWARF version is not defined in CU," + " assuming version 5")); + UnitVersion = 5; + } else { + UnitVersion = Version; + } + // TODO: Add support for DWARF64. + Format = dwarf::DwarfFormat::DWARF32; + if (UnitVersion >= 5) { + HeaderData.Length = Data.getU32(OffsetPtr); + if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { + invalidateLength(); + return createStringError(errc::not_supported, + "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64, + HeaderOffset); + } + if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) { + uint32_t TmpLength = getLength(); + invalidateLength(); + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx64 + " has too small length (0x%" PRIx32 + ") to contain a complete header", + HeaderOffset, TmpLength); + } + uint64_t End = HeaderOffset + getLength(); + if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) { + uint32_t TmpLength = getLength(); + invalidateLength(); + return createStringError(errc::invalid_argument, + "section is not large enough to contain a .debug_addr table " + "of length 0x%" PRIx32 " at offset 0x%" PRIx64, + TmpLength, HeaderOffset); + } + + HeaderData.Version = Data.getU16(OffsetPtr); + HeaderData.AddrSize = Data.getU8(OffsetPtr); + HeaderData.SegSize = Data.getU8(OffsetPtr); + DataSize = getDataSize(); + } else { + HeaderData.Version = UnitVersion; + HeaderData.AddrSize = AddrSize; + // TODO: Support for non-zero SegSize. + HeaderData.SegSize = 0; + DataSize = Data.size(); + } + + // Perform basic validation of the remaining header fields. + + // We support DWARF version 5 for now as well as pre-DWARF5 + // implementations of .debug_addr table, which doesn't contain a header + // and consists only of a series of addresses. + if (HeaderData.Version > 5) { + return createStringError(errc::not_supported, "version %" PRIu16 + " of .debug_addr section at offset 0x%" PRIx64 " is not supported", + HeaderData.Version, HeaderOffset); + } + // FIXME: For now we just treat version mismatch as an error, + // however the correct way to associate a .debug_addr table + // with a .debug_info table is to look at the DW_AT_addr_base + // attribute in the info table. + if (HeaderData.Version != UnitVersion) + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx64 + " has version %" PRIu16 + " which is different from the version suggested" + " by the DWARF unit header: %" PRIu16, + HeaderOffset, HeaderData.Version, UnitVersion); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createStringError(errc::not_supported, + ".debug_addr table at offset 0x%" PRIx64 + " has unsupported address size %" PRIu8, + HeaderOffset, HeaderData.AddrSize); + if (HeaderData.AddrSize != AddrSize && AddrSize != 0) + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx64 + " has address size %" PRIu8 + " which is different from CU address size %" PRIu8, + HeaderOffset, HeaderData.AddrSize, AddrSize); + + // TODO: add support for non-zero segment selector size. + if (HeaderData.SegSize != 0) + return createStringError(errc::not_supported, + ".debug_addr table at offset 0x%" PRIx64 + " has unsupported segment selector size %" PRIu8, + HeaderOffset, HeaderData.SegSize); + if (DataSize % HeaderData.AddrSize != 0) { + invalidateLength(); + return createStringError(errc::invalid_argument, + ".debug_addr table at offset 0x%" PRIx64 + " contains data of size %" PRIu32 + " which is not a multiple of addr size %" PRIu8, + HeaderOffset, DataSize, HeaderData.AddrSize); + } + Data.setAddressSize(HeaderData.AddrSize); + uint32_t AddrCount = DataSize / HeaderData.AddrSize; + for (uint32_t I = 0; I < AddrCount; ++I) + if (HeaderData.AddrSize == 4) + Addrs.push_back(Data.getU32(OffsetPtr)); + else + Addrs.push_back(Data.getU64(OffsetPtr)); + return Error::success(); +} + +void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (DumpOpts.Verbose) + OS << format("0x%8.8" PRIx32 ": ", HeaderOffset); + OS << format("Addr Section: length = 0x%8.8" PRIx32 + ", version = 0x%4.4" PRIx16 ", " + "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n", + HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, + HeaderData.SegSize); + + if (Addrs.size() > 0) { + const char *AddrFmt = (HeaderData.AddrSize == 4) ? "0x%8.8" PRIx64 "\n" + : "0x%16.16" PRIx64 "\n"; + OS << "Addrs: [\n"; + for (uint64_t Addr : Addrs) + OS << format(AddrFmt, Addr); + OS << "]\n"; + } +} + +Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const { + if (Index < Addrs.size()) + return Addrs[Index]; + return createStringError(errc::invalid_argument, + "Index %" PRIu32 " is out of range of the " + ".debug_addr table at offset 0x%" PRIx64, + Index, HeaderOffset); +} + +uint32_t DWARFDebugAddrTable::getLength() const { + if (HeaderData.Length == 0) + return 0; + // TODO: DWARF64 support. + return HeaderData.Length + sizeof(uint32_t); +} + +uint32_t DWARFDebugAddrTable::getDataSize() const { + if (DataSize != 0) + return DataSize; + if (getLength() == 0) + return 0; + return getLength() - getHeaderSize(); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 000000000000..200b2d52a02b --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,111 @@ +//===- DWARFDebugArangeSet.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <cstring> + +using namespace llvm; + +void DWARFDebugArangeSet::Descriptor::dump(raw_ostream &OS, + uint32_t AddressSize) const { + OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, Address) + << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, + getEndAddress()); +} + +void DWARFDebugArangeSet::clear() { + Offset = -1ULL; + std::memset(&HeaderData, 0, sizeof(Header)); + ArangeDescriptors.clear(); +} + +bool +DWARFDebugArangeSet::extract(DataExtractor data, uint64_t *offset_ptr) { + if (data.isValidOffset(*offset_ptr)) { + ArangeDescriptors.clear(); + Offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header consisting of: a + // 4-byte length containing the length of the set of entries for this + // compilation unit, not including the length field itself; a 2-byte + // version identifier containing the value 2 for DWARF Version 2; a + // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer + // containing the size in bytes of an address (or the offset portion of + // an address for segmented addressing) on the target system; and a + // 1-byte unsigned integer containing the size in bytes of a segment + // descriptor on the target system. This header is followed by a series + // of tuples. Each tuple consists of an address and a length, each in + // the size appropriate for an address on the target architecture. + HeaderData.Length = data.getU32(offset_ptr); + HeaderData.Version = data.getU16(offset_ptr); + HeaderData.CuOffset = data.getU32(offset_ptr); + HeaderData.AddrSize = data.getU8(offset_ptr); + HeaderData.SegSize = data.getU8(offset_ptr); + + // Perform basic validation of the header fields. + if (!data.isValidOffsetForDataOfSize(Offset, HeaderData.Length) || + (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)) { + clear(); + return false; + } + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - Offset; + const uint32_t tuple_size = HeaderData.AddrSize * 2; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = Offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + static_assert(sizeof(arangeDescriptor.Address) == + sizeof(arangeDescriptor.Length), + "Different datatypes for addresses and sizes!"); + assert(sizeof(arangeDescriptor.Address) >= HeaderData.AddrSize); + + while (data.isValidOffset(*offset_ptr)) { + arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.Address || arangeDescriptor.Length) + ArangeDescriptors.push_back(arangeDescriptor); + else + break; // We are done if we get a zero address and length + } + + return !ArangeDescriptors.empty(); + } + return false; +} + +void DWARFDebugArangeSet::dump(raw_ostream &OS) const { + OS << format("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, ", + HeaderData.Length, HeaderData.Version) + << format("cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", + HeaderData.CuOffset, HeaderData.AddrSize, HeaderData.SegSize); + + for (const auto &Desc : ArangeDescriptors) { + Desc.dump(OS, HeaderData.AddrSize); + OS << '\n'; + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp new file mode 100644 index 000000000000..ca6043109cdb --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,122 @@ +//===- DWARFDebugAranges.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/WithColor.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <set> +#include <vector> + +using namespace llvm; + +void DWARFDebugAranges::extract(DataExtractor DebugArangesData) { + if (!DebugArangesData.isValidOffset(0)) + return; + uint64_t Offset = 0; + DWARFDebugArangeSet Set; + + while (Set.extract(DebugArangesData, &Offset)) { + uint64_t CUOffset = Set.getCompileUnitDIEOffset(); + for (const auto &Desc : Set.descriptors()) { + uint64_t LowPC = Desc.Address; + uint64_t HighPC = Desc.getEndAddress(); + appendRange(CUOffset, LowPC, HighPC); + } + ParsedCUOffsets.insert(CUOffset); + } +} + +void DWARFDebugAranges::generate(DWARFContext *CTX) { + clear(); + if (!CTX) + return; + + // Extract aranges from .debug_aranges section. + DataExtractor ArangesData(CTX->getDWARFObj().getArangesSection(), + CTX->isLittleEndian(), 0); + extract(ArangesData); + + // Generate aranges from DIEs: even if .debug_aranges section is present, + // it may describe only a small subset of compilation units, so we need to + // manually build aranges for the rest of them. + for (const auto &CU : CTX->compile_units()) { + uint64_t CUOffset = CU->getOffset(); + if (ParsedCUOffsets.insert(CUOffset).second) { + Expected<DWARFAddressRangesVector> CURanges = CU->collectAddressRanges(); + if (!CURanges) + WithColor::error() << toString(CURanges.takeError()) << '\n'; + else + for (const auto &R : *CURanges) + appendRange(CUOffset, R.LowPC, R.HighPC); + } + } + + construct(); +} + +void DWARFDebugAranges::clear() { + Endpoints.clear(); + Aranges.clear(); + ParsedCUOffsets.clear(); +} + +void DWARFDebugAranges::appendRange(uint64_t CUOffset, uint64_t LowPC, + uint64_t HighPC) { + if (LowPC >= HighPC) + return; + Endpoints.emplace_back(LowPC, CUOffset, true); + Endpoints.emplace_back(HighPC, CUOffset, false); +} + +void DWARFDebugAranges::construct() { + std::multiset<uint64_t> ValidCUs; // Maintain the set of CUs describing + // a current address range. + llvm::sort(Endpoints); + uint64_t PrevAddress = -1ULL; + for (const auto &E : Endpoints) { + if (PrevAddress < E.Address && !ValidCUs.empty()) { + // If the address range between two endpoints is described by some + // CU, first try to extend the last range in Aranges. If we can't + // do it, start a new range. + if (!Aranges.empty() && Aranges.back().HighPC() == PrevAddress && + ValidCUs.find(Aranges.back().CUOffset) != ValidCUs.end()) { + Aranges.back().setHighPC(E.Address); + } else { + Aranges.emplace_back(PrevAddress, E.Address, *ValidCUs.begin()); + } + } + // Update the set of valid CUs. + if (E.IsRangeStart) { + ValidCUs.insert(E.CUOffset); + } else { + auto CUPos = ValidCUs.find(E.CUOffset); + assert(CUPos != ValidCUs.end()); + ValidCUs.erase(CUPos); + } + PrevAddress = E.Address; + } + assert(ValidCUs.empty()); + + // Endpoints are not needed now. + Endpoints.clear(); + Endpoints.shrink_to_fit(); +} + +uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { + RangeCollIterator It = + partition_point(Aranges, [=](Range R) { return R.HighPC() <= Address; }); + if (It != Aranges.end() && It->LowPC <= Address) + return It->CUOffset; + return -1U; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp new file mode 100644 index 000000000000..81b00f65741b --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -0,0 +1,554 @@ +//===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <string> +#include <vector> + +using namespace llvm; +using namespace dwarf; + + +// See DWARF standard v3, section 7.23 +const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0; +const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; + +Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, + uint64_t EndOffset) { + while (*Offset < EndOffset) { + uint8_t Opcode = Data.getRelocatedValue(1, Offset); + // Some instructions have a primary opcode encoded in the top bits. + uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; + + if (Primary) { + // If it's a primary opcode, the first operand is encoded in the bottom + // bits of the opcode itself. + uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; + switch (Primary) { + default: + return createStringError(errc::illegal_byte_sequence, + "Invalid primary CFI opcode 0x%" PRIx8, + Primary); + case DW_CFA_advance_loc: + case DW_CFA_restore: + addInstruction(Primary, Op1); + break; + case DW_CFA_offset: + addInstruction(Primary, Op1, Data.getULEB128(Offset)); + break; + } + } else { + // Extended opcode - its value is Opcode itself. + switch (Opcode) { + default: + return createStringError(errc::illegal_byte_sequence, + "Invalid extended CFI opcode 0x%" PRIx8, + Opcode); + case DW_CFA_nop: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_GNU_window_save: + // No operands + addInstruction(Opcode); + break; + case DW_CFA_set_loc: + // Operands: Address + addInstruction(Opcode, Data.getRelocatedAddress(Offset)); + break; + case DW_CFA_advance_loc1: + // Operands: 1-byte delta + addInstruction(Opcode, Data.getRelocatedValue(1, Offset)); + break; + case DW_CFA_advance_loc2: + // Operands: 2-byte delta + addInstruction(Opcode, Data.getRelocatedValue(2, Offset)); + break; + case DW_CFA_advance_loc4: + // Operands: 4-byte delta + addInstruction(Opcode, Data.getRelocatedValue(4, Offset)); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_GNU_args_size: + // Operands: ULEB128 + addInstruction(Opcode, Data.getULEB128(Offset)); + break; + case DW_CFA_def_cfa_offset_sf: + // Operands: SLEB128 + addInstruction(Opcode, Data.getSLEB128(Offset)); + break; + case DW_CFA_offset_extended: + case DW_CFA_register: + case DW_CFA_def_cfa: + case DW_CFA_val_offset: { + // Operands: ULEB128, ULEB128 + // Note: We can not embed getULEB128 directly into function + // argument list. getULEB128 changes Offset and order of evaluation + // for arguments is unspecified. + auto op1 = Data.getULEB128(Offset); + auto op2 = Data.getULEB128(Offset); + addInstruction(Opcode, op1, op2); + break; + } + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_val_offset_sf: { + // Operands: ULEB128, SLEB128 + // Note: see comment for the previous case + auto op1 = Data.getULEB128(Offset); + auto op2 = (uint64_t)Data.getSLEB128(Offset); + addInstruction(Opcode, op1, op2); + break; + } + case DW_CFA_def_cfa_expression: { + uint32_t ExprLength = Data.getULEB128(Offset); + addInstruction(Opcode, 0); + DataExtractor Extractor( + Data.getData().slice(*Offset, *Offset + ExprLength), + Data.isLittleEndian(), Data.getAddressSize()); + Instructions.back().Expression = DWARFExpression( + Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); + *Offset += ExprLength; + break; + } + case DW_CFA_expression: + case DW_CFA_val_expression: { + auto RegNum = Data.getULEB128(Offset); + auto BlockLength = Data.getULEB128(Offset); + addInstruction(Opcode, RegNum, 0); + DataExtractor Extractor( + Data.getData().slice(*Offset, *Offset + BlockLength), + Data.isLittleEndian(), Data.getAddressSize()); + Instructions.back().Expression = DWARFExpression( + Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); + *Offset += BlockLength; + break; + } + } + } + } + + return Error::success(); +} + +namespace { + + +} // end anonymous namespace + +ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() { + static OperandType OpTypes[DW_CFA_restore+1][2]; + static bool Initialized = false; + if (Initialized) { + return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1); + } + Initialized = true; + +#define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \ + do { \ + OpTypes[OP][0] = OPTYPE0; \ + OpTypes[OP][1] = OPTYPE1; \ + } while (false) +#define DECLARE_OP1(OP, OPTYPE0) DECLARE_OP2(OP, OPTYPE0, OT_None) +#define DECLARE_OP0(OP) DECLARE_OP1(OP, OT_None) + + DECLARE_OP1(DW_CFA_set_loc, OT_Address); + DECLARE_OP1(DW_CFA_advance_loc, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc1, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc2, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_advance_loc4, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_MIPS_advance_loc8, OT_FactoredCodeOffset); + DECLARE_OP2(DW_CFA_def_cfa, OT_Register, OT_Offset); + DECLARE_OP2(DW_CFA_def_cfa_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP1(DW_CFA_def_cfa_register, OT_Register); + DECLARE_OP1(DW_CFA_def_cfa_offset, OT_Offset); + DECLARE_OP1(DW_CFA_def_cfa_offset_sf, OT_SignedFactDataOffset); + DECLARE_OP1(DW_CFA_def_cfa_expression, OT_Expression); + DECLARE_OP1(DW_CFA_undefined, OT_Register); + DECLARE_OP1(DW_CFA_same_value, OT_Register); + DECLARE_OP2(DW_CFA_offset, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_offset_extended, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_offset_extended_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP2(DW_CFA_val_offset, OT_Register, OT_UnsignedFactDataOffset); + DECLARE_OP2(DW_CFA_val_offset_sf, OT_Register, OT_SignedFactDataOffset); + DECLARE_OP2(DW_CFA_register, OT_Register, OT_Register); + DECLARE_OP2(DW_CFA_expression, OT_Register, OT_Expression); + DECLARE_OP2(DW_CFA_val_expression, OT_Register, OT_Expression); + DECLARE_OP1(DW_CFA_restore, OT_Register); + DECLARE_OP1(DW_CFA_restore_extended, OT_Register); + DECLARE_OP0(DW_CFA_remember_state); + DECLARE_OP0(DW_CFA_restore_state); + DECLARE_OP0(DW_CFA_GNU_window_save); + DECLARE_OP1(DW_CFA_GNU_args_size, OT_Offset); + DECLARE_OP0(DW_CFA_nop); + +#undef DECLARE_OP0 +#undef DECLARE_OP1 +#undef DECLARE_OP2 + + return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1); +} + +/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. +void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, + bool IsEH, const Instruction &Instr, + unsigned OperandIdx, uint64_t Operand) const { + assert(OperandIdx < 2); + uint8_t Opcode = Instr.Opcode; + OperandType Type = getOperandTypes()[Opcode][OperandIdx]; + + switch (Type) { + case OT_Unset: { + OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; + auto OpcodeName = CallFrameString(Opcode, Arch); + if (!OpcodeName.empty()) + OS << " " << OpcodeName; + else + OS << format(" Opcode %x", Opcode); + break; + } + case OT_None: + break; + case OT_Address: + OS << format(" %" PRIx64, Operand); + break; + case OT_Offset: + // The offsets are all encoded in a unsigned form, but in practice + // consumers use them signed. It's most certainly legacy due to + // the lack of signed variants in the first Dwarf standards. + OS << format(" %+" PRId64, int64_t(Operand)); + break; + case OT_FactoredCodeOffset: // Always Unsigned + if (CodeAlignmentFactor) + OS << format(" %" PRId64, Operand * CodeAlignmentFactor); + else + OS << format(" %" PRId64 "*code_alignment_factor" , Operand); + break; + case OT_SignedFactDataOffset: + if (DataAlignmentFactor) + OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor); + else + OS << format(" %" PRId64 "*data_alignment_factor" , int64_t(Operand)); + break; + case OT_UnsignedFactDataOffset: + if (DataAlignmentFactor) + OS << format(" %" PRId64, Operand * DataAlignmentFactor); + else + OS << format(" %" PRId64 "*data_alignment_factor" , Operand); + break; + case OT_Register: + OS << format(" reg%" PRId64, Operand); + break; + case OT_Expression: + assert(Instr.Expression && "missing DWARFExpression object"); + OS << " "; + Instr.Expression->print(OS, MRI, nullptr, IsEH); + break; + } +} + +void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, + unsigned IndentLevel) const { + for (const auto &Instr : Instructions) { + uint8_t Opcode = Instr.Opcode; + if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) + Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK; + OS.indent(2 * IndentLevel); + OS << CallFrameString(Opcode, Arch) << ":"; + for (unsigned i = 0; i < Instr.Ops.size(); ++i) + printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]); + OS << '\n'; + } +} + +void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { + OS << format("%08x %08x %08x CIE", (uint32_t)Offset, (uint32_t)Length, + DW_CIE_ID) + << "\n"; + OS << format(" Version: %d\n", Version); + OS << " Augmentation: \"" << Augmentation << "\"\n"; + if (Version >= 4) { + OS << format(" Address size: %u\n", (uint32_t)AddressSize); + OS << format(" Segment desc size: %u\n", + (uint32_t)SegmentDescriptorSize); + } + OS << format(" Code alignment factor: %u\n", (uint32_t)CodeAlignmentFactor); + OS << format(" Data alignment factor: %d\n", (int32_t)DataAlignmentFactor); + OS << format(" Return address column: %d\n", (int32_t)ReturnAddressRegister); + if (Personality) + OS << format(" Personality Address: %016" PRIx64 "\n", *Personality); + if (!AugmentationData.empty()) { + OS << " Augmentation data: "; + for (uint8_t Byte : AugmentationData) + OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf); + OS << "\n"; + } + OS << "\n"; + CFIs.dump(OS, MRI, IsEH); + OS << "\n"; +} + +void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { + OS << format("%08x %08x %08x FDE ", (uint32_t)Offset, (uint32_t)Length, + (int32_t)LinkedCIEOffset); + OS << format("cie=%08x pc=%08x...%08x\n", (int32_t)LinkedCIEOffset, + (uint32_t)InitialLocation, + (uint32_t)InitialLocation + (uint32_t)AddressRange); + if (LSDAAddress) + OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress); + CFIs.dump(OS, MRI, IsEH); + OS << "\n"; +} + +DWARFDebugFrame::DWARFDebugFrame(Triple::ArchType Arch, + bool IsEH, uint64_t EHFrameAddress) + : Arch(Arch), IsEH(IsEH), EHFrameAddress(EHFrameAddress) {} + +DWARFDebugFrame::~DWARFDebugFrame() = default; + +static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, + uint64_t Offset, int Length) { + errs() << "DUMP: "; + for (int i = 0; i < Length; ++i) { + uint8_t c = Data.getU8(&Offset); + errs().write_hex(c); errs() << " "; + } + errs() << "\n"; +} + +// This is a workaround for old compilers which do not allow +// noreturn attribute usage in lambdas. Once the support for those +// compilers are phased out, we can remove this and return back to +// a ReportError lambda: [StartOffset](const char *ErrorMsg). +static void LLVM_ATTRIBUTE_NORETURN ReportError(uint64_t StartOffset, + const char *ErrorMsg) { + std::string Str; + raw_string_ostream OS(Str); + OS << format(ErrorMsg, StartOffset); + OS.flush(); + report_fatal_error(Str); +} + +void DWARFDebugFrame::parse(DWARFDataExtractor Data) { + uint64_t Offset = 0; + DenseMap<uint64_t, CIE *> CIEs; + + while (Data.isValidOffset(Offset)) { + uint64_t StartOffset = Offset; + + bool IsDWARF64 = false; + uint64_t Length = Data.getRelocatedValue(4, &Offset); + uint64_t Id; + + if (Length == dwarf::DW_LENGTH_DWARF64) { + // DWARF-64 is distinguished by the first 32 bits of the initial length + // field being 0xffffffff. Then, the next 64 bits are the actual entry + // length. + IsDWARF64 = true; + Length = Data.getRelocatedValue(8, &Offset); + } + + // At this point, Offset points to the next field after Length. + // Length is the structure size excluding itself. Compute an offset one + // past the end of the structure (needed to know how many instructions to + // read). + uint64_t StartStructureOffset = Offset; + uint64_t EndStructureOffset = Offset + Length; + + // The Id field's size depends on the DWARF format + Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4); + bool IsCIE = + ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id)); + + if (IsCIE) { + uint8_t Version = Data.getU8(&Offset); + const char *Augmentation = Data.getCStr(&Offset); + StringRef AugmentationString(Augmentation ? Augmentation : ""); + uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : + Data.getU8(&Offset); + Data.setAddressSize(AddressSize); + uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(&Offset); + uint64_t CodeAlignmentFactor = Data.getULEB128(&Offset); + int64_t DataAlignmentFactor = Data.getSLEB128(&Offset); + uint64_t ReturnAddressRegister = + Version == 1 ? Data.getU8(&Offset) : Data.getULEB128(&Offset); + + // Parse the augmentation data for EH CIEs + StringRef AugmentationData(""); + uint32_t FDEPointerEncoding = DW_EH_PE_absptr; + uint32_t LSDAPointerEncoding = DW_EH_PE_omit; + Optional<uint64_t> Personality; + Optional<uint32_t> PersonalityEncoding; + if (IsEH) { + Optional<uint64_t> AugmentationLength; + uint64_t StartAugmentationOffset; + uint64_t EndAugmentationOffset; + + // Walk the augmentation string to get all the augmentation data. + for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { + switch (AugmentationString[i]) { + default: + ReportError( + StartOffset, + "Unknown augmentation character in entry at %" PRIx64); + case 'L': + LSDAPointerEncoding = Data.getU8(&Offset); + break; + case 'P': { + if (Personality) + ReportError(StartOffset, + "Duplicate personality in entry at %" PRIx64); + PersonalityEncoding = Data.getU8(&Offset); + Personality = Data.getEncodedPointer( + &Offset, *PersonalityEncoding, + EHFrameAddress ? EHFrameAddress + Offset : 0); + break; + } + case 'R': + FDEPointerEncoding = Data.getU8(&Offset); + break; + case 'S': + // Current frame is a signal trampoline. + break; + case 'z': + if (i) + ReportError(StartOffset, + "'z' must be the first character at %" PRIx64); + // Parse the augmentation length first. We only parse it if + // the string contains a 'z'. + AugmentationLength = Data.getULEB128(&Offset); + StartAugmentationOffset = Offset; + EndAugmentationOffset = Offset + *AugmentationLength; + break; + case 'B': + // B-Key is used for signing functions associated with this + // augmentation string + break; + } + } + + if (AugmentationLength.hasValue()) { + if (Offset != EndAugmentationOffset) + ReportError(StartOffset, + "Parsing augmentation data at %" PRIx64 " failed"); + + AugmentationData = Data.getData().slice(StartAugmentationOffset, + EndAugmentationOffset); + } + } + + auto Cie = std::make_unique<CIE>( + StartOffset, Length, Version, AugmentationString, AddressSize, + SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor, + ReturnAddressRegister, AugmentationData, FDEPointerEncoding, + LSDAPointerEncoding, Personality, PersonalityEncoding, Arch); + CIEs[StartOffset] = Cie.get(); + Entries.emplace_back(std::move(Cie)); + } else { + // FDE + uint64_t CIEPointer = Id; + uint64_t InitialLocation = 0; + uint64_t AddressRange = 0; + Optional<uint64_t> LSDAAddress; + CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer]; + + if (IsEH) { + // The address size is encoded in the CIE we reference. + if (!Cie) + ReportError(StartOffset, "Parsing FDE data at %" PRIx64 + " failed due to missing CIE"); + + if (auto Val = Data.getEncodedPointer( + &Offset, Cie->getFDEPointerEncoding(), + EHFrameAddress ? EHFrameAddress + Offset : 0)) { + InitialLocation = *Val; + } + if (auto Val = Data.getEncodedPointer( + &Offset, Cie->getFDEPointerEncoding(), 0)) { + AddressRange = *Val; + } + + StringRef AugmentationString = Cie->getAugmentationString(); + if (!AugmentationString.empty()) { + // Parse the augmentation length and data for this FDE. + uint64_t AugmentationLength = Data.getULEB128(&Offset); + + uint64_t EndAugmentationOffset = Offset + AugmentationLength; + + // Decode the LSDA if the CIE augmentation string said we should. + if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) { + LSDAAddress = Data.getEncodedPointer( + &Offset, Cie->getLSDAPointerEncoding(), + EHFrameAddress ? Offset + EHFrameAddress : 0); + } + + if (Offset != EndAugmentationOffset) + ReportError(StartOffset, + "Parsing augmentation data at %" PRIx64 " failed"); + } + } else { + InitialLocation = Data.getRelocatedAddress(&Offset); + AddressRange = Data.getRelocatedAddress(&Offset); + } + + Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, + InitialLocation, AddressRange, + Cie, LSDAAddress, Arch)); + } + + if (Error E = + Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) { + report_fatal_error(toString(std::move(E))); + } + + if (Offset != EndStructureOffset) + ReportError(StartOffset, + "Parsing entry instructions at %" PRIx64 " failed"); + } +} + +FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { + auto It = partition_point(Entries, [=](const std::unique_ptr<FrameEntry> &E) { + return E->getOffset() < Offset; + }); + if (It != Entries.end() && (*It)->getOffset() == Offset) + return It->get(); + return nullptr; +} + +void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, + Optional<uint64_t> Offset) const { + if (Offset) { + if (auto *Entry = getEntryAtOffset(*Offset)) + Entry->dump(OS, MRI, IsEH); + return; + } + + OS << "\n"; + for (const auto &Entry : Entries) + Entry->dump(OS, MRI, IsEH); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 000000000000..87eab34d58ee --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,69 @@ +//===- DWARFDebugInfoEntry.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, + uint64_t *OffsetPtr) { + DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); + const uint64_t UEndOffset = U.getNextUnitOffset(); + return extractFast(U, OffsetPtr, DebugInfoData, UEndOffset, 0); +} + +bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, uint64_t *OffsetPtr, + const DWARFDataExtractor &DebugInfoData, + uint64_t UEndOffset, uint32_t D) { + Offset = *OffsetPtr; + Depth = D; + if (Offset >= UEndOffset || !DebugInfoData.isValidOffset(Offset)) + return false; + uint64_t AbbrCode = DebugInfoData.getULEB128(OffsetPtr); + if (0 == AbbrCode) { + // NULL debug tag entry. + AbbrevDecl = nullptr; + return true; + } + AbbrevDecl = U.getAbbreviations()->getAbbreviationDeclaration(AbbrCode); + if (nullptr == AbbrevDecl) { + // Restore the original offset. + *OffsetPtr = Offset; + return false; + } + // See if all attributes in this DIE have fixed byte sizes. If so, we can + // just add this size to the offset to skip to the next DIE. + if (Optional<size_t> FixedSize = AbbrevDecl->getFixedAttributesByteSize(U)) { + *OffsetPtr += *FixedSize; + return true; + } + + // Skip all data in the .debug_info for the attributes + for (const auto &AttrSpec : AbbrevDecl->attributes()) { + // Check if this attribute has a fixed byte size. + if (auto FixedSize = AttrSpec.getByteSize(U)) { + // Attribute byte size if fixed, just add the size to the offset. + *OffsetPtr += *FixedSize; + } else if (!DWARFFormValue::skipValue(AttrSpec.Form, DebugInfoData, + OffsetPtr, U.getFormParams())) { + // We failed to skip this attribute's value, restore the original offset + // and return the failure status. + *OffsetPtr = Offset; + return false; + } + } + return true; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp new file mode 100644 index 000000000000..dbee28ff5ab1 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,1181 @@ +//===- DWARFDebugLine.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <cstdio> +#include <utility> + +using namespace llvm; +using namespace dwarf; + +using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; + +namespace { + +struct ContentDescriptor { + dwarf::LineNumberEntryFormat Type; + dwarf::Form Form; +}; + +using ContentDescriptors = SmallVector<ContentDescriptor, 4>; + +} // end anonmyous namespace + +void DWARFDebugLine::ContentTypeTracker::trackContentType( + dwarf::LineNumberEntryFormat ContentType) { + switch (ContentType) { + case dwarf::DW_LNCT_timestamp: + HasModTime = true; + break; + case dwarf::DW_LNCT_size: + HasLength = true; + break; + case dwarf::DW_LNCT_MD5: + HasMD5 = true; + break; + case dwarf::DW_LNCT_LLVM_source: + HasSource = true; + break; + default: + // We only care about values we consider optional, and new values may be + // added in the vendor extension range, so we do not match exhaustively. + break; + } +} + +DWARFDebugLine::Prologue::Prologue() { clear(); } + +bool DWARFDebugLine::Prologue::hasFileAtIndex(uint64_t FileIndex) const { + uint16_t DwarfVersion = getVersion(); + assert(DwarfVersion != 0 && + "line table prologue has no dwarf version information"); + if (DwarfVersion >= 5) + return FileIndex < FileNames.size(); + return FileIndex != 0 && FileIndex <= FileNames.size(); +} + +const llvm::DWARFDebugLine::FileNameEntry & +DWARFDebugLine::Prologue::getFileNameEntry(uint64_t Index) const { + uint16_t DwarfVersion = getVersion(); + assert(DwarfVersion != 0 && + "line table prologue has no dwarf version information"); + // In DWARF v5 the file names are 0-indexed. + if (DwarfVersion >= 5) + return FileNames[Index]; + return FileNames[Index - 1]; +} + +void DWARFDebugLine::Prologue::clear() { + TotalLength = PrologueLength = 0; + SegSelectorSize = 0; + MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0; + OpcodeBase = 0; + FormParams = dwarf::FormParams({0, 0, DWARF32}); + ContentTypes = ContentTypeTracker(); + StandardOpcodeLengths.clear(); + IncludeDirectories.clear(); + FileNames.clear(); +} + +void DWARFDebugLine::Prologue::dump(raw_ostream &OS, + DIDumpOptions DumpOptions) const { + OS << "Line table prologue:\n" + << format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength) + << format(" version: %u\n", getVersion()); + if (getVersion() >= 5) + OS << format(" address_size: %u\n", getAddressSize()) + << format(" seg_select_size: %u\n", SegSelectorSize); + OS << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength) + << format(" min_inst_length: %u\n", MinInstLength) + << format(getVersion() >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst) + << format(" default_is_stmt: %u\n", DefaultIsStmt) + << format(" line_base: %i\n", LineBase) + << format(" line_range: %u\n", LineRange) + << format(" opcode_base: %u\n", OpcodeBase); + + for (uint32_t I = 0; I != StandardOpcodeLengths.size(); ++I) + OS << format("standard_opcode_lengths[%s] = %u\n", + LNStandardString(I + 1).data(), StandardOpcodeLengths[I]); + + if (!IncludeDirectories.empty()) { + // DWARF v5 starts directory indexes at 0. + uint32_t DirBase = getVersion() >= 5 ? 0 : 1; + for (uint32_t I = 0; I != IncludeDirectories.size(); ++I) { + OS << format("include_directories[%3u] = ", I + DirBase); + IncludeDirectories[I].dump(OS, DumpOptions); + OS << '\n'; + } + } + + if (!FileNames.empty()) { + // DWARF v5 starts file indexes at 0. + uint32_t FileBase = getVersion() >= 5 ? 0 : 1; + for (uint32_t I = 0; I != FileNames.size(); ++I) { + const FileNameEntry &FileEntry = FileNames[I]; + OS << format("file_names[%3u]:\n", I + FileBase); + OS << " name: "; + FileEntry.Name.dump(OS, DumpOptions); + OS << '\n' + << format(" dir_index: %" PRIu64 "\n", FileEntry.DirIdx); + if (ContentTypes.HasMD5) + OS << " md5_checksum: " << FileEntry.Checksum.digest() << '\n'; + if (ContentTypes.HasModTime) + OS << format(" mod_time: 0x%8.8" PRIx64 "\n", FileEntry.ModTime); + if (ContentTypes.HasLength) + OS << format(" length: 0x%8.8" PRIx64 "\n", FileEntry.Length); + if (ContentTypes.HasSource) { + OS << " source: "; + FileEntry.Source.dump(OS, DumpOptions); + OS << '\n'; + } + } + } +} + +// Parse v2-v4 directory and file tables. +static void +parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, + uint64_t *OffsetPtr, uint64_t EndPrologueOffset, + DWARFDebugLine::ContentTypeTracker &ContentTypes, + std::vector<DWARFFormValue> &IncludeDirectories, + std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { + while (*OffsetPtr < EndPrologueOffset) { + StringRef S = DebugLineData.getCStrRef(OffsetPtr); + if (S.empty()) + break; + DWARFFormValue Dir = + DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, S.data()); + IncludeDirectories.push_back(Dir); + } + + while (*OffsetPtr < EndPrologueOffset) { + StringRef Name = DebugLineData.getCStrRef(OffsetPtr); + if (Name.empty()) + break; + DWARFDebugLine::FileNameEntry FileEntry; + FileEntry.Name = + DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name.data()); + FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); + FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); + FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileNames.push_back(FileEntry); + } + + ContentTypes.HasModTime = true; + ContentTypes.HasLength = true; +} + +// Parse v5 directory/file entry content descriptions. +// Returns the descriptors, or an error if we did not find a path or ran off +// the end of the prologue. +static llvm::Expected<ContentDescriptors> +parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, + uint64_t EndPrologueOffset, + DWARFDebugLine::ContentTypeTracker *ContentTypes) { + ContentDescriptors Descriptors; + int FormatCount = DebugLineData.getU8(OffsetPtr); + bool HasPath = false; + for (int I = 0; I != FormatCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return createStringError( + errc::invalid_argument, + "failed to parse entry content descriptions at offset " + "0x%8.8" PRIx64 + " because offset extends beyond the prologue end at offset " + "0x%8.8" PRIx64, + *OffsetPtr, EndPrologueOffset); + ContentDescriptor Descriptor; + Descriptor.Type = + dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); + Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); + if (Descriptor.Type == dwarf::DW_LNCT_path) + HasPath = true; + if (ContentTypes) + ContentTypes->trackContentType(Descriptor.Type); + Descriptors.push_back(Descriptor); + } + + if (!HasPath) + return createStringError(errc::invalid_argument, + "failed to parse entry content descriptions" + " because no path was found"); + return Descriptors; +} + +static Error +parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, + uint64_t *OffsetPtr, uint64_t EndPrologueOffset, + const dwarf::FormParams &FormParams, + const DWARFContext &Ctx, const DWARFUnit *U, + DWARFDebugLine::ContentTypeTracker &ContentTypes, + std::vector<DWARFFormValue> &IncludeDirectories, + std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { + // Get the directory entry description. + llvm::Expected<ContentDescriptors> DirDescriptors = + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); + if (!DirDescriptors) + return DirDescriptors.takeError(); + + // Get the directory entries, according to the format described above. + int DirEntryCount = DebugLineData.getU8(OffsetPtr); + for (int I = 0; I != DirEntryCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return createStringError( + errc::invalid_argument, + "failed to parse directory entry at offset " + "0x%8.8" PRIx64 + " because offset extends beyond the prologue end at offset " + "0x%8.8" PRIx64, + *OffsetPtr, EndPrologueOffset); + for (auto Descriptor : *DirDescriptors) { + DWARFFormValue Value(Descriptor.Form); + switch (Descriptor.Type) { + case DW_LNCT_path: + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) + return createStringError(errc::invalid_argument, + "failed to parse directory entry because " + "extracting the form value failed."); + IncludeDirectories.push_back(Value); + break; + default: + if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) + return createStringError(errc::invalid_argument, + "failed to parse directory entry because " + "skipping the form value failed."); + } + } + } + + // Get the file entry description. + llvm::Expected<ContentDescriptors> FileDescriptors = parseV5EntryFormat( + DebugLineData, OffsetPtr, EndPrologueOffset, &ContentTypes); + if (!FileDescriptors) + return FileDescriptors.takeError(); + + // Get the file entries, according to the format described above. + int FileEntryCount = DebugLineData.getU8(OffsetPtr); + for (int I = 0; I != FileEntryCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return createStringError( + errc::invalid_argument, + "failed to parse file entry at offset " + "0x%8.8" PRIx64 + " because offset extends beyond the prologue end at offset " + "0x%8.8" PRIx64, + *OffsetPtr, EndPrologueOffset); + DWARFDebugLine::FileNameEntry FileEntry; + for (auto Descriptor : *FileDescriptors) { + DWARFFormValue Value(Descriptor.Form); + if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) + return createStringError(errc::invalid_argument, + "failed to parse file entry because " + "extracting the form value failed."); + switch (Descriptor.Type) { + case DW_LNCT_path: + FileEntry.Name = Value; + break; + case DW_LNCT_LLVM_source: + FileEntry.Source = Value; + break; + case DW_LNCT_directory_index: + FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_timestamp: + FileEntry.ModTime = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_size: + FileEntry.Length = Value.getAsUnsignedConstant().getValue(); + break; + case DW_LNCT_MD5: + if (!Value.getAsBlock() || Value.getAsBlock().getValue().size() != 16) + return createStringError( + errc::invalid_argument, + "failed to parse file entry because the MD5 hash is invalid"); + std::uninitialized_copy_n(Value.getAsBlock().getValue().begin(), 16, + FileEntry.Checksum.Bytes.begin()); + break; + default: + break; + } + } + FileNames.push_back(FileEntry); + } + return Error::success(); +} + +Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, + uint64_t *OffsetPtr, + const DWARFContext &Ctx, + const DWARFUnit *U) { + const uint64_t PrologueOffset = *OffsetPtr; + + clear(); + TotalLength = DebugLineData.getRelocatedValue(4, OffsetPtr); + if (TotalLength == dwarf::DW_LENGTH_DWARF64) { + FormParams.Format = dwarf::DWARF64; + TotalLength = DebugLineData.getU64(OffsetPtr); + } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) { + return createStringError(errc::invalid_argument, + "parsing line table prologue at offset 0x%8.8" PRIx64 + " unsupported reserved unit length found of value 0x%8.8" PRIx64, + PrologueOffset, TotalLength); + } + FormParams.Version = DebugLineData.getU16(OffsetPtr); + if (getVersion() < 2) + return createStringError(errc::not_supported, + "parsing line table prologue at offset 0x%8.8" PRIx64 + " found unsupported version 0x%2.2" PRIx16, + PrologueOffset, getVersion()); + + if (getVersion() >= 5) { + FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); + assert((DebugLineData.getAddressSize() == 0 || + DebugLineData.getAddressSize() == getAddressSize()) && + "Line table header and data extractor disagree"); + SegSelectorSize = DebugLineData.getU8(OffsetPtr); + } + + PrologueLength = + DebugLineData.getRelocatedValue(sizeofPrologueLength(), OffsetPtr); + const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; + MinInstLength = DebugLineData.getU8(OffsetPtr); + if (getVersion() >= 4) + MaxOpsPerInst = DebugLineData.getU8(OffsetPtr); + DefaultIsStmt = DebugLineData.getU8(OffsetPtr); + LineBase = DebugLineData.getU8(OffsetPtr); + LineRange = DebugLineData.getU8(OffsetPtr); + OpcodeBase = DebugLineData.getU8(OffsetPtr); + + StandardOpcodeLengths.reserve(OpcodeBase - 1); + for (uint32_t I = 1; I < OpcodeBase; ++I) { + uint8_t OpLen = DebugLineData.getU8(OffsetPtr); + StandardOpcodeLengths.push_back(OpLen); + } + + if (getVersion() >= 5) { + if (Error e = parseV5DirFileTables( + DebugLineData, OffsetPtr, EndPrologueOffset, FormParams, Ctx, U, + ContentTypes, IncludeDirectories, FileNames)) { + return joinErrors( + createStringError( + errc::invalid_argument, + "parsing line table prologue at 0x%8.8" PRIx64 + " found an invalid directory or file table description at" + " 0x%8.8" PRIx64, + PrologueOffset, *OffsetPtr), + std::move(e)); + } + } else + parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, + ContentTypes, IncludeDirectories, FileNames); + + if (*OffsetPtr != EndPrologueOffset) + return createStringError(errc::invalid_argument, + "parsing line table prologue at 0x%8.8" PRIx64 + " should have ended at 0x%8.8" PRIx64 + " but it ended at 0x%8.8" PRIx64, + PrologueOffset, EndPrologueOffset, *OffsetPtr); + return Error::success(); +} + +DWARFDebugLine::Row::Row(bool DefaultIsStmt) { reset(DefaultIsStmt); } + +void DWARFDebugLine::Row::postAppend() { + Discriminator = 0; + BasicBlock = false; + PrologueEnd = false; + EpilogueBegin = false; +} + +void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { + Address.Address = 0; + Address.SectionIndex = object::SectionedAddress::UndefSection; + Line = 1; + Column = 0; + File = 1; + Isa = 0; + Discriminator = 0; + IsStmt = DefaultIsStmt; + BasicBlock = false; + EndSequence = false; + PrologueEnd = false; + EpilogueBegin = false; +} + +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { + OS << "Address Line Column File ISA Discriminator Flags\n" + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; +} + +void DWARFDebugLine::Row::dump(raw_ostream &OS) const { + OS << format("0x%16.16" PRIx64 " %6u %6u", Address.Address, Line, Column) + << format(" %6u %3u %13u ", File, Isa, Discriminator) + << (IsStmt ? " is_stmt" : "") << (BasicBlock ? " basic_block" : "") + << (PrologueEnd ? " prologue_end" : "") + << (EpilogueBegin ? " epilogue_begin" : "") + << (EndSequence ? " end_sequence" : "") << '\n'; +} + +DWARFDebugLine::Sequence::Sequence() { reset(); } + +void DWARFDebugLine::Sequence::reset() { + LowPC = 0; + HighPC = 0; + SectionIndex = object::SectionedAddress::UndefSection; + FirstRowIndex = 0; + LastRowIndex = 0; + Empty = true; +} + +DWARFDebugLine::LineTable::LineTable() { clear(); } + +void DWARFDebugLine::LineTable::dump(raw_ostream &OS, + DIDumpOptions DumpOptions) const { + Prologue.dump(OS, DumpOptions); + OS << '\n'; + + if (!Rows.empty()) { + Row::dumpTableHeader(OS); + for (const Row &R : Rows) { + R.dump(OS); + } + } +} + +void DWARFDebugLine::LineTable::clear() { + Prologue.clear(); + Rows.clear(); + Sequences.clear(); +} + +DWARFDebugLine::ParsingState::ParsingState(struct LineTable *LT) + : LineTable(LT) { + resetRowAndSequence(); +} + +void DWARFDebugLine::ParsingState::resetRowAndSequence() { + Row.reset(LineTable->Prologue.DefaultIsStmt); + Sequence.reset(); +} + +void DWARFDebugLine::ParsingState::appendRowToMatrix() { + unsigned RowNumber = LineTable->Rows.size(); + if (Sequence.Empty) { + // Record the beginning of instruction sequence. + Sequence.Empty = false; + Sequence.LowPC = Row.Address.Address; + Sequence.FirstRowIndex = RowNumber; + } + LineTable->appendRow(Row); + if (Row.EndSequence) { + // Record the end of instruction sequence. + Sequence.HighPC = Row.Address.Address; + Sequence.LastRowIndex = RowNumber + 1; + Sequence.SectionIndex = Row.Address.SectionIndex; + if (Sequence.isValid()) + LineTable->appendSequence(Sequence); + Sequence.reset(); + } + Row.postAppend(); +} + +const DWARFDebugLine::LineTable * +DWARFDebugLine::getLineTable(uint64_t Offset) const { + LineTableConstIter Pos = LineTableMap.find(Offset); + if (Pos != LineTableMap.end()) + return &Pos->second; + return nullptr; +} + +Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( + DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx, + const DWARFUnit *U, std::function<void(Error)> RecoverableErrorCallback) { + if (!DebugLineData.isValidOffset(Offset)) + return createStringError(errc::invalid_argument, "offset 0x%8.8" PRIx64 + " is not a valid debug line section offset", + Offset); + + std::pair<LineTableIter, bool> Pos = + LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable())); + LineTable *LT = &Pos.first->second; + if (Pos.second) { + if (Error Err = + LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorCallback)) + return std::move(Err); + return LT; + } + return LT; +} + +Error DWARFDebugLine::LineTable::parse( + DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, + const DWARFContext &Ctx, const DWARFUnit *U, + std::function<void(Error)> RecoverableErrorCallback, raw_ostream *OS) { + const uint64_t DebugLineOffset = *OffsetPtr; + + clear(); + + Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U); + + if (OS) { + // The presence of OS signals verbose dumping. + DIDumpOptions DumpOptions; + DumpOptions.Verbose = true; + Prologue.dump(*OS, DumpOptions); + } + + if (PrologueErr) + return PrologueErr; + + const uint64_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" PRIx64 ": ", *OffsetPtr); + + uint8_t Opcode = DebugLineData.getU8(OffsetPtr); + + if (OS) + *OS << format("%02.02" PRIx8 " ", Opcode); + + if (Opcode == 0) { + // Extended Opcodes always start with a zero opcode followed by + // a uleb128 length so you can skip ones you don't know about + uint64_t Len = DebugLineData.getULEB128(OffsetPtr); + uint64_t ExtOffset = *OffsetPtr; + + // Tolerate zero-length; assume length is correct and soldier on. + if (Len == 0) { + if (OS) + *OS << "Badly formed extended line op (length 0)\n"; + continue; + } + + uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); + if (OS) + *OS << LNExtendedString(SubOpcode); + switch (SubOpcode) { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the + // state-machine registers. Then reset the registers to the initial + // values specified above. Every statement program sequence must end + // with a DW_LNE_end_sequence instruction which creates a row whose + // address is that of the byte after the last target machine instruction + // of the sequence. + State.Row.EndSequence = true; + State.appendRowToMatrix(); + if (OS) { + *OS << "\n"; + OS->indent(12); + State.Row.dump(*OS); + } + State.resetRowAndSequence(); + break; + + case DW_LNE_set_address: + // Takes a single relocatable address as an operand. The size of the + // operand is the size appropriate to hold an address on the target + // machine. Set the address register to the value given by the + // relocatable address. All of the other statement program opcodes + // that affect the address register add a delta to it. This instruction + // stores a relocatable value into it instead. + // + // Make sure the extractor knows the address size. If not, infer it + // from the size of the operand. + if (DebugLineData.getAddressSize() == 0) + DebugLineData.setAddressSize(Len - 1); + else if (DebugLineData.getAddressSize() != Len - 1) { + return createStringError(errc::invalid_argument, + "mismatching address size at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, + ExtOffset, DebugLineData.getAddressSize(), + Len - 1); + } + State.Row.Address.Address = DebugLineData.getRelocatedAddress( + OffsetPtr, &State.Row.Address.SectionIndex); + if (OS) + *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); + break; + + case DW_LNE_define_file: + // Takes 4 arguments. The first is a null terminated string containing + // a source file name. The second is an unsigned LEB128 number + // representing the directory index of the directory in which the file + // was found. The third is an unsigned LEB128 number representing the + // time of last modification of the file. The fourth is an unsigned + // LEB128 number representing the length in bytes of the file. The time + // and length fields may contain LEB128(0) if the information is not + // available. + // + // The directory index represents an entry in the include_directories + // section of the statement program prologue. The index is LEB128(0) + // if the file was found in the current directory of the compilation, + // LEB128(1) if it was found in the first directory in the + // include_directories section, and so on. The directory index is + // ignored for file names that represent full path names. + // + // The files are numbered, starting at 1, in the order in which they + // appear; the names in the prologue come before names defined by + // the DW_LNE_define_file instruction. These numbers are used in the + // the file register of the state machine. + { + FileNameEntry FileEntry; + const char *Name = DebugLineData.getCStr(OffsetPtr); + FileEntry.Name = + DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name); + FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); + FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); + FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + Prologue.FileNames.push_back(FileEntry); + if (OS) + *OS << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" + << format("(0x%16.16" PRIx64 ")", FileEntry.ModTime) + << ", length=" << FileEntry.Length << ")"; + } + break; + + case DW_LNE_set_discriminator: + State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Discriminator << ")"; + break; + + default: + if (OS) + *OS << format("Unrecognized extended op 0x%02.02" PRIx8, SubOpcode) + << format(" length %" PRIx64, Len); + // Len doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that. + (*OffsetPtr) += Len - 1; + break; + } + // Make sure the stated and parsed lengths are the same. + // Otherwise we have an unparseable line-number program. + if (*OffsetPtr - ExtOffset != Len) + return createStringError(errc::illegal_byte_sequence, + "unexpected line op length at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, + ExtOffset, Len, *OffsetPtr - ExtOffset); + } else if (Opcode < Prologue.OpcodeBase) { + if (OS) + *OS << LNStandardString(Opcode); + switch (Opcode) { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the + // current values of the state-machine registers. + if (OS) { + *OS << "\n"; + OS->indent(12); + State.Row.dump(*OS); + *OS << "\n"; + } + State.appendRowToMatrix(); + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the + // result to the address register of the state machine. + { + uint64_t AddrOffset = + DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; + State.Row.Address.Address += AddrOffset; + if (OS) + *OS << " (" << AddrOffset << ")"; + } + break; + + case DW_LNS_advance_line: + // Takes a single signed LEB128 operand and adds that value to + // the line register of the state machine. + State.Row.Line += DebugLineData.getSLEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Line << ")"; + break; + + case DW_LNS_set_file: + // Takes a single unsigned LEB128 operand and stores it in the file + // register of the state machine. + State.Row.File = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.File << ")"; + break; + + case DW_LNS_set_column: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + State.Row.Column = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Column << ")"; + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state + // machine to the logical negation of its current value. + State.Row.IsStmt = !State.Row.IsStmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the + // state machine to true + State.Row.BasicBlock = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state + // machine the address increment value corresponding to special + // opcode 255. The motivation for DW_LNS_const_add_pc is this: + // when the statement program needs to advance the address by a + // small amount, it can use a single special opcode, which occupies + // a single byte. When it needs to advance the address by up to + // twice the range of the last special opcode, it can use + // DW_LNS_const_add_pc followed by a special opcode, for a total + // of two bytes. Only if it needs to advance the address by more + // than twice that range will it need to use both DW_LNS_advance_pc + // and a special opcode, requiring three or more bytes. + { + uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; + uint64_t AddrOffset = + (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; + State.Row.Address.Address += AddrOffset; + if (OS) + *OS + << format(" (0x%16.16" PRIx64 ")", AddrOffset); + } + break; + + case DW_LNS_fixed_advance_pc: + // Takes a single uhalf operand. Add to the address register of + // the state machine the value of the (unencoded) operand. This + // is the only extended opcode that takes an argument that is not + // a variable length number. The motivation for DW_LNS_fixed_advance_pc + // is this: existing assemblers cannot emit DW_LNS_advance_pc or + // special opcodes because they cannot encode LEB128 numbers or + // judge when the computation of a special opcode overflows and + // requires the use of DW_LNS_advance_pc. Such assemblers, however, + // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. + { + uint16_t PCOffset = DebugLineData.getRelocatedValue(2, OffsetPtr); + State.Row.Address.Address += PCOffset; + if (OS) + *OS + << format(" (0x%4.4" PRIx16 ")", PCOffset); + } + break; + + case DW_LNS_set_prologue_end: + // Takes no arguments. Set the prologue_end register of the + // state machine to true + State.Row.PrologueEnd = true; + break; + + case DW_LNS_set_epilogue_begin: + // Takes no arguments. Set the basic_block register of the + // state machine to true + State.Row.EpilogueBegin = true; + break; + + case DW_LNS_set_isa: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << " (" << State.Row.Isa << ")"; + break; + + default: + // Handle any unknown standard opcodes here. We know the lengths + // of such opcodes because they are specified in the prologue + // as a multiple of LEB128 operands for each opcode. + { + assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); + uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; + for (uint8_t I = 0; I < OpcodeLength; ++I) { + uint64_t Value = DebugLineData.getULEB128(OffsetPtr); + if (OS) + *OS << format("Skipping ULEB128 value: 0x%16.16" PRIx64 ")\n", + Value); + } + } + break; + } + } else { + // Special Opcodes + + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The "address + // advance" is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + + // (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * + // minimum_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; + uint64_t AddrOffset = + (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; + int32_t LineOffset = + Prologue.LineBase + (AdjustOpcode % Prologue.LineRange); + State.Row.Line += LineOffset; + State.Row.Address.Address += AddrOffset; + + if (OS) { + *OS << "address += " << AddrOffset << ", line += " << LineOffset + << "\n"; + OS->indent(12); + State.Row.dump(*OS); + } + + State.appendRowToMatrix(); + } + if(OS) + *OS << "\n"; + } + + if (!State.Sequence.Empty) + RecoverableErrorCallback( + createStringError(errc::illegal_byte_sequence, + "last sequence in debug line table is not terminated!")); + + // Sort all sequences so that address lookup will work faster. + if (!Sequences.empty()) { + llvm::sort(Sequences, Sequence::orderByHighPC); + // Note: actually, instruction address ranges of sequences should not + // overlap (in shared objects and executables). If they do, the address + // lookup would still work, though, but result would be ambiguous. + // We don't report warning in this case. For example, + // sometimes .so compiled from multiple object files contains a few + // rudimentary sequences for address ranges [0x0, 0xsomething). + } + + return Error::success(); +} + +uint32_t DWARFDebugLine::LineTable::findRowInSeq( + const DWARFDebugLine::Sequence &Seq, + object::SectionedAddress Address) const { + if (!Seq.containsPC(Address)) + return UnknownRowIndex; + assert(Seq.SectionIndex == Address.SectionIndex); + // In some cases, e.g. first instruction in a function, the compiler generates + // two entries, both with the same address. We want the last one. + // + // In general we want a non-empty range: the last row whose address is less + // than or equal to Address. This can be computed as upper_bound - 1. + DWARFDebugLine::Row Row; + Row.Address = Address; + RowIter FirstRow = Rows.begin() + Seq.FirstRowIndex; + RowIter LastRow = Rows.begin() + Seq.LastRowIndex; + assert(FirstRow->Address.Address <= Row.Address.Address && + Row.Address.Address < LastRow[-1].Address.Address); + RowIter RowPos = std::upper_bound(FirstRow + 1, LastRow - 1, Row, + DWARFDebugLine::Row::orderByAddress) - + 1; + assert(Seq.SectionIndex == RowPos->Address.SectionIndex); + return RowPos - Rows.begin(); +} + +uint32_t DWARFDebugLine::LineTable::lookupAddress( + object::SectionedAddress Address) const { + + // Search for relocatable addresses + uint32_t Result = lookupAddressImpl(Address); + + if (Result != UnknownRowIndex || + Address.SectionIndex == object::SectionedAddress::UndefSection) + return Result; + + // Search for absolute addresses + Address.SectionIndex = object::SectionedAddress::UndefSection; + return lookupAddressImpl(Address); +} + +uint32_t DWARFDebugLine::LineTable::lookupAddressImpl( + object::SectionedAddress Address) const { + // First, find an instruction sequence containing the given address. + DWARFDebugLine::Sequence Sequence; + Sequence.SectionIndex = Address.SectionIndex; + Sequence.HighPC = Address.Address; + SequenceIter It = llvm::upper_bound(Sequences, Sequence, + DWARFDebugLine::Sequence::orderByHighPC); + if (It == Sequences.end() || It->SectionIndex != Address.SectionIndex) + return UnknownRowIndex; + return findRowInSeq(*It, Address); +} + +bool DWARFDebugLine::LineTable::lookupAddressRange( + object::SectionedAddress Address, uint64_t Size, + std::vector<uint32_t> &Result) const { + + // Search for relocatable addresses + if (lookupAddressRangeImpl(Address, Size, Result)) + return true; + + if (Address.SectionIndex == object::SectionedAddress::UndefSection) + return false; + + // Search for absolute addresses + Address.SectionIndex = object::SectionedAddress::UndefSection; + return lookupAddressRangeImpl(Address, Size, Result); +} + +bool DWARFDebugLine::LineTable::lookupAddressRangeImpl( + object::SectionedAddress Address, uint64_t Size, + std::vector<uint32_t> &Result) const { + if (Sequences.empty()) + return false; + uint64_t EndAddr = Address.Address + Size; + // First, find an instruction sequence containing the given address. + DWARFDebugLine::Sequence Sequence; + Sequence.SectionIndex = Address.SectionIndex; + Sequence.HighPC = Address.Address; + SequenceIter LastSeq = Sequences.end(); + SequenceIter SeqPos = llvm::upper_bound( + Sequences, Sequence, DWARFDebugLine::Sequence::orderByHighPC); + if (SeqPos == LastSeq || !SeqPos->containsPC(Address)) + return false; + + SequenceIter StartPos = SeqPos; + + // Add the rows from the first sequence to the vector, starting with the + // index we just calculated + + while (SeqPos != LastSeq && SeqPos->LowPC < EndAddr) { + const DWARFDebugLine::Sequence &CurSeq = *SeqPos; + // For the first sequence, we need to find which row in the sequence is the + // first in our range. + uint32_t FirstRowIndex = CurSeq.FirstRowIndex; + if (SeqPos == StartPos) + FirstRowIndex = findRowInSeq(CurSeq, Address); + + // Figure out the last row in the range. + uint32_t LastRowIndex = + findRowInSeq(CurSeq, {EndAddr - 1, Address.SectionIndex}); + if (LastRowIndex == UnknownRowIndex) + LastRowIndex = CurSeq.LastRowIndex - 1; + + assert(FirstRowIndex != UnknownRowIndex); + assert(LastRowIndex != UnknownRowIndex); + + for (uint32_t I = FirstRowIndex; I <= LastRowIndex; ++I) { + Result.push_back(I); + } + + ++SeqPos; + } + + return true; +} + +Optional<StringRef> DWARFDebugLine::LineTable::getSourceByIndex(uint64_t FileIndex, + FileLineInfoKind Kind) const { + if (Kind == FileLineInfoKind::None || !Prologue.hasFileAtIndex(FileIndex)) + return None; + const FileNameEntry &Entry = Prologue.getFileNameEntry(FileIndex); + if (Optional<const char *> source = Entry.Source.getAsCString()) + return StringRef(*source); + return None; +} + +static bool isPathAbsoluteOnWindowsOrPosix(const Twine &Path) { + // Debug info can contain paths from any OS, not necessarily + // an OS we're currently running on. Moreover different compilation units can + // be compiled on different operating systems and linked together later. + return sys::path::is_absolute(Path, sys::path::Style::posix) || + sys::path::is_absolute(Path, sys::path::Style::windows); +} + +bool DWARFDebugLine::Prologue::getFileNameByIndex( + uint64_t FileIndex, StringRef CompDir, FileLineInfoKind Kind, + std::string &Result, sys::path::Style Style) const { + if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) + return false; + const FileNameEntry &Entry = getFileNameEntry(FileIndex); + StringRef FileName = Entry.Name.getAsCString().getValue(); + if (Kind != FileLineInfoKind::AbsoluteFilePath || + isPathAbsoluteOnWindowsOrPosix(FileName)) { + Result = FileName; + return true; + } + + SmallString<16> FilePath; + StringRef IncludeDir; + // Be defensive about the contents of Entry. + if (getVersion() >= 5) { + if (Entry.DirIdx < IncludeDirectories.size()) + IncludeDir = IncludeDirectories[Entry.DirIdx].getAsCString().getValue(); + } else { + if (0 < Entry.DirIdx && Entry.DirIdx <= IncludeDirectories.size()) + IncludeDir = + IncludeDirectories[Entry.DirIdx - 1].getAsCString().getValue(); + + // We may still need to append compilation directory of compile unit. + // We know that FileName is not absolute, the only way to have an + // absolute path at this point would be if IncludeDir is absolute. + if (!CompDir.empty() && !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) + sys::path::append(FilePath, Style, CompDir); + } + + // sys::path::append skips empty strings. + sys::path::append(FilePath, Style, IncludeDir, FileName); + Result = FilePath.str(); + return true; +} + +bool DWARFDebugLine::LineTable::getFileLineInfoForAddress( + object::SectionedAddress Address, const char *CompDir, + FileLineInfoKind Kind, DILineInfo &Result) const { + // Get the index of row we're looking for in the line table. + uint32_t RowIndex = lookupAddress(Address); + if (RowIndex == -1U) + return false; + // Take file number and line/column from the row. + const auto &Row = Rows[RowIndex]; + if (!getFileNameByIndex(Row.File, CompDir, Kind, Result.FileName)) + return false; + Result.Line = Row.Line; + Result.Column = Row.Column; + Result.Discriminator = Row.Discriminator; + Result.Source = getSourceByIndex(Row.File, Kind); + return true; +} + +// We want to supply the Unit associated with a .debug_line[.dwo] table when +// we dump it, if possible, but still dump the table even if there isn't a Unit. +// Therefore, collect up handles on all the Units that point into the +// line-table section. +static DWARFDebugLine::SectionParser::LineToUnitMap +buildLineToUnitMap(DWARFDebugLine::SectionParser::cu_range CUs, + DWARFDebugLine::SectionParser::tu_range TUs) { + DWARFDebugLine::SectionParser::LineToUnitMap LineToUnit; + for (const auto &CU : CUs) + if (auto CUDIE = CU->getUnitDIE()) + if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) + LineToUnit.insert(std::make_pair(*StmtOffset, &*CU)); + for (const auto &TU : TUs) + if (auto TUDIE = TU->getUnitDIE()) + if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list))) + LineToUnit.insert(std::make_pair(*StmtOffset, &*TU)); + return LineToUnit; +} + +DWARFDebugLine::SectionParser::SectionParser(DWARFDataExtractor &Data, + const DWARFContext &C, + cu_range CUs, tu_range TUs) + : DebugLineData(Data), Context(C) { + LineToUnit = buildLineToUnitMap(CUs, TUs); + if (!DebugLineData.isValidOffset(Offset)) + Done = true; +} + +bool DWARFDebugLine::Prologue::totalLengthIsValid() const { + return TotalLength == dwarf::DW_LENGTH_DWARF64 || + TotalLength < dwarf::DW_LENGTH_lo_reserved; +} + +DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext( + function_ref<void(Error)> RecoverableErrorCallback, + function_ref<void(Error)> UnrecoverableErrorCallback, raw_ostream *OS) { + assert(DebugLineData.isValidOffset(Offset) && + "parsing should have terminated"); + DWARFUnit *U = prepareToParse(Offset); + uint64_t OldOffset = Offset; + LineTable LT; + if (Error Err = LT.parse(DebugLineData, &Offset, Context, U, + RecoverableErrorCallback, OS)) + UnrecoverableErrorCallback(std::move(Err)); + moveToNextTable(OldOffset, LT.Prologue); + return LT; +} + +void DWARFDebugLine::SectionParser::skip( + function_ref<void(Error)> ErrorCallback) { + assert(DebugLineData.isValidOffset(Offset) && + "parsing should have terminated"); + DWARFUnit *U = prepareToParse(Offset); + uint64_t OldOffset = Offset; + LineTable LT; + if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U)) + ErrorCallback(std::move(Err)); + moveToNextTable(OldOffset, LT.Prologue); +} + +DWARFUnit *DWARFDebugLine::SectionParser::prepareToParse(uint64_t Offset) { + DWARFUnit *U = nullptr; + auto It = LineToUnit.find(Offset); + if (It != LineToUnit.end()) + U = It->second; + DebugLineData.setAddressSize(U ? U->getAddressByteSize() : 0); + return U; +} + +void DWARFDebugLine::SectionParser::moveToNextTable(uint64_t OldOffset, + const Prologue &P) { + // If the length field is not valid, we don't know where the next table is, so + // cannot continue to parse. Mark the parser as done, and leave the Offset + // value as it currently is. This will be the end of the bad length field. + if (!P.totalLengthIsValid()) { + Done = true; + return; + } + + Offset = OldOffset + P.TotalLength + P.sizeofTotalLength(); + if (!DebugLineData.isValidOffset(Offset)) { + Done = true; + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp new file mode 100644 index 000000000000..4f7b01130a47 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -0,0 +1,339 @@ +//===- DWARFDebugLoc.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +// When directly dumping the .debug_loc without a compile unit, we have to guess +// at the DWARF version. This only affects DW_OP_call_ref, which is a rare +// expression that LLVM doesn't produce. Guessing the wrong version means we +// won't be able to pretty print expressions in DWARF2 binaries produced by +// non-LLVM tools. +static void dumpExpression(raw_ostream &OS, ArrayRef<uint8_t> Data, + bool IsLittleEndian, unsigned AddressSize, + const MCRegisterInfo *MRI, DWARFUnit *U) { + DWARFDataExtractor Extractor(toStringRef(Data), IsLittleEndian, AddressSize); + DWARFExpression(Extractor, dwarf::DWARF_VERSION, AddressSize).print(OS, MRI, U); +} + +void DWARFDebugLoc::LocationList::dump(raw_ostream &OS, uint64_t BaseAddress, + bool IsLittleEndian, + unsigned AddressSize, + const MCRegisterInfo *MRI, DWARFUnit *U, + DIDumpOptions DumpOpts, + unsigned Indent) const { + for (const Entry &E : Entries) { + OS << '\n'; + OS.indent(Indent); + OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, + BaseAddress + E.Begin); + OS << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, + BaseAddress + E.End); + OS << ": "; + + dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI, U); + } +} + +DWARFDebugLoc::LocationList const * +DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const { + auto It = partition_point( + Locations, [=](const LocationList &L) { return L.Offset < Offset; }); + if (It != Locations.end() && It->Offset == Offset) + return &(*It); + return nullptr; +} + +void DWARFDebugLoc::dump(raw_ostream &OS, const MCRegisterInfo *MRI, DIDumpOptions DumpOpts, + Optional<uint64_t> Offset) const { + auto DumpLocationList = [&](const LocationList &L) { + OS << format("0x%8.8" PRIx64 ": ", L.Offset); + L.dump(OS, 0, IsLittleEndian, AddressSize, MRI, nullptr, DumpOpts, 12); + OS << "\n"; + }; + + if (Offset) { + if (auto *L = getLocationListAtOffset(*Offset)) + DumpLocationList(*L); + return; + } + + for (const LocationList &L : Locations) { + DumpLocationList(L); + if (&L != &Locations.back()) + OS << '\n'; + } +} + +Expected<DWARFDebugLoc::LocationList> +DWARFDebugLoc::parseOneLocationList(const DWARFDataExtractor &Data, + uint64_t *Offset) { + LocationList LL; + LL.Offset = *Offset; + AddressSize = Data.getAddressSize(); + DataExtractor::Cursor C(*Offset); + + // 2.6.2 Location Lists + // A location list entry consists of: + while (true) { + Entry E; + + // 1. A beginning address offset. ... + E.Begin = Data.getRelocatedAddress(C); + + // 2. An ending address offset. ... + E.End = Data.getRelocatedAddress(C); + + if (Error Err = C.takeError()) + return std::move(Err); + + // 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) { + *Offset = C.tell(); + return LL; + } + + if (E.Begin != (AddressSize == 4 ? -1U : -1ULL)) { + unsigned Bytes = Data.getU16(C); + // A single location description describing the location of the object... + Data.getU8(C, E.Loc, Bytes); + } + + LL.Entries.push_back(std::move(E)); + } +} + +void DWARFDebugLoc::parse(const DWARFDataExtractor &data) { + IsLittleEndian = data.isLittleEndian(); + AddressSize = data.getAddressSize(); + + uint64_t Offset = 0; + while (Offset < data.getData().size()) { + if (auto LL = parseOneLocationList(data, &Offset)) + Locations.push_back(std::move(*LL)); + else { + logAllUnhandledErrors(LL.takeError(), WithColor::error()); + break; + } + } +} + +Expected<DWARFDebugLoclists::LocationList> +DWARFDebugLoclists::parseOneLocationList(const DataExtractor &Data, + uint64_t *Offset, unsigned Version) { + LocationList LL; + LL.Offset = *Offset; + DataExtractor::Cursor C(*Offset); + + // dwarf::DW_LLE_end_of_list_entry is 0 and indicates the end of the list. + while (auto Kind = Data.getU8(C)) { + Entry E; + E.Kind = Kind; + E.Offset = C.tell() - 1; + switch (Kind) { + case dwarf::DW_LLE_base_addressx: + E.Value0 = Data.getULEB128(C); + break; + case dwarf::DW_LLE_startx_length: + E.Value0 = Data.getULEB128(C); + // Pre-DWARF 5 has different interpretation of the length field. We have + // to support both pre- and standartized styles for the compatibility. + if (Version < 5) + E.Value1 = Data.getU32(C); + else + E.Value1 = Data.getULEB128(C); + break; + case dwarf::DW_LLE_start_length: + E.Value0 = Data.getAddress(C); + E.Value1 = Data.getULEB128(C); + break; + case dwarf::DW_LLE_offset_pair: + E.Value0 = Data.getULEB128(C); + E.Value1 = Data.getULEB128(C); + break; + case dwarf::DW_LLE_base_address: + E.Value0 = Data.getAddress(C); + break; + default: + cantFail(C.takeError()); + return createStringError(errc::illegal_byte_sequence, + "LLE of kind %x not supported", (int)Kind); + } + + if (Kind != dwarf::DW_LLE_base_address && + Kind != dwarf::DW_LLE_base_addressx) { + unsigned Bytes = Version >= 5 ? Data.getULEB128(C) : Data.getU16(C); + // A single location description describing the location of the object... + Data.getU8(C, E.Loc, Bytes); + } + + LL.Entries.push_back(std::move(E)); + } + if (Error Err = C.takeError()) + return std::move(Err); + Entry E; + E.Kind = dwarf::DW_LLE_end_of_list; + E.Offset = C.tell() - 1; + LL.Entries.push_back(E); + *Offset = C.tell(); + return LL; +} + +void DWARFDebugLoclists::parse(DataExtractor data, uint64_t Offset, uint64_t EndOffset, uint16_t Version) { + IsLittleEndian = data.isLittleEndian(); + AddressSize = data.getAddressSize(); + + while (Offset < EndOffset) { + if (auto LL = parseOneLocationList(data, &Offset, Version)) + Locations.push_back(std::move(*LL)); + else { + logAllUnhandledErrors(LL.takeError(), WithColor::error()); + return; + } + } +} + +DWARFDebugLoclists::LocationList const * +DWARFDebugLoclists::getLocationListAtOffset(uint64_t Offset) const { + auto It = partition_point( + Locations, [=](const LocationList &L) { return L.Offset < Offset; }); + if (It != Locations.end() && It->Offset == Offset) + return &(*It); + return nullptr; +} + +void DWARFDebugLoclists::Entry::dump(raw_ostream &OS, uint64_t &BaseAddr, + bool IsLittleEndian, unsigned AddressSize, + const MCRegisterInfo *MRI, DWARFUnit *U, + DIDumpOptions DumpOpts, unsigned Indent, + size_t MaxEncodingStringLength) const { + if (DumpOpts.Verbose) { + OS << "\n"; + OS.indent(Indent); + auto EncodingString = dwarf::LocListEncodingString(Kind); + // Unsupported encodings should have been reported during parsing. + assert(!EncodingString.empty() && "Unknown loclist entry encoding"); + OS << format("%s%*c", EncodingString.data(), + MaxEncodingStringLength - EncodingString.size() + 1, '('); + switch (Kind) { + case dwarf::DW_LLE_startx_length: + case dwarf::DW_LLE_start_length: + case dwarf::DW_LLE_offset_pair: + OS << format("0x%*.*" PRIx64 ", 0x%*.*" PRIx64, AddressSize * 2, + AddressSize * 2, Value0, AddressSize * 2, AddressSize * 2, + Value1); + break; + case dwarf::DW_LLE_base_addressx: + case dwarf::DW_LLE_base_address: + OS << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, + Value0); + break; + case dwarf::DW_LLE_end_of_list: + break; + } + OS << ')'; + } + auto PrintPrefix = [&] { + OS << "\n"; + OS.indent(Indent); + if (DumpOpts.Verbose) + OS << format("%*s", MaxEncodingStringLength, (const char *)"=> "); + }; + switch (Kind) { + case dwarf::DW_LLE_startx_length: + PrintPrefix(); + OS << "Addr idx " << Value0 << " (w/ length " << Value1 << "): "; + break; + case dwarf::DW_LLE_start_length: + PrintPrefix(); + DWARFAddressRange(Value0, Value0 + Value1) + .dump(OS, AddressSize, DumpOpts); + OS << ": "; + break; + case dwarf::DW_LLE_offset_pair: + PrintPrefix(); + DWARFAddressRange(BaseAddr + Value0, BaseAddr + Value1) + .dump(OS, AddressSize, DumpOpts); + OS << ": "; + break; + case dwarf::DW_LLE_base_addressx: + if (!DumpOpts.Verbose) + return; + break; + case dwarf::DW_LLE_end_of_list: + if (!DumpOpts.Verbose) + return; + break; + case dwarf::DW_LLE_base_address: + BaseAddr = Value0; + if (!DumpOpts.Verbose) + return; + break; + default: + llvm_unreachable("unreachable locations list kind"); + } + + dumpExpression(OS, Loc, IsLittleEndian, AddressSize, MRI, U); +} +void DWARFDebugLoclists::LocationList::dump(raw_ostream &OS, uint64_t BaseAddr, + bool IsLittleEndian, + unsigned AddressSize, + const MCRegisterInfo *MRI, + DWARFUnit *U, + DIDumpOptions DumpOpts, + unsigned Indent) const { + size_t MaxEncodingStringLength = 0; + if (DumpOpts.Verbose) + for (const auto &Entry : Entries) + MaxEncodingStringLength = + std::max(MaxEncodingStringLength, + dwarf::LocListEncodingString(Entry.Kind).size()); + + for (const Entry &E : Entries) + E.dump(OS, BaseAddr, IsLittleEndian, AddressSize, MRI, U, DumpOpts, Indent, + MaxEncodingStringLength); +} + +void DWARFDebugLoclists::dump(raw_ostream &OS, uint64_t BaseAddr, + const MCRegisterInfo *MRI, DIDumpOptions DumpOpts, + Optional<uint64_t> Offset) const { + auto DumpLocationList = [&](const LocationList &L) { + OS << format("0x%8.8" PRIx64 ": ", L.Offset); + L.dump(OS, BaseAddr, IsLittleEndian, AddressSize, MRI, nullptr, DumpOpts, + /*Indent=*/12); + OS << "\n"; + }; + + if (Offset) { + if (auto *L = getLocationListAtOffset(*Offset)) + DumpLocationList(*L); + return; + } + + for (const LocationList &L : Locations) { + DumpLocationList(L); + if (&L != &Locations.back()) + OS << '\n'; + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp new file mode 100644 index 000000000000..9a0e770aed3d --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -0,0 +1,100 @@ +//===- DWARFDebugMacro.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +void DWARFDebugMacro::dump(raw_ostream &OS) const { + unsigned IndLevel = 0; + for (const Entry &E : Macros) { + // There should not be DW_MACINFO_end_file when IndLevel is Zero. However, + // this check handles the case of corrupted ".debug_macinfo" section. + if (IndLevel > 0) + IndLevel -= (E.Type == DW_MACINFO_end_file); + // Print indentation. + for (unsigned I = 0; I < IndLevel; I++) + OS << " "; + IndLevel += (E.Type == DW_MACINFO_start_file); + + WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + OS << " - lineno: " << E.Line; + OS << " macro: " << E.MacroStr; + break; + case DW_MACINFO_start_file: + OS << " - lineno: " << E.Line; + OS << " filenum: " << E.File; + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + OS << " - constant: " << E.ExtConstant; + OS << " string: " << E.ExtStr; + break; + } + OS << "\n"; + } +} + +void DWARFDebugMacro::parse(DataExtractor data) { + uint64_t Offset = 0; + while (data.isValidOffset(Offset)) { + // A macro list entry consists of: + Entry E; + // 1. Macinfo type + E.Type = data.getULEB128(&Offset); + + if (E.Type == 0) { + // Reached end of ".debug_macinfo" section. + return; + } + + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + // Push the corrupted entry to the list and halt parsing. + E.Type = DW_MACINFO_invalid; + Macros.push_back(E); + return; + case DW_MACINFO_define: + case DW_MACINFO_undef: + // 2. Source line + E.Line = data.getULEB128(&Offset); + // 3. Macro string + E.MacroStr = data.getCStr(&Offset); + break; + case DW_MACINFO_start_file: + // 2. Source line + E.Line = data.getULEB128(&Offset); + // 3. Source file id + E.File = data.getULEB128(&Offset); + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + // 2. Vendor extension constant + E.ExtConstant = data.getULEB128(&Offset); + // 3. Vendor extension string + E.ExtStr = data.getCStr(&Offset); + break; + } + + Macros.push_back(E); + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp new file mode 100644 index 000000000000..ab71b239cb67 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -0,0 +1,69 @@ +//===- DWARFDebugPubTable.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +DWARFDebugPubTable::DWARFDebugPubTable(const DWARFObject &Obj, + const DWARFSection &Sec, + bool LittleEndian, bool GnuStyle) + : GnuStyle(GnuStyle) { + DWARFDataExtractor PubNames(Obj, Sec, LittleEndian, 0); + uint64_t Offset = 0; + while (PubNames.isValidOffset(Offset)) { + Sets.push_back({}); + Set &SetData = Sets.back(); + + SetData.Length = PubNames.getU32(&Offset); + SetData.Version = PubNames.getU16(&Offset); + SetData.Offset = PubNames.getRelocatedValue(4, &Offset); + SetData.Size = PubNames.getU32(&Offset); + + while (Offset < Sec.Data.size()) { + uint32_t DieRef = PubNames.getU32(&Offset); + if (DieRef == 0) + break; + uint8_t IndexEntryValue = GnuStyle ? PubNames.getU8(&Offset) : 0; + StringRef Name = PubNames.getCStrRef(&Offset); + SetData.Entries.push_back( + {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + } + } +} + +void DWARFDebugPubTable::dump(raw_ostream &OS) const { + for (const Set &S : Sets) { + OS << "length = " << format("0x%08x", S.Length); + OS << " version = " << format("0x%04x", S.Version); + OS << " unit_offset = " << format("0x%08" PRIx64, S.Offset); + OS << " unit_size = " << format("0x%08x", S.Size) << '\n'; + OS << (GnuStyle ? "Offset Linkage Kind Name\n" + : "Offset Name\n"); + + for (const Entry &E : S.Entries) { + OS << format("0x%8.8" PRIx64 " ", E.SecOffset); + if (GnuStyle) { + StringRef EntryLinkage = + GDBIndexEntryLinkageString(E.Descriptor.Linkage); + StringRef EntryKind = dwarf::GDBIndexEntryKindString(E.Descriptor.Kind); + OS << format("%-8s", EntryLinkage.data()) << ' ' + << format("%-8s", EntryKind.data()) << ' '; + } + OS << '\"' << E.Name << "\"\n"; + } + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp new file mode 100644 index 000000000000..1a1857d8cd79 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp @@ -0,0 +1,95 @@ +//===- DWARFDebugRangesList.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +void DWARFDebugRangeList::clear() { + Offset = -1ULL; + AddressSize = 0; + Entries.clear(); +} + +Error DWARFDebugRangeList::extract(const DWARFDataExtractor &data, + uint64_t *offset_ptr) { + clear(); + if (!data.isValidOffset(*offset_ptr)) + return createStringError(errc::invalid_argument, + "invalid range list offset 0x%" PRIx64, *offset_ptr); + + AddressSize = data.getAddressSize(); + if (AddressSize != 4 && AddressSize != 8) + return createStringError(errc::invalid_argument, + "invalid address size: %" PRIu8, AddressSize); + Offset = *offset_ptr; + while (true) { + RangeListEntry Entry; + Entry.SectionIndex = -1ULL; + + uint64_t prev_offset = *offset_ptr; + Entry.StartAddress = data.getRelocatedAddress(offset_ptr); + Entry.EndAddress = + data.getRelocatedAddress(offset_ptr, &Entry.SectionIndex); + + // Check that both values were extracted correctly. + if (*offset_ptr != prev_offset + 2 * AddressSize) { + clear(); + return createStringError(errc::invalid_argument, + "invalid range list entry at offset 0x%" PRIx64, + prev_offset); + } + if (Entry.isEndOfListEntry()) + break; + Entries.push_back(Entry); + } + return Error::success(); +} + +void DWARFDebugRangeList::dump(raw_ostream &OS) const { + for (const RangeListEntry &RLE : Entries) { + const char *format_str = + (AddressSize == 4 ? "%08" PRIx64 " %08" PRIx64 " %08" PRIx64 "\n" + : "%08" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n"); + OS << format(format_str, Offset, RLE.StartAddress, RLE.EndAddress); + } + OS << format("%08" PRIx64 " <End of list>\n", Offset); +} + +DWARFAddressRangesVector DWARFDebugRangeList::getAbsoluteRanges( + llvm::Optional<object::SectionedAddress> BaseAddr) const { + DWARFAddressRangesVector Res; + for (const RangeListEntry &RLE : Entries) { + if (RLE.isBaseAddressSelectionEntry(AddressSize)) { + BaseAddr = {RLE.EndAddress, RLE.SectionIndex}; + continue; + } + + DWARFAddressRange E; + E.LowPC = RLE.StartAddress; + E.HighPC = RLE.EndAddress; + E.SectionIndex = RLE.SectionIndex; + // Base address of a range list entry is determined by the closest preceding + // base address selection entry in the same range list. It defaults to the + // base address of the compilation unit if there is no such entry. + if (BaseAddr) { + E.LowPC += BaseAddr->Address; + E.HighPC += BaseAddr->Address; + if (E.SectionIndex == -1ULL) + E.SectionIndex = BaseAddr->SectionIndex; + } + Res.push_back(E); + } + return Res; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp new file mode 100644 index 000000000000..f6785b89e86d --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp @@ -0,0 +1,245 @@ +//===- DWARFDebugRnglists.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +Error RangeListEntry::extract(DWARFDataExtractor Data, uint64_t End, + uint64_t *OffsetPtr) { + Offset = *OffsetPtr; + SectionIndex = -1ULL; + // The caller should guarantee that we have at least 1 byte available, so + // we just assert instead of revalidate. + assert(*OffsetPtr < End && + "not enough space to extract a rangelist encoding"); + uint8_t Encoding = Data.getU8(OffsetPtr); + + switch (Encoding) { + case dwarf::DW_RLE_end_of_list: + Value0 = Value1 = 0; + break; + // TODO: Support other encodings. + case dwarf::DW_RLE_base_addressx: { + uint64_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError( + errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_base_addressx encoding at offset 0x%" PRIx64, + PreviousOffset); + break; + } + case dwarf::DW_RLE_startx_endx: + return createStringError(errc::not_supported, + "unsupported rnglists encoding DW_RLE_startx_endx at " + "offset 0x%" PRIx64, + *OffsetPtr - 1); + case dwarf::DW_RLE_startx_length: { + uint64_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError( + errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_startx_length encoding at offset 0x%" PRIx64, + PreviousOffset); + break; + } + case dwarf::DW_RLE_offset_pair: { + uint64_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getULEB128(OffsetPtr); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError(errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_offset_pair encoding at offset 0x%" PRIx64, + PreviousOffset); + break; + } + case dwarf::DW_RLE_base_address: { + if ((End - *OffsetPtr) < Data.getAddressSize()) + return createStringError(errc::invalid_argument, + "insufficient space remaining in table for " + "DW_RLE_base_address encoding at offset 0x%" PRIx64, + *OffsetPtr - 1); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + break; + } + case dwarf::DW_RLE_start_end: { + if ((End - *OffsetPtr) < unsigned(Data.getAddressSize() * 2)) + return createStringError(errc::invalid_argument, + "insufficient space remaining in table for " + "DW_RLE_start_end encoding " + "at offset 0x%" PRIx64, + *OffsetPtr - 1); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + Value1 = Data.getRelocatedAddress(OffsetPtr); + break; + } + case dwarf::DW_RLE_start_length: { + uint64_t PreviousOffset = *OffsetPtr - 1; + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + Value1 = Data.getULEB128(OffsetPtr); + if (End < *OffsetPtr) + return createStringError(errc::invalid_argument, + "read past end of table when reading " + "DW_RLE_start_length encoding at offset 0x%" PRIx64, + PreviousOffset); + break; + } + default: + return createStringError(errc::not_supported, + "unknown rnglists encoding 0x%" PRIx32 + " at offset 0x%" PRIx64, + uint32_t(Encoding), *OffsetPtr - 1); + } + + EntryKind = Encoding; + return Error::success(); +} + +DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( + llvm::Optional<object::SectionedAddress> BaseAddr, DWARFUnit &U) const { + DWARFAddressRangesVector Res; + for (const RangeListEntry &RLE : Entries) { + if (RLE.EntryKind == dwarf::DW_RLE_end_of_list) + break; + if (RLE.EntryKind == dwarf::DW_RLE_base_addressx) { + BaseAddr = U.getAddrOffsetSectionItem(RLE.Value0); + if (!BaseAddr) + BaseAddr = {RLE.Value0, -1ULL}; + continue; + } + if (RLE.EntryKind == dwarf::DW_RLE_base_address) { + BaseAddr = {RLE.Value0, RLE.SectionIndex}; + continue; + } + + DWARFAddressRange E; + E.SectionIndex = RLE.SectionIndex; + if (BaseAddr && E.SectionIndex == -1ULL) + E.SectionIndex = BaseAddr->SectionIndex; + + switch (RLE.EntryKind) { + case dwarf::DW_RLE_offset_pair: + E.LowPC = RLE.Value0; + E.HighPC = RLE.Value1; + if (BaseAddr) { + E.LowPC += BaseAddr->Address; + E.HighPC += BaseAddr->Address; + } + break; + case dwarf::DW_RLE_start_end: + E.LowPC = RLE.Value0; + E.HighPC = RLE.Value1; + break; + case dwarf::DW_RLE_start_length: + E.LowPC = RLE.Value0; + E.HighPC = E.LowPC + RLE.Value1; + break; + case dwarf::DW_RLE_startx_length: { + auto Start = U.getAddrOffsetSectionItem(RLE.Value0); + if (!Start) + Start = {0, -1ULL}; + E.SectionIndex = Start->SectionIndex; + E.LowPC = Start->Address; + E.HighPC = E.LowPC + RLE.Value1; + break; + } + default: + // Unsupported encodings should have been reported during extraction, + // so we should not run into any here. + llvm_unreachable("Unsupported range list encoding"); + } + Res.push_back(E); + } + return Res; +} + +void RangeListEntry::dump( + raw_ostream &OS, uint8_t AddrSize, uint8_t MaxEncodingStringLength, + uint64_t &CurrentBase, DIDumpOptions DumpOpts, + llvm::function_ref<Optional<object::SectionedAddress>(uint32_t)> + LookupPooledAddress) const { + auto PrintRawEntry = [](raw_ostream &OS, const RangeListEntry &Entry, + uint8_t AddrSize, DIDumpOptions DumpOpts) { + if (DumpOpts.Verbose) { + DumpOpts.DisplayRawContents = true; + DWARFAddressRange(Entry.Value0, Entry.Value1) + .dump(OS, AddrSize, DumpOpts); + OS << " => "; + } + }; + + if (DumpOpts.Verbose) { + // Print the section offset in verbose mode. + OS << format("0x%8.8" PRIx64 ":", Offset); + auto EncodingString = dwarf::RangeListEncodingString(EntryKind); + // Unsupported encodings should have been reported during parsing. + assert(!EncodingString.empty() && "Unknown range entry encoding"); + OS << format(" [%s%*c", EncodingString.data(), + MaxEncodingStringLength - EncodingString.size() + 1, ']'); + if (EntryKind != dwarf::DW_RLE_end_of_list) + OS << ": "; + } + + switch (EntryKind) { + case dwarf::DW_RLE_end_of_list: + OS << (DumpOpts.Verbose ? "" : "<End of list>"); + break; + case dwarf::DW_RLE_base_addressx: { + if (auto SA = LookupPooledAddress(Value0)) + CurrentBase = SA->Address; + else + CurrentBase = Value0; + if (!DumpOpts.Verbose) + return; + OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Value0); + break; + } + case dwarf::DW_RLE_base_address: + // In non-verbose mode we do not print anything for this entry. + CurrentBase = Value0; + if (!DumpOpts.Verbose) + return; + OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Value0); + break; + case dwarf::DW_RLE_start_length: + PrintRawEntry(OS, *this, AddrSize, DumpOpts); + DWARFAddressRange(Value0, Value0 + Value1).dump(OS, AddrSize, DumpOpts); + break; + case dwarf::DW_RLE_offset_pair: + PrintRawEntry(OS, *this, AddrSize, DumpOpts); + DWARFAddressRange(Value0 + CurrentBase, Value1 + CurrentBase) + .dump(OS, AddrSize, DumpOpts); + break; + case dwarf::DW_RLE_start_end: + DWARFAddressRange(Value0, Value1).dump(OS, AddrSize, DumpOpts); + break; + case dwarf::DW_RLE_startx_length: { + PrintRawEntry(OS, *this, AddrSize, DumpOpts); + uint64_t Start = 0; + if (auto SA = LookupPooledAddress(Value0)) + Start = SA->Address; + DWARFAddressRange(Start, Start + Value1).dump(OS, AddrSize, DumpOpts); + break; + } + default: + llvm_unreachable("Unsupported range list encoding"); + } + OS << "\n"; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp new file mode 100644 index 000000000000..cec194e8b6b3 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -0,0 +1,740 @@ +//===- DWARFDie.cpp -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <string> +#include <utility> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +static void dumpApplePropertyAttribute(raw_ostream &OS, uint64_t Val) { + OS << " ("; + do { + uint64_t Shift = countTrailingZeros(Val); + assert(Shift < 64 && "undefined behavior"); + uint64_t Bit = 1ULL << Shift; + auto PropName = ApplePropertyString(Bit); + if (!PropName.empty()) + OS << PropName; + else + OS << format("DW_APPLE_PROPERTY_0x%" PRIx64, Bit); + if (!(Val ^= Bit)) + break; + OS << ", "; + } while (true); + OS << ")"; +} + +static void dumpRanges(const DWARFObject &Obj, raw_ostream &OS, + const DWARFAddressRangesVector &Ranges, + unsigned AddressSize, unsigned Indent, + const DIDumpOptions &DumpOpts) { + if (!DumpOpts.ShowAddresses) + return; + + ArrayRef<SectionName> SectionNames; + if (DumpOpts.Verbose) + SectionNames = Obj.getSectionNames(); + + for (const DWARFAddressRange &R : Ranges) { + OS << '\n'; + OS.indent(Indent); + R.dump(OS, AddressSize); + + DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, R.SectionIndex); + } +} + +static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, + DWARFUnit *U, unsigned Indent, + DIDumpOptions DumpOpts) { + DWARFContext &Ctx = U->getContext(); + const DWARFObject &Obj = Ctx.getDWARFObj(); + const MCRegisterInfo *MRI = Ctx.getRegisterInfo(); + if (FormValue.isFormClass(DWARFFormValue::FC_Block) || + FormValue.isFormClass(DWARFFormValue::FC_Exprloc)) { + ArrayRef<uint8_t> Expr = *FormValue.getAsBlock(); + DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), + Ctx.isLittleEndian(), 0); + DWARFExpression(Data, U->getVersion(), U->getAddressByteSize()) + .print(OS, MRI, U); + return; + } + + FormValue.dump(OS, DumpOpts); + const auto &DumpLL = [&](auto ExpectedLL) { + if (ExpectedLL) { + uint64_t BaseAddr = 0; + if (Optional<object::SectionedAddress> BA = U->getBaseAddress()) + BaseAddr = BA->Address; + auto LLDumpOpts = DumpOpts; + LLDumpOpts.Verbose = false; + ExpectedLL->dump(OS, BaseAddr, Ctx.isLittleEndian(), Obj.getAddressSize(), + MRI, U, LLDumpOpts, Indent); + } else { + OS << '\n'; + OS.indent(Indent); + OS << formatv("error extracting location list: {0}", + fmt_consume(ExpectedLL.takeError())); + } + }; + if (FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { + uint64_t Offset = *FormValue.getAsSectionOffset(); + if (!U->isDWOUnit() && !U->getLocSection()->Data.empty()) { + DWARFDebugLoc DebugLoc; + DWARFDataExtractor Data(Obj, *U->getLocSection(), Ctx.isLittleEndian(), + Obj.getAddressSize()); + DumpLL(DebugLoc.parseOneLocationList(Data, &Offset)); + return; + } + + bool UseLocLists = !U->isDWOUnit(); + StringRef LoclistsSectionData = + UseLocLists ? Obj.getLoclistsSection().Data : U->getLocSectionData(); + + if (!LoclistsSectionData.empty()) { + DataExtractor Data(LoclistsSectionData, Ctx.isLittleEndian(), + Obj.getAddressSize()); + + // Old-style location list were used in DWARF v4 (.debug_loc.dwo section). + // Modern locations list (.debug_loclists) are used starting from v5. + // Ideally we should take the version from the .debug_loclists section + // header, but using CU's version for simplicity. + DumpLL(DWARFDebugLoclists::parseOneLocationList( + Data, &Offset, UseLocLists ? U->getVersion() : 4)); + } + } +} + +/// Dump the name encoded in the type tag. +static void dumpTypeTagName(raw_ostream &OS, dwarf::Tag T) { + StringRef TagStr = TagString(T); + if (!TagStr.startswith("DW_TAG_") || !TagStr.endswith("_type")) + return; + OS << TagStr.substr(7, TagStr.size() - 12) << " "; +} + +static void dumpArrayType(raw_ostream &OS, const DWARFDie &D) { + Optional<uint64_t> Bound; + for (const DWARFDie &C : D.children()) + if (C.getTag() == DW_TAG_subrange_type) { + Optional<uint64_t> LB; + Optional<uint64_t> Count; + Optional<uint64_t> UB; + Optional<unsigned> DefaultLB; + if (Optional<DWARFFormValue> L = C.find(DW_AT_lower_bound)) + LB = L->getAsUnsignedConstant(); + if (Optional<DWARFFormValue> CountV = C.find(DW_AT_count)) + Count = CountV->getAsUnsignedConstant(); + if (Optional<DWARFFormValue> UpperV = C.find(DW_AT_upper_bound)) + UB = UpperV->getAsUnsignedConstant(); + if (Optional<DWARFFormValue> LV = + D.getDwarfUnit()->getUnitDIE().find(DW_AT_language)) + if (Optional<uint64_t> LC = LV->getAsUnsignedConstant()) + if ((DefaultLB = + LanguageLowerBound(static_cast<dwarf::SourceLanguage>(*LC)))) + if (LB && *LB == *DefaultLB) + LB = None; + if (!LB && !Count && !UB) + OS << "[]"; + else if (!LB && (Count || UB) && DefaultLB) + OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']'; + else { + OS << "[["; + if (LB) + OS << *LB; + else + OS << '?'; + OS << ", "; + if (Count) + if (LB) + OS << *LB + *Count; + else + OS << "? + " << *Count; + else if (UB) + OS << *UB + 1; + else + OS << '?'; + OS << ")]"; + } + } +} + +/// Recursively dump the DIE type name when applicable. +static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) { + if (!D.isValid()) + return; + + if (const char *Name = D.getName(DINameKind::LinkageName)) { + OS << Name; + return; + } + + // FIXME: We should have pretty printers per language. Currently we print + // everything as if it was C++ and fall back to the TAG type name. + const dwarf::Tag T = D.getTag(); + switch (T) { + case DW_TAG_array_type: + case DW_TAG_pointer_type: + case DW_TAG_ptr_to_member_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_subroutine_type: + break; + default: + dumpTypeTagName(OS, T); + } + + // Follow the DW_AT_type if possible. + DWARFDie TypeDie = D.getAttributeValueAsReferencedDie(DW_AT_type); + dumpTypeName(OS, TypeDie); + + switch (T) { + case DW_TAG_subroutine_type: { + if (!TypeDie) + OS << "void"; + OS << '('; + bool First = true; + for (const DWARFDie &C : D.children()) { + if (C.getTag() == DW_TAG_formal_parameter) { + if (!First) + OS << ", "; + First = false; + dumpTypeName(OS, C.getAttributeValueAsReferencedDie(DW_AT_type)); + } + } + OS << ')'; + break; + } + case DW_TAG_array_type: { + dumpArrayType(OS, D); + break; + } + case DW_TAG_pointer_type: + OS << '*'; + break; + case DW_TAG_ptr_to_member_type: + if (DWARFDie Cont = + D.getAttributeValueAsReferencedDie(DW_AT_containing_type)) { + dumpTypeName(OS << ' ', Cont); + OS << "::"; + } + OS << '*'; + break; + case DW_TAG_reference_type: + OS << '&'; + break; + case DW_TAG_rvalue_reference_type: + OS << "&&"; + break; + default: + break; + } +} + +static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, + uint64_t *OffsetPtr, dwarf::Attribute Attr, + dwarf::Form Form, unsigned Indent, + DIDumpOptions DumpOpts) { + if (!Die.isValid()) + return; + const char BaseIndent[] = " "; + OS << BaseIndent; + OS.indent(Indent + 2); + WithColor(OS, HighlightColor::Attribute) << formatv("{0}", Attr); + + if (DumpOpts.Verbose || DumpOpts.ShowForm) + OS << formatv(" [{0}]", Form); + + DWARFUnit *U = Die.getDwarfUnit(); + DWARFFormValue FormValue = DWARFFormValue::createFromUnit(Form, U, OffsetPtr); + + OS << "\t("; + + StringRef Name; + std::string File; + auto Color = HighlightColor::Enumerator; + if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { + Color = HighlightColor::String; + if (const auto *LT = U->getContext().getLineTableForUnit(U)) + if (LT->getFileNameByIndex( + FormValue.getAsUnsignedConstant().getValue(), + U->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { + File = '"' + File + '"'; + Name = File; + } + } else if (Optional<uint64_t> Val = FormValue.getAsUnsignedConstant()) + Name = AttributeValueString(Attr, *Val); + + if (!Name.empty()) + WithColor(OS, Color) << Name; + else if (Attr == DW_AT_decl_line || Attr == DW_AT_call_line) + OS << *FormValue.getAsUnsignedConstant(); + else if (Attr == DW_AT_high_pc && !DumpOpts.ShowForm && !DumpOpts.Verbose && + FormValue.getAsUnsignedConstant()) { + if (DumpOpts.ShowAddresses) { + // Print the actual address rather than the offset. + uint64_t LowPC, HighPC, Index; + if (Die.getLowAndHighPC(LowPC, HighPC, Index)) + OS << format("0x%016" PRIx64, HighPC); + else + FormValue.dump(OS, DumpOpts); + } + } else if (DWARFAttribute::mayHaveLocationDescription(Attr)) + dumpLocation(OS, FormValue, U, sizeof(BaseIndent) + Indent + 4, DumpOpts); + else + FormValue.dump(OS, DumpOpts); + + std::string Space = DumpOpts.ShowAddresses ? " " : ""; + + // We have dumped the attribute raw value. For some attributes + // having both the raw value and the pretty-printed value is + // interesting. These attributes are handled below. + if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin) { + if (const char *Name = + Die.getAttributeValueAsReferencedDie(FormValue).getName( + DINameKind::LinkageName)) + OS << Space << "\"" << Name << '\"'; + } else if (Attr == DW_AT_type) { + OS << Space << "\""; + dumpTypeName(OS, Die.getAttributeValueAsReferencedDie(FormValue)); + OS << '"'; + } else if (Attr == DW_AT_APPLE_property_attribute) { + if (Optional<uint64_t> OptVal = FormValue.getAsUnsignedConstant()) + dumpApplePropertyAttribute(OS, *OptVal); + } else if (Attr == DW_AT_ranges) { + const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); + // For DW_FORM_rnglistx we need to dump the offset separately, since + // we have only dumped the index so far. + if (FormValue.getForm() == DW_FORM_rnglistx) + if (auto RangeListOffset = + U->getRnglistOffset(*FormValue.getAsSectionOffset())) { + DWARFFormValue FV = DWARFFormValue::createFromUValue( + dwarf::DW_FORM_sec_offset, *RangeListOffset); + FV.dump(OS, DumpOpts); + } + if (auto RangesOrError = Die.getAddressRanges()) + dumpRanges(Obj, OS, RangesOrError.get(), U->getAddressByteSize(), + sizeof(BaseIndent) + Indent + 4, DumpOpts); + else + WithColor::error() << "decoding address ranges: " + << toString(RangesOrError.takeError()) << '\n'; + } + + OS << ")\n"; +} + +bool DWARFDie::isSubprogramDIE() const { return getTag() == DW_TAG_subprogram; } + +bool DWARFDie::isSubroutineDIE() const { + auto Tag = getTag(); + return Tag == DW_TAG_subprogram || Tag == DW_TAG_inlined_subroutine; +} + +Optional<DWARFFormValue> DWARFDie::find(dwarf::Attribute Attr) const { + if (!isValid()) + return None; + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) + return AbbrevDecl->getAttributeValue(getOffset(), Attr, *U); + return None; +} + +Optional<DWARFFormValue> +DWARFDie::find(ArrayRef<dwarf::Attribute> Attrs) const { + if (!isValid()) + return None; + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) { + for (auto Attr : Attrs) { + if (auto Value = AbbrevDecl->getAttributeValue(getOffset(), Attr, *U)) + return Value; + } + } + return None; +} + +Optional<DWARFFormValue> +DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const { + std::vector<DWARFDie> Worklist; + Worklist.push_back(*this); + + // Keep track if DIEs already seen to prevent infinite recursion. + // Empirically we rarely see a depth of more than 3 when dealing with valid + // DWARF. This corresponds to following the DW_AT_abstract_origin and + // DW_AT_specification just once. + SmallSet<DWARFDie, 3> Seen; + Seen.insert(*this); + + while (!Worklist.empty()) { + DWARFDie Die = Worklist.back(); + Worklist.pop_back(); + + if (!Die.isValid()) + continue; + + if (auto Value = Die.find(Attrs)) + return Value; + + if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) + if (Seen.insert(D).second) + Worklist.push_back(D); + + if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_specification)) + if (Seen.insert(D).second) + Worklist.push_back(D); + } + + return None; +} + +DWARFDie +DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const { + if (Optional<DWARFFormValue> F = find(Attr)) + return getAttributeValueAsReferencedDie(*F); + return DWARFDie(); +} + +DWARFDie +DWARFDie::getAttributeValueAsReferencedDie(const DWARFFormValue &V) const { + if (auto SpecRef = V.getAsRelativeReference()) { + if (SpecRef->Unit) + return SpecRef->Unit->getDIEForOffset(SpecRef->Unit->getOffset() + SpecRef->Offset); + if (auto SpecUnit = U->getUnitVector().getUnitForOffset(SpecRef->Offset)) + return SpecUnit->getDIEForOffset(SpecRef->Offset); + } + return DWARFDie(); +} + +Optional<uint64_t> DWARFDie::getRangesBaseAttribute() const { + return toSectionOffset(find({DW_AT_rnglists_base, DW_AT_GNU_ranges_base})); +} + +Optional<uint64_t> DWARFDie::getHighPC(uint64_t LowPC) const { + if (auto FormValue = find(DW_AT_high_pc)) { + if (auto Address = FormValue->getAsAddress()) { + // High PC is an address. + return Address; + } + if (auto Offset = FormValue->getAsUnsignedConstant()) { + // High PC is an offset from LowPC. + return LowPC + *Offset; + } + } + return None; +} + +bool DWARFDie::getLowAndHighPC(uint64_t &LowPC, uint64_t &HighPC, + uint64_t &SectionIndex) const { + auto F = find(DW_AT_low_pc); + auto LowPcAddr = toSectionedAddress(F); + if (!LowPcAddr) + return false; + if (auto HighPcAddr = getHighPC(LowPcAddr->Address)) { + LowPC = LowPcAddr->Address; + HighPC = *HighPcAddr; + SectionIndex = LowPcAddr->SectionIndex; + return true; + } + return false; +} + +Expected<DWARFAddressRangesVector> DWARFDie::getAddressRanges() const { + if (isNULL()) + return DWARFAddressRangesVector(); + // Single range specified by low/high PC. + uint64_t LowPC, HighPC, Index; + if (getLowAndHighPC(LowPC, HighPC, Index)) + return DWARFAddressRangesVector{{LowPC, HighPC, Index}}; + + Optional<DWARFFormValue> Value = find(DW_AT_ranges); + if (Value) { + if (Value->getForm() == DW_FORM_rnglistx) + return U->findRnglistFromIndex(*Value->getAsSectionOffset()); + return U->findRnglistFromOffset(*Value->getAsSectionOffset()); + } + return DWARFAddressRangesVector(); +} + +void DWARFDie::collectChildrenAddressRanges( + DWARFAddressRangesVector &Ranges) const { + if (isNULL()) + return; + if (isSubprogramDIE()) { + if (auto DIERangesOrError = getAddressRanges()) + Ranges.insert(Ranges.end(), DIERangesOrError.get().begin(), + DIERangesOrError.get().end()); + else + llvm::consumeError(DIERangesOrError.takeError()); + } + + for (auto Child : children()) + Child.collectChildrenAddressRanges(Ranges); +} + +bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { + auto RangesOrError = getAddressRanges(); + if (!RangesOrError) { + llvm::consumeError(RangesOrError.takeError()); + return false; + } + + for (const auto &R : RangesOrError.get()) + if (R.LowPC <= Address && Address < R.HighPC) + return true; + return false; +} + +const char *DWARFDie::getSubroutineName(DINameKind Kind) const { + if (!isSubroutineDIE()) + return nullptr; + return getName(Kind); +} + +const char *DWARFDie::getName(DINameKind Kind) const { + if (!isValid() || Kind == DINameKind::None) + return nullptr; + // Try to get mangled name only if it was asked for. + if (Kind == DINameKind::LinkageName) { + if (auto Name = dwarf::toString( + findRecursively({DW_AT_MIPS_linkage_name, DW_AT_linkage_name}), + nullptr)) + return Name; + } + if (auto Name = dwarf::toString(findRecursively(DW_AT_name), nullptr)) + return Name; + return nullptr; +} + +uint64_t DWARFDie::getDeclLine() const { + return toUnsigned(findRecursively(DW_AT_decl_line), 0); +} + +void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, + uint32_t &CallColumn, + uint32_t &CallDiscriminator) const { + CallFile = toUnsigned(find(DW_AT_call_file), 0); + CallLine = toUnsigned(find(DW_AT_call_line), 0); + CallColumn = toUnsigned(find(DW_AT_call_column), 0); + CallDiscriminator = toUnsigned(find(DW_AT_GNU_discriminator), 0); +} + +/// Helper to dump a DIE with all of its parents, but no siblings. +static unsigned dumpParentChain(DWARFDie Die, raw_ostream &OS, unsigned Indent, + DIDumpOptions DumpOpts, unsigned Depth = 0) { + if (!Die) + return Indent; + if (DumpOpts.ParentRecurseDepth > 0 && Depth >= DumpOpts.ParentRecurseDepth) + return Indent; + Indent = dumpParentChain(Die.getParent(), OS, Indent, DumpOpts, Depth + 1); + 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 uint64_t Offset = getOffset(); + uint64_t offset = Offset; + if (DumpOpts.ShowParents) { + DIDumpOptions ParentDumpOpts = DumpOpts; + ParentDumpOpts.ShowParents = false; + ParentDumpOpts.ShowChildren = false; + Indent = dumpParentChain(getParent(), OS, Indent, ParentDumpOpts); + } + + if (debug_info_data.isValidOffset(offset)) { + uint32_t abbrCode = debug_info_data.getULEB128(&offset); + if (DumpOpts.ShowAddresses) + WithColor(OS, HighlightColor::Address).get() + << format("\n0x%8.8" PRIx64 ": ", Offset); + + if (abbrCode) { + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) { + WithColor(OS, HighlightColor::Tag).get().indent(Indent) + << formatv("{0}", getTag()); + if (DumpOpts.Verbose) + OS << format(" [%u] %c", abbrCode, + AbbrevDecl->hasChildren() ? '*' : ' '); + OS << '\n'; + + // Dump all data in the DIE for the attributes. + for (const auto &AttrSpec : AbbrevDecl->attributes()) { + if (AttrSpec.Form == DW_FORM_implicit_const) { + // We are dumping .debug_info section , + // implicit_const attribute values are not really stored here, + // but in .debug_abbrev section. So we just skip such attrs. + continue; + } + dumpAttribute(OS, *this, &offset, AttrSpec.Attr, AttrSpec.Form, + Indent, DumpOpts); + } + + DWARFDie child = getFirstChild(); + if (DumpOpts.ShowChildren && DumpOpts.ChildRecurseDepth > 0 && child) { + DumpOpts.ChildRecurseDepth--; + DIDumpOptions ChildDumpOpts = DumpOpts; + ChildDumpOpts.ShowParents = false; + while (child) { + child.dump(OS, Indent + 2, ChildDumpOpts); + child = child.getSibling(); + } + } + } else { + OS << "Abbreviation code not found in 'debug_abbrev' class for code: " + << abbrCode << '\n'; + } + } else { + OS.indent(Indent) << "NULL\n"; + } + } +} + +LLVM_DUMP_METHOD void DWARFDie::dump() const { dump(llvm::errs(), 0); } + +DWARFDie DWARFDie::getParent() const { + if (isValid()) + return U->getParent(Die); + return DWARFDie(); +} + +DWARFDie DWARFDie::getSibling() const { + if (isValid()) + return U->getSibling(Die); + return DWARFDie(); +} + +DWARFDie DWARFDie::getPreviousSibling() const { + if (isValid()) + return U->getPreviousSibling(Die); + return DWARFDie(); +} + +DWARFDie DWARFDie::getFirstChild() const { + if (isValid()) + return U->getFirstChild(Die); + return DWARFDie(); +} + +DWARFDie DWARFDie::getLastChild() const { + if (isValid()) + return U->getLastChild(Die); + return DWARFDie(); +} + +iterator_range<DWARFDie::attribute_iterator> DWARFDie::attributes() const { + return make_range(attribute_iterator(*this, false), + attribute_iterator(*this, true)); +} + +DWARFDie::attribute_iterator::attribute_iterator(DWARFDie D, bool End) + : Die(D), Index(0) { + auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); + assert(AbbrDecl && "Must have abbreviation declaration"); + if (End) { + // This is the end iterator so we set the index to the attribute count. + Index = AbbrDecl->getNumAttributes(); + } else { + // This is the begin iterator so we extract the value for this->Index. + AttrValue.Offset = D.getOffset() + AbbrDecl->getCodeByteSize(); + updateForIndex(*AbbrDecl, 0); + } +} + +void DWARFDie::attribute_iterator::updateForIndex( + const DWARFAbbreviationDeclaration &AbbrDecl, uint32_t I) { + Index = I; + // AbbrDecl must be valid before calling this function. + auto NumAttrs = AbbrDecl.getNumAttributes(); + if (Index < NumAttrs) { + AttrValue.Attr = AbbrDecl.getAttrByIndex(Index); + // Add the previous byte size of any previous attribute value. + AttrValue.Offset += AttrValue.ByteSize; + uint64_t ParseOffset = AttrValue.Offset; + auto U = Die.getDwarfUnit(); + assert(U && "Die must have valid DWARF unit"); + AttrValue.Value = DWARFFormValue::createFromUnit( + AbbrDecl.getFormByIndex(Index), U, &ParseOffset); + AttrValue.ByteSize = ParseOffset - AttrValue.Offset; + } else { + assert(Index == NumAttrs && "Indexes should be [0, NumAttrs) only"); + AttrValue = {}; + } +} + +DWARFDie::attribute_iterator &DWARFDie::attribute_iterator::operator++() { + if (auto AbbrDecl = Die.getAbbreviationDeclarationPtr()) + updateForIndex(*AbbrDecl, Index + 1); + return *this; +} + +bool DWARFAttribute::mayHaveLocationDescription(dwarf::Attribute Attr) { + switch (Attr) { + // From the DWARF v5 specification. + case DW_AT_location: + case DW_AT_byte_size: + case DW_AT_bit_size: + case DW_AT_string_length: + case DW_AT_lower_bound: + case DW_AT_return_addr: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_data_member_location: + case DW_AT_frame_base: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_vtable_elem_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_byte_stride: + case DW_AT_rank: + case DW_AT_call_value: + case DW_AT_call_origin: + case DW_AT_call_target: + case DW_AT_call_target_clobbered: + case DW_AT_call_data_location: + case DW_AT_call_data_value: + // Extensions. + case DW_AT_GNU_call_site_value: + case DW_AT_GNU_call_site_target: + return true; + default: + return false; + } +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp new file mode 100644 index 000000000000..5009b1b7b412 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -0,0 +1,344 @@ +//===-- DWARFExpression.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/Format.h" +#include <cassert> +#include <cstdint> +#include <vector> + +using namespace llvm; +using namespace dwarf; + +namespace llvm { + +typedef std::vector<DWARFExpression::Operation::Description> DescVector; + +static DescVector getDescriptions() { + DescVector Descriptions; + typedef DWARFExpression::Operation Op; + typedef Op::Description Desc; + + Descriptions.resize(0xff); + Descriptions[DW_OP_addr] = Desc(Op::Dwarf2, Op::SizeAddr); + Descriptions[DW_OP_deref] = Desc(Op::Dwarf2); + Descriptions[DW_OP_const1u] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_const1s] = Desc(Op::Dwarf2, Op::SignedSize1); + Descriptions[DW_OP_const2u] = Desc(Op::Dwarf2, Op::Size2); + Descriptions[DW_OP_const2s] = Desc(Op::Dwarf2, Op::SignedSize2); + Descriptions[DW_OP_const4u] = Desc(Op::Dwarf2, Op::Size4); + Descriptions[DW_OP_const4s] = Desc(Op::Dwarf2, Op::SignedSize4); + Descriptions[DW_OP_const8u] = Desc(Op::Dwarf2, Op::Size8); + Descriptions[DW_OP_const8s] = Desc(Op::Dwarf2, Op::SignedSize8); + Descriptions[DW_OP_constu] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_consts] = Desc(Op::Dwarf2, Op::SignedSizeLEB); + Descriptions[DW_OP_dup] = Desc(Op::Dwarf2); + Descriptions[DW_OP_drop] = Desc(Op::Dwarf2); + Descriptions[DW_OP_over] = Desc(Op::Dwarf2); + Descriptions[DW_OP_pick] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_swap] = Desc(Op::Dwarf2); + Descriptions[DW_OP_rot] = Desc(Op::Dwarf2); + Descriptions[DW_OP_xderef] = Desc(Op::Dwarf2); + Descriptions[DW_OP_abs] = Desc(Op::Dwarf2); + Descriptions[DW_OP_and] = Desc(Op::Dwarf2); + Descriptions[DW_OP_div] = Desc(Op::Dwarf2); + Descriptions[DW_OP_minus] = Desc(Op::Dwarf2); + Descriptions[DW_OP_mod] = Desc(Op::Dwarf2); + Descriptions[DW_OP_mul] = Desc(Op::Dwarf2); + Descriptions[DW_OP_neg] = Desc(Op::Dwarf2); + Descriptions[DW_OP_not] = Desc(Op::Dwarf2); + Descriptions[DW_OP_or] = Desc(Op::Dwarf2); + Descriptions[DW_OP_plus] = Desc(Op::Dwarf2); + Descriptions[DW_OP_plus_uconst] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_shl] = Desc(Op::Dwarf2); + Descriptions[DW_OP_shr] = Desc(Op::Dwarf2); + Descriptions[DW_OP_shra] = Desc(Op::Dwarf2); + Descriptions[DW_OP_xor] = Desc(Op::Dwarf2); + Descriptions[DW_OP_skip] = Desc(Op::Dwarf2, Op::SignedSize2); + Descriptions[DW_OP_bra] = Desc(Op::Dwarf2, Op::SignedSize2); + Descriptions[DW_OP_eq] = Desc(Op::Dwarf2); + Descriptions[DW_OP_ge] = Desc(Op::Dwarf2); + Descriptions[DW_OP_gt] = Desc(Op::Dwarf2); + Descriptions[DW_OP_le] = Desc(Op::Dwarf2); + Descriptions[DW_OP_lt] = Desc(Op::Dwarf2); + Descriptions[DW_OP_ne] = Desc(Op::Dwarf2); + for (uint16_t LA = DW_OP_lit0; LA <= DW_OP_lit31; ++LA) + Descriptions[LA] = Desc(Op::Dwarf2); + for (uint16_t LA = DW_OP_reg0; LA <= DW_OP_reg31; ++LA) + Descriptions[LA] = Desc(Op::Dwarf2); + for (uint16_t LA = DW_OP_breg0; LA <= DW_OP_breg31; ++LA) + Descriptions[LA] = Desc(Op::Dwarf2, Op::SignedSizeLEB); + Descriptions[DW_OP_regx] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_fbreg] = Desc(Op::Dwarf2, Op::SignedSizeLEB); + Descriptions[DW_OP_bregx] = Desc(Op::Dwarf2, Op::SizeLEB, Op::SignedSizeLEB); + Descriptions[DW_OP_piece] = Desc(Op::Dwarf2, Op::SizeLEB); + Descriptions[DW_OP_deref_size] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_xderef_size] = Desc(Op::Dwarf2, Op::Size1); + Descriptions[DW_OP_nop] = Desc(Op::Dwarf2); + Descriptions[DW_OP_push_object_address] = Desc(Op::Dwarf3); + Descriptions[DW_OP_call2] = Desc(Op::Dwarf3, Op::Size2); + Descriptions[DW_OP_call4] = Desc(Op::Dwarf3, Op::Size4); + Descriptions[DW_OP_call_ref] = Desc(Op::Dwarf3, Op::SizeRefAddr); + Descriptions[DW_OP_form_tls_address] = Desc(Op::Dwarf3); + Descriptions[DW_OP_call_frame_cfa] = Desc(Op::Dwarf3); + Descriptions[DW_OP_bit_piece] = Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeLEB); + Descriptions[DW_OP_implicit_value] = + Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeBlock); + Descriptions[DW_OP_stack_value] = Desc(Op::Dwarf3); + Descriptions[DW_OP_GNU_push_tls_address] = Desc(Op::Dwarf3); + Descriptions[DW_OP_addrx] = Desc(Op::Dwarf4, Op::SizeLEB); + Descriptions[DW_OP_GNU_addr_index] = Desc(Op::Dwarf4, Op::SizeLEB); + Descriptions[DW_OP_GNU_const_index] = Desc(Op::Dwarf4, Op::SizeLEB); + Descriptions[DW_OP_GNU_entry_value] = Desc(Op::Dwarf4, Op::SizeLEB); + + Descriptions[DW_OP_convert] = Desc(Op::Dwarf5, Op::BaseTypeRef); + Descriptions[DW_OP_entry_value] = Desc(Op::Dwarf5, 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, uint64_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 if (AddressSize == 4) { + Operands[Operand] = Data.getU32(&Offset); + } else { + assert(AddressSize == 2); + Operands[Operand] = Data.getU16(&Offset); + } + break; + case Operation::SizeRefAddr: + if (getRefAddrSize(AddressSize, Version) == 8) { + Operands[Operand] = Data.getU64(&Offset); + } else if (getRefAddrSize(AddressSize, Version) == 4) { + Operands[Operand] = Data.getU32(&Offset); + } else { + assert(getRefAddrSize(AddressSize, Version) == 2); + Operands[Operand] = Data.getU16(&Offset); + } + break; + case Operation::SizeLEB: + if (Signed) + Operands[Operand] = Data.getSLEB128(&Offset); + else + Operands[Operand] = Data.getULEB128(&Offset); + break; + case Operation::BaseTypeRef: + 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"); + } + + OperandEndOffsets[Operand] = Offset; + } + + 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; + + if (Optional<unsigned> LLVMRegNum = MRI->getLLVMRegNum(DwarfRegNum, isEH)) { + 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, + DWARFUnit *U, + 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::BaseTypeRef && U) { + auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); + if (Die && Die.getTag() == dwarf::DW_TAG_base_type) { + OS << format(" (0x%08" PRIx64 ")", U->getOffset() + Operands[Operand]); + if (auto Name = Die.find(dwarf::DW_AT_name)) + OS << " \"" << Name->getAsCString() << "\""; + } else { + OS << format(" <invalid base_type ref: 0x%" PRIx64 ">", + Operands[Operand]); + } + } else if (Size == Operation::SizeBlock) { + uint64_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 if (Opcode != DW_OP_entry_value && + Opcode != DW_OP_GNU_entry_value) + OS << format(" 0x%" PRIx64, Operands[Operand]); + } + } + return true; +} + +void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo, + DWARFUnit *U, bool IsEH) const { + uint32_t EntryValExprSize = 0; + for (auto &Op : *this) { + if (!Op.print(OS, this, RegInfo, U, IsEH)) { + uint64_t FailOffset = Op.getEndOffset(); + while (FailOffset < Data.getData().size()) + OS << format(" %02x", Data.getU8(&FailOffset)); + return; + } + + if (Op.getCode() == DW_OP_entry_value || + Op.getCode() == DW_OP_GNU_entry_value) { + OS << "("; + EntryValExprSize = Op.getRawOperand(0); + continue; + } + + if (EntryValExprSize) { + EntryValExprSize--; + if (EntryValExprSize == 0) + OS << ")"; + } + + if (Op.getEndOffset() < Data.getData().size()) + OS << ", "; + } +} + +bool DWARFExpression::Operation::verify(DWARFUnit *U) { + + for (unsigned Operand = 0; Operand < 2; ++Operand) { + unsigned Size = Desc.Op[Operand]; + + if (Size == Operation::SizeNA) + break; + + if (Size == Operation::BaseTypeRef) { + auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); + if (!Die || Die.getTag() != dwarf::DW_TAG_base_type) { + Error = true; + return false; + } + } + } + + return true; +} + +bool DWARFExpression::verify(DWARFUnit *U) { + for (auto &Op : *this) + if (!Op.verify(U)) + return false; + + return true; +} + +} // namespace llvm diff --git a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp new file mode 100644 index 000000000000..26090638b34c --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -0,0 +1,720 @@ +//===- DWARFFormValue.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> +#include <limits> + +using namespace llvm; +using namespace dwarf; + +static const DWARFFormValue::FormClass DWARF5FormClasses[] = { + DWARFFormValue::FC_Unknown, // 0x0 + DWARFFormValue::FC_Address, // 0x01 DW_FORM_addr + DWARFFormValue::FC_Unknown, // 0x02 unused + DWARFFormValue::FC_Block, // 0x03 DW_FORM_block2 + DWARFFormValue::FC_Block, // 0x04 DW_FORM_block4 + DWARFFormValue::FC_Constant, // 0x05 DW_FORM_data2 + // --- These can be FC_SectionOffset in DWARF3 and below: + DWARFFormValue::FC_Constant, // 0x06 DW_FORM_data4 + DWARFFormValue::FC_Constant, // 0x07 DW_FORM_data8 + // --- + DWARFFormValue::FC_String, // 0x08 DW_FORM_string + DWARFFormValue::FC_Block, // 0x09 DW_FORM_block + DWARFFormValue::FC_Block, // 0x0a DW_FORM_block1 + DWARFFormValue::FC_Constant, // 0x0b DW_FORM_data1 + DWARFFormValue::FC_Flag, // 0x0c DW_FORM_flag + DWARFFormValue::FC_Constant, // 0x0d DW_FORM_sdata + DWARFFormValue::FC_String, // 0x0e DW_FORM_strp + DWARFFormValue::FC_Constant, // 0x0f DW_FORM_udata + DWARFFormValue::FC_Reference, // 0x10 DW_FORM_ref_addr + DWARFFormValue::FC_Reference, // 0x11 DW_FORM_ref1 + DWARFFormValue::FC_Reference, // 0x12 DW_FORM_ref2 + DWARFFormValue::FC_Reference, // 0x13 DW_FORM_ref4 + DWARFFormValue::FC_Reference, // 0x14 DW_FORM_ref8 + DWARFFormValue::FC_Reference, // 0x15 DW_FORM_ref_udata + DWARFFormValue::FC_Indirect, // 0x16 DW_FORM_indirect + DWARFFormValue::FC_SectionOffset, // 0x17 DW_FORM_sec_offset + DWARFFormValue::FC_Exprloc, // 0x18 DW_FORM_exprloc + DWARFFormValue::FC_Flag, // 0x19 DW_FORM_flag_present + DWARFFormValue::FC_String, // 0x1a DW_FORM_strx + DWARFFormValue::FC_Address, // 0x1b DW_FORM_addrx + DWARFFormValue::FC_Reference, // 0x1c DW_FORM_ref_sup4 + DWARFFormValue::FC_String, // 0x1d DW_FORM_strp_sup + DWARFFormValue::FC_Constant, // 0x1e DW_FORM_data16 + DWARFFormValue::FC_String, // 0x1f DW_FORM_line_strp + DWARFFormValue::FC_Reference, // 0x20 DW_FORM_ref_sig8 + DWARFFormValue::FC_Constant, // 0x21 DW_FORM_implicit_const + DWARFFormValue::FC_SectionOffset, // 0x22 DW_FORM_loclistx + DWARFFormValue::FC_SectionOffset, // 0x23 DW_FORM_rnglistx + DWARFFormValue::FC_Reference, // 0x24 DW_FORM_ref_sup8 + DWARFFormValue::FC_String, // 0x25 DW_FORM_strx1 + DWARFFormValue::FC_String, // 0x26 DW_FORM_strx2 + DWARFFormValue::FC_String, // 0x27 DW_FORM_strx3 + DWARFFormValue::FC_String, // 0x28 DW_FORM_strx4 + DWARFFormValue::FC_Address, // 0x29 DW_FORM_addrx1 + DWARFFormValue::FC_Address, // 0x2a DW_FORM_addrx2 + DWARFFormValue::FC_Address, // 0x2b DW_FORM_addrx3 + DWARFFormValue::FC_Address, // 0x2c DW_FORM_addrx4 + +}; + +DWARFFormValue DWARFFormValue::createFromSValue(dwarf::Form F, int64_t V) { + return DWARFFormValue(F, ValueType(V)); +} + +DWARFFormValue DWARFFormValue::createFromUValue(dwarf::Form F, uint64_t V) { + return DWARFFormValue(F, ValueType(V)); +} + +DWARFFormValue DWARFFormValue::createFromPValue(dwarf::Form F, const char *V) { + return DWARFFormValue(F, ValueType(V)); +} + +DWARFFormValue DWARFFormValue::createFromBlockValue(dwarf::Form F, + ArrayRef<uint8_t> D) { + ValueType V; + V.uval = D.size(); + V.data = D.data(); + return DWARFFormValue(F, V); +} + +DWARFFormValue DWARFFormValue::createFromUnit(dwarf::Form F, const DWARFUnit *U, + uint64_t *OffsetPtr) { + DWARFFormValue FormValue(F); + FormValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, + U->getFormParams(), U); + return FormValue; +} + +bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, + uint64_t *OffsetPtr, + const dwarf::FormParams Params) { + bool Indirect = false; + do { + switch (Form) { + // Blocks of inlined data that have a length field and the data bytes + // inlined in the .debug_info. + case DW_FORM_exprloc: + case DW_FORM_block: { + uint64_t size = DebugInfoData.getULEB128(OffsetPtr); + *OffsetPtr += size; + return true; + } + case DW_FORM_block1: { + uint8_t size = DebugInfoData.getU8(OffsetPtr); + *OffsetPtr += size; + return true; + } + case DW_FORM_block2: { + uint16_t size = DebugInfoData.getU16(OffsetPtr); + *OffsetPtr += size; + return true; + } + case DW_FORM_block4: { + uint32_t size = DebugInfoData.getU32(OffsetPtr); + *OffsetPtr += size; + return true; + } + + // Inlined NULL terminated C-strings. + case DW_FORM_string: + DebugInfoData.getCStr(OffsetPtr); + return true; + + case DW_FORM_addr: + case DW_FORM_ref_addr: + case DW_FORM_flag_present: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_data16: + case DW_FORM_flag: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + case DW_FORM_ref_sup4: + case DW_FORM_ref_sup8: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx4: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx4: + case DW_FORM_sec_offset: + case DW_FORM_strp: + case DW_FORM_strp_sup: + case DW_FORM_line_strp: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + if (Optional<uint8_t> FixedSize = + dwarf::getFixedFormByteSize(Form, Params)) { + *OffsetPtr += *FixedSize; + return true; + } + return false; + + // signed or unsigned LEB 128 values. + case DW_FORM_sdata: + DebugInfoData.getSLEB128(OffsetPtr); + return true; + + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_strx: + case DW_FORM_addrx: + case DW_FORM_loclistx: + case DW_FORM_rnglistx: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + DebugInfoData.getULEB128(OffsetPtr); + return true; + + case DW_FORM_indirect: + Indirect = true; + Form = static_cast<dwarf::Form>(DebugInfoData.getULEB128(OffsetPtr)); + break; + + default: + return false; + } + } while (Indirect); + return true; +} + +bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { + // First, check DWARF5 form classes. + if (Form < makeArrayRef(DWARF5FormClasses).size() && + DWARF5FormClasses[Form] == FC) + return true; + // Check more forms from extensions and proposals. + switch (Form) { + case DW_FORM_GNU_ref_alt: + return (FC == FC_Reference); + case DW_FORM_GNU_addr_index: + return (FC == FC_Address); + case DW_FORM_GNU_str_index: + case DW_FORM_GNU_strp_alt: + return (FC == FC_String); + default: + break; + } + + if (FC == FC_SectionOffset) { + if (Form == DW_FORM_strp || Form == DW_FORM_line_strp) + return true; + // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section + // offset. If we don't have a DWARFUnit, default to the old behavior. + if (Form == DW_FORM_data4 || Form == DW_FORM_data8) + return !U || U->getVersion() <= 3; + } + + return false; +} + +bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, + uint64_t *OffsetPtr, dwarf::FormParams FP, + const DWARFContext *Ctx, + const DWARFUnit *CU) { + if (!Ctx && CU) + Ctx = &CU->getContext(); + C = Ctx; + U = CU; + bool Indirect = false; + bool IsBlock = false; + Value.data = nullptr; + // Read the value for the form into value and follow and DW_FORM_indirect + // instances we run into + do { + Indirect = false; + switch (Form) { + case DW_FORM_addr: + case DW_FORM_ref_addr: { + uint16_t Size = + (Form == DW_FORM_addr) ? FP.AddrSize : FP.getRefAddrByteSize(); + Value.uval = Data.getRelocatedValue(Size, OffsetPtr, &Value.SectionIndex); + break; + } + case DW_FORM_exprloc: + case DW_FORM_block: + Value.uval = Data.getULEB128(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_block1: + Value.uval = Data.getU8(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_block2: + Value.uval = Data.getU16(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_block4: + Value.uval = Data.getU32(OffsetPtr); + IsBlock = true; + break; + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_strx1: + case DW_FORM_addrx1: + Value.uval = Data.getU8(OffsetPtr); + break; + case DW_FORM_data2: + case DW_FORM_ref2: + case DW_FORM_strx2: + case DW_FORM_addrx2: + Value.uval = Data.getU16(OffsetPtr); + break; + case DW_FORM_strx3: + Value.uval = Data.getU24(OffsetPtr); + break; + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_ref_sup4: + case DW_FORM_strx4: + case DW_FORM_addrx4: + Value.uval = Data.getRelocatedValue(4, OffsetPtr); + break; + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sup8: + Value.uval = Data.getRelocatedValue(8, OffsetPtr); + break; + case DW_FORM_data16: + // Treat this like a 16-byte block. + Value.uval = 16; + IsBlock = true; + break; + case DW_FORM_sdata: + Value.sval = Data.getSLEB128(OffsetPtr); + break; + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_rnglistx: + Value.uval = Data.getULEB128(OffsetPtr); + break; + case DW_FORM_string: + Value.cstr = Data.getCStr(OffsetPtr); + break; + case DW_FORM_indirect: + Form = static_cast<dwarf::Form>(Data.getULEB128(OffsetPtr)); + Indirect = true; + break; + case DW_FORM_strp: + case DW_FORM_sec_offset: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + case DW_FORM_line_strp: + case DW_FORM_strp_sup: { + Value.uval = + Data.getRelocatedValue(FP.getDwarfOffsetByteSize(), OffsetPtr); + break; + } + case DW_FORM_flag_present: + Value.uval = 1; + break; + case DW_FORM_ref_sig8: + Value.uval = Data.getU64(OffsetPtr); + break; + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + case DW_FORM_addrx: + case DW_FORM_strx: + Value.uval = Data.getULEB128(OffsetPtr); + break; + default: + // DWARFFormValue::skipValue() will have caught this and caused all + // DWARF DIEs to fail to be parsed, so this code is not be reachable. + llvm_unreachable("unsupported form"); + } + } while (Indirect); + + if (IsBlock) { + StringRef Str = Data.getData().substr(*OffsetPtr, Value.uval); + Value.data = nullptr; + if (!Str.empty()) { + Value.data = Str.bytes_begin(); + *OffsetPtr += Value.uval; + } + } + + return true; +} + +void DWARFFormValue::dumpSectionedAddress(raw_ostream &OS, + DIDumpOptions DumpOpts, + object::SectionedAddress SA) const { + OS << format("0x%016" PRIx64, SA.Address); + dumpAddressSection(U->getContext().getDWARFObj(), OS, DumpOpts, + SA.SectionIndex); +} + +void DWARFFormValue::dumpAddressSection(const DWARFObject &Obj, raw_ostream &OS, + DIDumpOptions DumpOpts, + uint64_t SectionIndex) { + if (!DumpOpts.Verbose || SectionIndex == -1ULL) + return; + ArrayRef<SectionName> SectionNames = Obj.getSectionNames(); + const auto &SecRef = SectionNames[SectionIndex]; + + OS << " \"" << SecRef.Name << '\"'; + + // Print section index if name is not unique. + if (!SecRef.IsNameUnique) + OS << format(" [%" PRIu64 "]", SectionIndex); +} + +void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + uint64_t UValue = Value.uval; + bool CURelativeOffset = false; + raw_ostream &AddrOS = DumpOpts.ShowAddresses + ? WithColor(OS, HighlightColor::Address).get() + : nulls(); + switch (Form) { + case DW_FORM_addr: + dumpSectionedAddress(AddrOS, DumpOpts, {Value.uval, Value.SectionIndex}); + break; + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: + case DW_FORM_GNU_addr_index: { + if (U == nullptr) { + OS << "<invalid dwarf unit>"; + break; + } + Optional<object::SectionedAddress> A = U->getAddrOffsetSectionItem(UValue); + if (!A || DumpOpts.Verbose) + AddrOS << format("indexed (%8.8x) address = ", (uint32_t)UValue); + if (A) + dumpSectionedAddress(AddrOS, DumpOpts, *A); + else + OS << "<no .debug_addr section>"; + break; + } + case DW_FORM_flag_present: + OS << "true"; + break; + case DW_FORM_flag: + case DW_FORM_data1: + OS << format("0x%02x", (uint8_t)UValue); + break; + case DW_FORM_data2: + OS << format("0x%04x", (uint16_t)UValue); + break; + case DW_FORM_data4: + OS << format("0x%08x", (uint32_t)UValue); + break; + case DW_FORM_ref_sig8: + AddrOS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_data8: + OS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_data16: + OS << format_bytes(ArrayRef<uint8_t>(Value.data, 16), None, 16, 16); + break; + case DW_FORM_string: + OS << '"'; + OS.write_escaped(Value.cstr); + OS << '"'; + break; + case DW_FORM_exprloc: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (UValue > 0) { + switch (Form) { + case DW_FORM_exprloc: + case DW_FORM_block: + AddrOS << format("<0x%" PRIx64 "> ", UValue); + break; + case DW_FORM_block1: + AddrOS << format("<0x%2.2x> ", (uint8_t)UValue); + break; + case DW_FORM_block2: + AddrOS << format("<0x%4.4x> ", (uint16_t)UValue); + break; + case DW_FORM_block4: + AddrOS << format("<0x%8.8x> ", (uint32_t)UValue); + break; + default: + break; + } + + const uint8_t *DataPtr = Value.data; + if (DataPtr) { + // UValue contains size of block + const uint8_t *EndDataPtr = DataPtr + UValue; + while (DataPtr < EndDataPtr) { + AddrOS << format("%2.2x ", *DataPtr); + ++DataPtr; + } + } else + OS << "NULL"; + } + break; + + case DW_FORM_sdata: + OS << Value.sval; + break; + case DW_FORM_udata: + OS << Value.uval; + break; + case DW_FORM_strp: + if (DumpOpts.Verbose) + OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); + dumpString(OS); + break; + case DW_FORM_line_strp: + if (DumpOpts.Verbose) + OS << format(" .debug_line_str[0x%8.8x] = ", (uint32_t)UValue); + dumpString(OS); + break; + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: + case DW_FORM_GNU_str_index: + if (DumpOpts.Verbose) + OS << format("indexed (%8.8x) string = ", (uint32_t)UValue); + dumpString(OS); + break; + case DW_FORM_GNU_strp_alt: + if (DumpOpts.Verbose) + OS << format("alt indirect string, offset: 0x%" PRIx64 "", UValue); + dumpString(OS); + break; + case DW_FORM_ref_addr: + AddrOS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_ref1: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%2.2x", (uint8_t)UValue); + break; + case DW_FORM_ref2: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%4.4x", (uint16_t)UValue); + break; + case DW_FORM_ref4: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%4.4x", (uint32_t)UValue); + break; + case DW_FORM_ref8: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%8.8" PRIx64, UValue); + break; + case DW_FORM_ref_udata: + CURelativeOffset = true; + if (DumpOpts.Verbose) + AddrOS << format("cu + 0x%" PRIx64, UValue); + break; + case DW_FORM_GNU_ref_alt: + AddrOS << format("<alt 0x%" PRIx64 ">", UValue); + break; + + // All DW_FORM_indirect attributes should be resolved prior to calling + // this function + case DW_FORM_indirect: + OS << "DW_FORM_indirect"; + break; + + case DW_FORM_rnglistx: + OS << format("indexed (0x%x) rangelist = ", (uint32_t)UValue); + break; + + // Should be formatted to 64-bit for DWARF64. + case DW_FORM_sec_offset: + AddrOS << format("0x%08x", (uint32_t)UValue); + break; + + default: + OS << format("DW_FORM(0x%4.4x)", Form); + break; + } + + if (CURelativeOffset) { + if (DumpOpts.Verbose) + OS << " => {"; + if (DumpOpts.ShowAddresses) + WithColor(OS, HighlightColor::Address).get() + << format("0x%8.8" PRIx64, UValue + (U ? U->getOffset() : 0)); + if (DumpOpts.Verbose) + OS << "}"; + } +} + +void DWARFFormValue::dumpString(raw_ostream &OS) const { + Optional<const char *> DbgStr = getAsCString(); + if (DbgStr.hasValue()) { + auto COS = WithColor(OS, HighlightColor::String); + COS.get() << '"'; + COS.get().write_escaped(DbgStr.getValue()); + COS.get() << '"'; + } +} + +Optional<const char *> DWARFFormValue::getAsCString() const { + if (!isFormClass(FC_String)) + return None; + if (Form == DW_FORM_string) + return Value.cstr; + // FIXME: Add support for DW_FORM_GNU_strp_alt + if (Form == DW_FORM_GNU_strp_alt || C == nullptr) + return None; + uint64_t Offset = Value.uval; + if (Form == DW_FORM_line_strp) { + // .debug_line_str is tracked in the Context. + if (const char *Str = C->getLineStringExtractor().getCStr(&Offset)) + return Str; + return None; + } + if (Form == DW_FORM_GNU_str_index || Form == DW_FORM_strx || + Form == DW_FORM_strx1 || Form == DW_FORM_strx2 || Form == DW_FORM_strx3 || + Form == DW_FORM_strx4) { + if (!U) + return None; + Optional<uint64_t> StrOffset = U->getStringOffsetSectionItem(Offset); + if (!StrOffset) + return None; + Offset = *StrOffset; + } + // Prefer the Unit's string extractor, because for .dwo it will point to + // .debug_str.dwo, while the Context's extractor always uses .debug_str. + if (U) { + if (const char *Str = U->getStringExtractor().getCStr(&Offset)) + return Str; + return None; + } + if (const char *Str = C->getStringExtractor().getCStr(&Offset)) + return Str; + return None; +} + +Optional<uint64_t> DWARFFormValue::getAsAddress() const { + if (auto SA = getAsSectionedAddress()) + return SA->Address; + return None; +} + +Optional<object::SectionedAddress> +DWARFFormValue::getAsSectionedAddress() const { + if (!isFormClass(FC_Address)) + return None; + if (Form == DW_FORM_GNU_addr_index || Form == DW_FORM_addrx) { + uint32_t Index = Value.uval; + if (!U) + return None; + Optional<object::SectionedAddress> SA = U->getAddrOffsetSectionItem(Index); + if (!SA) + return None; + return SA; + } + return {{Value.uval, Value.SectionIndex}}; +} + +Optional<uint64_t> DWARFFormValue::getAsReference() const { + if (auto R = getAsRelativeReference()) + return R->Unit ? R->Unit->getOffset() + R->Offset : R->Offset; + return None; +} + +Optional<DWARFFormValue::UnitOffset> DWARFFormValue::getAsRelativeReference() const { + if (!isFormClass(FC_Reference)) + return None; + switch (Form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + if (!U) + return None; + return UnitOffset{const_cast<DWARFUnit*>(U), Value.uval}; + case DW_FORM_ref_addr: + case DW_FORM_ref_sig8: + case DW_FORM_GNU_ref_alt: + return UnitOffset{nullptr, Value.uval}; + default: + return None; + } +} + +Optional<uint64_t> DWARFFormValue::getAsSectionOffset() const { + if (!isFormClass(FC_SectionOffset)) + return None; + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsUnsignedConstant() const { + if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || + Form == DW_FORM_sdata) + return None; + return Value.uval; +} + +Optional<int64_t> DWARFFormValue::getAsSignedConstant() const { + if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || + (Form == DW_FORM_udata && + uint64_t(std::numeric_limits<int64_t>::max()) < Value.uval)) + return None; + switch (Form) { + case DW_FORM_data4: + return int32_t(Value.uval); + case DW_FORM_data2: + return int16_t(Value.uval); + case DW_FORM_data1: + return int8_t(Value.uval); + case DW_FORM_sdata: + case DW_FORM_data8: + default: + return Value.sval; + } +} + +Optional<ArrayRef<uint8_t>> DWARFFormValue::getAsBlock() const { + if (!isFormClass(FC_Block) && !isFormClass(FC_Exprloc) && + Form != DW_FORM_data16) + return None; + return makeArrayRef(Value.data, Value.uval); +} + +Optional<uint64_t> DWARFFormValue::getAsCStringOffset() const { + if (!isFormClass(FC_String) && Form == DW_FORM_string) + return None; + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsReferenceUVal() const { + if (!isFormClass(FC_Reference)) + return None; + return Value.uval; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp b/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp new file mode 100644 index 000000000000..252b58e5a591 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp @@ -0,0 +1,199 @@ +//===- DWARFGdbIndex.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <utility> + +using namespace llvm; + +// .gdb_index section format reference: +// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html + +void DWARFGdbIndex::dumpCUList(raw_ostream &OS) const { + OS << format("\n CU list offset = 0x%x, has %" PRId64 " entries:", + CuListOffset, (uint64_t)CuList.size()) + << '\n'; + uint32_t I = 0; + for (const CompUnitEntry &CU : CuList) + OS << format(" %d: Offset = 0x%llx, Length = 0x%llx\n", I++, CU.Offset, + CU.Length); +} + +void DWARFGdbIndex::dumpTUList(raw_ostream &OS) const { + OS << formatv("\n Types CU list offset = {0:x}, has {1} entries:\n", + TuListOffset, TuList.size()); + uint32_t I = 0; + for (const TypeUnitEntry &TU : TuList) + OS << formatv(" {0}: offset = {1:x8}, type_offset = {2:x8}, " + "type_signature = {3:x16}\n", + I++, TU.Offset, TU.TypeOffset, TU.TypeSignature); +} + +void DWARFGdbIndex::dumpAddressArea(raw_ostream &OS) const { + OS << format("\n Address area offset = 0x%x, has %" PRId64 " entries:", + AddressAreaOffset, (uint64_t)AddressArea.size()) + << '\n'; + for (const AddressEntry &Addr : AddressArea) + OS << format( + " Low/High address = [0x%llx, 0x%llx) (Size: 0x%llx), CU id = %d\n", + Addr.LowAddress, Addr.HighAddress, Addr.HighAddress - Addr.LowAddress, + Addr.CuIndex); +} + +void DWARFGdbIndex::dumpSymbolTable(raw_ostream &OS) const { + OS << format("\n Symbol table offset = 0x%x, size = %" PRId64 + ", filled slots:", + SymbolTableOffset, (uint64_t)SymbolTable.size()) + << '\n'; + uint32_t I = -1; + for (const SymTableEntry &E : SymbolTable) { + ++I; + if (!E.NameOffset && !E.VecOffset) + continue; + + OS << format(" %d: Name offset = 0x%x, CU vector offset = 0x%x\n", I, + E.NameOffset, E.VecOffset); + + StringRef Name = ConstantPoolStrings.substr( + ConstantPoolOffset - StringPoolOffset + E.NameOffset); + + auto CuVector = std::find_if( + ConstantPoolVectors.begin(), ConstantPoolVectors.end(), + [&](const std::pair<uint32_t, SmallVector<uint32_t, 0>> &V) { + return V.first == E.VecOffset; + }); + assert(CuVector != ConstantPoolVectors.end() && "Invalid symbol table"); + uint32_t CuVectorId = CuVector - ConstantPoolVectors.begin(); + OS << format(" String name: %s, CU vector index: %d\n", Name.data(), + CuVectorId); + } +} + +void DWARFGdbIndex::dumpConstantPool(raw_ostream &OS) const { + OS << format("\n Constant pool offset = 0x%x, has %" PRId64 " CU vectors:", + ConstantPoolOffset, (uint64_t)ConstantPoolVectors.size()); + uint32_t I = 0; + for (const auto &V : ConstantPoolVectors) { + OS << format("\n %d(0x%x): ", I++, V.first); + for (uint32_t Val : V.second) + OS << format("0x%x ", Val); + } + OS << '\n'; +} + +void DWARFGdbIndex::dump(raw_ostream &OS) { + if (HasError) { + OS << "\n<error parsing>\n"; + return; + } + + if (HasContent) { + OS << " Version = " << Version << '\n'; + dumpCUList(OS); + dumpTUList(OS); + dumpAddressArea(OS); + dumpSymbolTable(OS); + dumpConstantPool(OS); + } +} + +bool DWARFGdbIndex::parseImpl(DataExtractor Data) { + uint64_t Offset = 0; + + // Only version 7 is supported at this moment. + Version = Data.getU32(&Offset); + if (Version != 7) + return false; + + CuListOffset = Data.getU32(&Offset); + TuListOffset = Data.getU32(&Offset); + AddressAreaOffset = Data.getU32(&Offset); + SymbolTableOffset = Data.getU32(&Offset); + ConstantPoolOffset = Data.getU32(&Offset); + + if (Offset != CuListOffset) + return false; + + uint32_t CuListSize = (TuListOffset - CuListOffset) / 16; + CuList.reserve(CuListSize); + for (uint32_t i = 0; i < CuListSize; ++i) { + uint64_t CuOffset = Data.getU64(&Offset); + uint64_t CuLength = Data.getU64(&Offset); + CuList.push_back({CuOffset, CuLength}); + } + + // CU Types are no longer needed as DWARF skeleton type units never made it + // into the standard. + uint32_t TuListSize = (AddressAreaOffset - TuListOffset) / 24; + TuList.resize(TuListSize); + for (uint32_t I = 0; I < TuListSize; ++I) { + uint64_t CuOffset = Data.getU64(&Offset); + uint64_t TypeOffset = Data.getU64(&Offset); + uint64_t Signature = Data.getU64(&Offset); + TuList[I] = {CuOffset, TypeOffset, Signature}; + } + + uint32_t AddressAreaSize = (SymbolTableOffset - AddressAreaOffset) / 20; + AddressArea.reserve(AddressAreaSize); + for (uint32_t i = 0; i < AddressAreaSize; ++i) { + uint64_t LowAddress = Data.getU64(&Offset); + uint64_t HighAddress = Data.getU64(&Offset); + uint32_t CuIndex = Data.getU32(&Offset); + AddressArea.push_back({LowAddress, HighAddress, CuIndex}); + } + + // The symbol table. This is an open addressed hash table. The size of the + // hash table is always a power of 2. + // Each slot in the hash table consists of a pair of offset_type values. The + // first value is the offset of the symbol's name in the constant pool. The + // second value is the offset of the CU vector in the constant pool. + // If both values are 0, then this slot in the hash table is empty. This is ok + // because while 0 is a valid constant pool index, it cannot be a valid index + // for both a string and a CU vector. + uint32_t SymTableSize = (ConstantPoolOffset - SymbolTableOffset) / 8; + SymbolTable.reserve(SymTableSize); + uint32_t CuVectorsTotal = 0; + for (uint32_t i = 0; i < SymTableSize; ++i) { + uint32_t NameOffset = Data.getU32(&Offset); + uint32_t CuVecOffset = Data.getU32(&Offset); + SymbolTable.push_back({NameOffset, CuVecOffset}); + if (NameOffset || CuVecOffset) + ++CuVectorsTotal; + } + + // The constant pool. CU vectors are stored first, followed by strings. + // The first value is the number of CU indices in the vector. Each subsequent + // value is the index and symbol attributes of a CU in the CU list. + for (uint32_t i = 0; i < CuVectorsTotal; ++i) { + ConstantPoolVectors.emplace_back(0, SmallVector<uint32_t, 0>()); + auto &Vec = ConstantPoolVectors.back(); + Vec.first = Offset - ConstantPoolOffset; + + uint32_t Num = Data.getU32(&Offset); + for (uint32_t j = 0; j < Num; ++j) + Vec.second.push_back(Data.getU32(&Offset)); + } + + ConstantPoolStrings = Data.getData().drop_front(Offset); + StringPoolOffset = Offset; + return true; +} + +void DWARFGdbIndex::parse(DataExtractor Data) { + HasContent = !Data.getData().empty(); + HasError = HasContent && !parseImpl(Data); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp new file mode 100644 index 000000000000..269ea9f79a6e --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp @@ -0,0 +1,116 @@ +//===- DWARFListTable.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFListTable.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +Error DWARFListTableHeader::extract(DWARFDataExtractor Data, + uint64_t *OffsetPtr) { + HeaderOffset = *OffsetPtr; + // Read and verify the length field. + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) + return createStringError(errc::invalid_argument, + "section is not large enough to contain a " + "%s table length at offset 0x%" PRIx64, + SectionName.data(), *OffsetPtr); + Format = dwarf::DwarfFormat::DWARF32; + uint8_t OffsetByteSize = 4; + HeaderData.Length = Data.getRelocatedValue(4, OffsetPtr); + if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { + Format = dwarf::DwarfFormat::DWARF64; + OffsetByteSize = 8; + HeaderData.Length = Data.getU64(OffsetPtr); + } else if (HeaderData.Length >= dwarf::DW_LENGTH_lo_reserved) { + return createStringError(errc::invalid_argument, + "%s table at offset 0x%" PRIx64 + " has unsupported reserved unit length of value 0x%8.8" PRIx64, + SectionName.data(), HeaderOffset, HeaderData.Length); + } + uint64_t FullLength = + HeaderData.Length + dwarf::getUnitLengthFieldByteSize(Format); + assert(FullLength == length()); + if (FullLength < getHeaderSize(Format)) + return createStringError(errc::invalid_argument, + "%s table at offset 0x%" PRIx64 + " has too small length (0x%" PRIx64 + ") to contain a complete header", + SectionName.data(), HeaderOffset, FullLength); + uint64_t End = HeaderOffset + FullLength; + if (!Data.isValidOffsetForDataOfSize(HeaderOffset, FullLength)) + return createStringError(errc::invalid_argument, + "section is not large enough to contain a %s table " + "of length 0x%" PRIx64 " at offset 0x%" PRIx64, + SectionName.data(), FullLength, HeaderOffset); + + HeaderData.Version = Data.getU16(OffsetPtr); + HeaderData.AddrSize = Data.getU8(OffsetPtr); + HeaderData.SegSize = Data.getU8(OffsetPtr); + HeaderData.OffsetEntryCount = Data.getU32(OffsetPtr); + + // Perform basic validation of the remaining header fields. + if (HeaderData.Version != 5) + return createStringError(errc::invalid_argument, + "unrecognised %s table version %" PRIu16 + " in table at offset 0x%" PRIx64, + SectionName.data(), HeaderData.Version, HeaderOffset); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createStringError(errc::not_supported, + "%s table at offset 0x%" PRIx64 + " has unsupported address size %" PRIu8, + SectionName.data(), HeaderOffset, HeaderData.AddrSize); + if (HeaderData.SegSize != 0) + return createStringError(errc::not_supported, + "%s table at offset 0x%" PRIx64 + " has unsupported segment selector size %" PRIu8, + SectionName.data(), HeaderOffset, HeaderData.SegSize); + if (End < HeaderOffset + getHeaderSize(Format) + + HeaderData.OffsetEntryCount * OffsetByteSize) + return createStringError(errc::invalid_argument, + "%s table at offset 0x%" PRIx64 " has more offset entries (%" PRIu32 + ") than there is space for", + SectionName.data(), HeaderOffset, HeaderData.OffsetEntryCount); + Data.setAddressSize(HeaderData.AddrSize); + for (uint32_t I = 0; I < HeaderData.OffsetEntryCount; ++I) + Offsets.push_back(Data.getRelocatedValue(OffsetByteSize, OffsetPtr)); + return Error::success(); +} + +void DWARFListTableHeader::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (DumpOpts.Verbose) + OS << format("0x%8.8" PRIx64 ": ", HeaderOffset); + OS << format( + "%s list header: length = 0x%8.8" PRIx64 ", version = 0x%4.4" PRIx16 ", " + "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 + ", offset_entry_count = " + "0x%8.8" PRIx32 "\n", + ListTypeString.data(), HeaderData.Length, HeaderData.Version, + HeaderData.AddrSize, HeaderData.SegSize, HeaderData.OffsetEntryCount); + + if (HeaderData.OffsetEntryCount > 0) { + OS << "offsets: ["; + for (const auto &Off : Offsets) { + OS << format("\n0x%8.8" PRIx64, Off); + if (DumpOpts.Verbose) + OS << format(" => 0x%8.8" PRIx64, + Off + HeaderOffset + getHeaderSize(Format)); + } + OS << "\n]\n"; + } +} + +uint64_t DWARFListTableHeader::length() const { + if (HeaderData.Length == 0) + return 0; + return HeaderData.Length + dwarf::getUnitLengthFieldByteSize(Format); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp new file mode 100644 index 000000000000..bb81090ba25c --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp @@ -0,0 +1,49 @@ +//===- DWARFTypeUnit.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> + +using namespace llvm; + +void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + DWARFDie TD = getDIEForOffset(getTypeOffset() + getOffset()); + const char *Name = TD.getName(DINameKind::ShortName); + + if (DumpOpts.SummarizeTypes) { + OS << "name = '" << Name << "'" + << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) + << " length = " << format("0x%08" PRIx64, getLength()) << '\n'; + return; + } + + OS << format("0x%08" PRIx64, getOffset()) << ": Type Unit:" + << " length = " << format("0x%08" PRIx64, getLength()) + << " version = " << format("0x%04x", getVersion()); + if (getVersion() >= 5) + OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << " abbr_offset = " + << format("0x%04" PRIx64, getAbbreviations()->getOffset()) + << " addr_size = " << format("0x%02x", getAddressByteSize()) + << " name = '" << Name << "'" + << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) + << " type_offset = " << format("0x%04" PRIx64, getTypeOffset()) + << " (next unit at " << format("0x%08" PRIx64, getNextUnitOffset()) + << ")\n"; + + if (DWARFDie TU = getUnitDIE(false)) + TU.dump(OS, 0, DumpOpts); + else + OS << "<type unit can't be parsed!>\n\n"; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp new file mode 100644 index 000000000000..a56402a707ad --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -0,0 +1,904 @@ +//===- DWARFUnit.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace dwarf; + +void DWARFUnitVector::addUnitsForSection(DWARFContext &C, + const DWARFSection &Section, + DWARFSectionKind SectionKind) { + const DWARFObject &D = C.getDWARFObj(); + addUnitsImpl(C, D, Section, C.getDebugAbbrev(), &D.getRangesSection(), + &D.getLocSection(), D.getStrSection(), + D.getStrOffsetsSection(), &D.getAddrSection(), + D.getLineSection(), D.isLittleEndian(), false, false, + SectionKind); +} + +void DWARFUnitVector::addUnitsForDWOSection(DWARFContext &C, + const DWARFSection &DWOSection, + DWARFSectionKind SectionKind, + bool Lazy) { + const DWARFObject &D = C.getDWARFObj(); + addUnitsImpl(C, D, DWOSection, C.getDebugAbbrevDWO(), &D.getRangesDWOSection(), + &D.getLocDWOSection(), D.getStrDWOSection(), + D.getStrOffsetsDWOSection(), &D.getAddrSection(), + D.getLineDWOSection(), C.isLittleEndian(), true, Lazy, + SectionKind); +} + +void DWARFUnitVector::addUnitsImpl( + DWARFContext &Context, const DWARFObject &Obj, const DWARFSection &Section, + const DWARFDebugAbbrev *DA, const DWARFSection *RS, + const DWARFSection *LocSection, StringRef SS, const DWARFSection &SOS, + const DWARFSection *AOS, const DWARFSection &LS, bool LE, bool IsDWO, + bool Lazy, DWARFSectionKind SectionKind) { + DWARFDataExtractor Data(Obj, Section, LE, 0); + // Lazy initialization of Parser, now that we have all section info. + if (!Parser) { + Parser = [=, &Context, &Obj, &Section, &SOS, + &LS](uint64_t Offset, DWARFSectionKind SectionKind, + const DWARFSection *CurSection, + const DWARFUnitIndex::Entry *IndexEntry) + -> std::unique_ptr<DWARFUnit> { + const DWARFSection &InfoSection = CurSection ? *CurSection : Section; + DWARFDataExtractor Data(Obj, InfoSection, LE, 0); + if (!Data.isValidOffset(Offset)) + return nullptr; + const DWARFUnitIndex *Index = nullptr; + if (IsDWO) + Index = &getDWARFUnitIndex(Context, SectionKind); + DWARFUnitHeader Header; + if (!Header.extract(Context, Data, &Offset, SectionKind, Index, + IndexEntry)) + return nullptr; + std::unique_ptr<DWARFUnit> U; + if (Header.isTypeUnit()) + U = std::make_unique<DWARFTypeUnit>(Context, InfoSection, Header, DA, + RS, LocSection, SS, SOS, AOS, LS, + LE, IsDWO, *this); + else + U = std::make_unique<DWARFCompileUnit>(Context, InfoSection, Header, + DA, RS, LocSection, SS, SOS, + AOS, LS, LE, IsDWO, *this); + return U; + }; + } + if (Lazy) + return; + // Find a reasonable insertion point within the vector. We skip over + // (a) units from a different section, (b) units from the same section + // but with lower offset-within-section. This keeps units in order + // within a section, although not necessarily within the object file, + // even if we do lazy parsing. + auto I = this->begin(); + uint64_t Offset = 0; + while (Data.isValidOffset(Offset)) { + if (I != this->end() && + (&(*I)->getInfoSection() != &Section || (*I)->getOffset() == Offset)) { + ++I; + continue; + } + auto U = Parser(Offset, SectionKind, &Section, nullptr); + // If parsing failed, we're done with this section. + if (!U) + break; + Offset = U->getNextUnitOffset(); + I = std::next(this->insert(I, std::move(U))); + } +} + +DWARFUnit *DWARFUnitVector::addUnit(std::unique_ptr<DWARFUnit> Unit) { + auto I = std::upper_bound(begin(), end(), Unit, + [](const std::unique_ptr<DWARFUnit> &LHS, + const std::unique_ptr<DWARFUnit> &RHS) { + return LHS->getOffset() < RHS->getOffset(); + }); + return this->insert(I, std::move(Unit))->get(); +} + +DWARFUnit *DWARFUnitVector::getUnitForOffset(uint64_t Offset) const { + auto end = begin() + getNumInfoUnits(); + auto *CU = + std::upper_bound(begin(), end, Offset, + [](uint64_t LHS, const std::unique_ptr<DWARFUnit> &RHS) { + return LHS < RHS->getNextUnitOffset(); + }); + if (CU != end && (*CU)->getOffset() <= Offset) + return CU->get(); + return nullptr; +} + +DWARFUnit * +DWARFUnitVector::getUnitForIndexEntry(const DWARFUnitIndex::Entry &E) { + const auto *CUOff = E.getOffset(DW_SECT_INFO); + if (!CUOff) + return nullptr; + + auto Offset = CUOff->Offset; + auto end = begin() + getNumInfoUnits(); + + auto *CU = + std::upper_bound(begin(), end, CUOff->Offset, + [](uint64_t LHS, const std::unique_ptr<DWARFUnit> &RHS) { + return LHS < RHS->getNextUnitOffset(); + }); + if (CU != end && (*CU)->getOffset() <= Offset) + return CU->get(); + + if (!Parser) + return nullptr; + + auto U = Parser(Offset, DW_SECT_INFO, nullptr, &E); + if (!U) + U = nullptr; + + auto *NewCU = U.get(); + this->insert(CU, std::move(U)); + ++NumInfoUnits; + return NewCU; +} + +DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, + const DWARFUnitHeader &Header, const DWARFDebugAbbrev *DA, + const DWARFSection *RS, const DWARFSection *LocSection, + StringRef SS, const DWARFSection &SOS, + const DWARFSection *AOS, const DWARFSection &LS, bool LE, + bool IsDWO, const DWARFUnitVector &UnitVector) + : Context(DC), InfoSection(Section), Header(Header), Abbrev(DA), + RangeSection(RS), LocSection(LocSection), LineSection(LS), + StringSection(SS), StringOffsetSection(SOS), AddrOffsetSection(AOS), + isLittleEndian(LE), IsDWO(IsDWO), UnitVector(UnitVector) { + clear(); + // For split DWARF we only need to keep track of the location list section's + // data (no relocations), and if we are reading a package file, we need to + // adjust the location list data based on the index entries. + if (IsDWO) { + LocSectionData = LocSection->Data; + if (auto *IndexEntry = Header.getIndexEntry()) + if (const auto *C = IndexEntry->getOffset(DW_SECT_LOC)) + LocSectionData = LocSectionData.substr(C->Offset, C->Length); + } +} + +DWARFUnit::~DWARFUnit() = default; + +DWARFDataExtractor DWARFUnit::getDebugInfoExtractor() const { + return DWARFDataExtractor(Context.getDWARFObj(), InfoSection, isLittleEndian, + getAddressByteSize()); +} + +Optional<object::SectionedAddress> +DWARFUnit::getAddrOffsetSectionItem(uint32_t Index) const { + if (IsDWO) { + auto R = Context.info_section_units(); + auto I = R.begin(); + // Surprising if a DWO file has more than one skeleton unit in it - this + // probably shouldn't be valid, but if a use case is found, here's where to + // support it (probably have to linearly search for the matching skeleton CU + // here) + if (I != R.end() && std::next(I) == R.end()) + return (*I)->getAddrOffsetSectionItem(Index); + } + uint64_t Offset = AddrOffsetSectionBase + Index * getAddressByteSize(); + if (AddrOffsetSection->Data.size() < Offset + getAddressByteSize()) + return None; + DWARFDataExtractor DA(Context.getDWARFObj(), *AddrOffsetSection, + isLittleEndian, getAddressByteSize()); + uint64_t Section; + uint64_t Address = DA.getRelocatedAddress(&Offset, &Section); + return {{Address, Section}}; +} + +Optional<uint64_t> DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { + if (!StringOffsetsTableContribution) + return None; + unsigned ItemSize = getDwarfStringOffsetsByteSize(); + uint64_t Offset = getStringOffsetsBase() + Index * ItemSize; + if (StringOffsetSection.Data.size() < Offset + ItemSize) + return None; + DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, + isLittleEndian, 0); + return DA.getRelocatedValue(ItemSize, &Offset); +} + +bool DWARFUnitHeader::extract(DWARFContext &Context, + const DWARFDataExtractor &debug_info, + uint64_t *offset_ptr, + DWARFSectionKind SectionKind, + const DWARFUnitIndex *Index, + const DWARFUnitIndex::Entry *Entry) { + Offset = *offset_ptr; + IndexEntry = Entry; + if (!IndexEntry && Index) + IndexEntry = Index->getFromOffset(*offset_ptr); + Length = debug_info.getRelocatedValue(4, offset_ptr); + FormParams.Format = DWARF32; + if (Length == dwarf::DW_LENGTH_DWARF64) { + Length = debug_info.getU64(offset_ptr); + FormParams.Format = DWARF64; + } + FormParams.Version = debug_info.getU16(offset_ptr); + if (FormParams.Version >= 5) { + UnitType = debug_info.getU8(offset_ptr); + FormParams.AddrSize = debug_info.getU8(offset_ptr); + AbbrOffset = debug_info.getRelocatedValue(FormParams.getDwarfOffsetByteSize(), offset_ptr); + } else { + AbbrOffset = debug_info.getRelocatedValue(FormParams.getDwarfOffsetByteSize(), offset_ptr); + FormParams.AddrSize = debug_info.getU8(offset_ptr); + // Fake a unit type based on the section type. This isn't perfect, + // but distinguishing compile and type units is generally enough. + if (SectionKind == DW_SECT_TYPES) + UnitType = DW_UT_type; + else + UnitType = DW_UT_compile; + } + if (IndexEntry) { + if (AbbrOffset) + return false; + auto *UnitContrib = IndexEntry->getOffset(); + if (!UnitContrib || UnitContrib->Length != (Length + 4)) + return false; + auto *AbbrEntry = IndexEntry->getOffset(DW_SECT_ABBREV); + if (!AbbrEntry) + return false; + AbbrOffset = AbbrEntry->Offset; + } + if (isTypeUnit()) { + TypeHash = debug_info.getU64(offset_ptr); + TypeOffset = + debug_info.getUnsigned(offset_ptr, FormParams.getDwarfOffsetByteSize()); + } else if (UnitType == DW_UT_split_compile || UnitType == DW_UT_skeleton) + DWOId = debug_info.getU64(offset_ptr); + + // Header fields all parsed, capture the size of this unit header. + assert(*offset_ptr - Offset <= 255 && "unexpected header size"); + Size = uint8_t(*offset_ptr - Offset); + + // Type offset is unit-relative; should be after the header and before + // the end of the current unit. + bool TypeOffsetOK = + !isTypeUnit() + ? true + : TypeOffset >= Size && + TypeOffset < getLength() + getUnitLengthFieldByteSize(); + bool LengthOK = debug_info.isValidOffset(getNextUnitOffset() - 1); + bool VersionOK = DWARFContext::isSupportedVersion(getVersion()); + bool AddrSizeOK = getAddressByteSize() == 4 || getAddressByteSize() == 8; + + if (!LengthOK || !VersionOK || !AddrSizeOK || !TypeOffsetOK) + return false; + + // Keep track of the highest DWARF version we encounter across all units. + Context.setMaxVersionIfGreater(getVersion()); + return true; +} + +// Parse the rangelist table header, including the optional array of offsets +// following it (DWARF v5 and later). +static Expected<DWARFDebugRnglistTable> +parseRngListTableHeader(DWARFDataExtractor &DA, uint64_t Offset, + DwarfFormat Format) { + // We are expected to be called with Offset 0 or pointing just past the table + // header. Correct Offset in the latter case so that it points to the start + // of the header. + if (Offset > 0) { + uint64_t HeaderSize = DWARFListTableHeader::getHeaderSize(Format); + if (Offset < HeaderSize) + return createStringError(errc::invalid_argument, "Did not detect a valid" + " range list table with base = 0x%" PRIx64 "\n", + Offset); + Offset -= HeaderSize; + } + llvm::DWARFDebugRnglistTable Table; + if (Error E = Table.extractHeaderAndOffsets(DA, &Offset)) + return std::move(E); + return Table; +} + +Error DWARFUnit::extractRangeList(uint64_t RangeListOffset, + DWARFDebugRangeList &RangeList) const { + // Require that compile unit is extracted. + assert(!DieArray.empty()); + DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, + isLittleEndian, getAddressByteSize()); + uint64_t ActualRangeListOffset = RangeSectionBase + RangeListOffset; + return RangeList.extract(RangesData, &ActualRangeListOffset); +} + +void DWARFUnit::clear() { + Abbrevs = nullptr; + BaseAddr.reset(); + RangeSectionBase = 0; + AddrOffsetSectionBase = 0; + clearDIEs(false); + DWO.reset(); +} + +const char *DWARFUnit::getCompilationDir() { + return dwarf::toString(getUnitDIE().find(DW_AT_comp_dir), nullptr); +} + +void DWARFUnit::extractDIEsToVector( + bool AppendCUDie, bool AppendNonCUDies, + std::vector<DWARFDebugInfoEntry> &Dies) const { + if (!AppendCUDie && !AppendNonCUDies) + return; + + // Set the offset to that of the first DIE and calculate the start of the + // next compilation unit header. + uint64_t DIEOffset = getOffset() + getHeaderSize(); + uint64_t NextCUOffset = getNextUnitOffset(); + DWARFDebugInfoEntry DIE; + DWARFDataExtractor DebugInfoData = getDebugInfoExtractor(); + uint32_t Depth = 0; + bool IsCUDie = true; + + while (DIE.extractFast(*this, &DIEOffset, DebugInfoData, NextCUOffset, + Depth)) { + if (IsCUDie) { + if (AppendCUDie) + Dies.push_back(DIE); + if (!AppendNonCUDies) + break; + // The average bytes per DIE entry has been seen to be + // around 14-20 so let's pre-reserve the needed memory for + // our DIE entries accordingly. + Dies.reserve(Dies.size() + getDebugInfoSize() / 14); + IsCUDie = false; + } else { + Dies.push_back(DIE); + } + + if (const DWARFAbbreviationDeclaration *AbbrDecl = + DIE.getAbbreviationDeclarationPtr()) { + // Normal DIE + if (AbbrDecl->hasChildren()) + ++Depth; + } else { + // NULL DIE. + if (Depth > 0) + --Depth; + if (Depth == 0) + break; // We are done with this compile unit! + } + } + + // Give a little bit of info if we encounter corrupt DWARF (our offset + // should always terminate at or before the start of the next compilation + // unit header). + if (DIEOffset > NextCUOffset) + WithColor::warning() << format("DWARF compile unit extends beyond its " + "bounds cu 0x%8.8" PRIx64 " " + "at 0x%8.8" PRIx64 "\n", + getOffset(), DIEOffset); +} + +void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { + if (Error e = tryExtractDIEsIfNeeded(CUDieOnly)) + WithColor::error() << toString(std::move(e)); +} + +Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { + if ((CUDieOnly && !DieArray.empty()) || + DieArray.size() > 1) + return Error::success(); // Already parsed. + + bool HasCUDie = !DieArray.empty(); + extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray); + + if (DieArray.empty()) + return Error::success(); + + // If CU DIE was just parsed, copy several attribute values from it. + if (HasCUDie) + return Error::success(); + + DWARFDie UnitDie(this, &DieArray[0]); + if (Optional<uint64_t> DWOId = toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id))) + Header.setDWOId(*DWOId); + if (!IsDWO) { + assert(AddrOffsetSectionBase == 0); + assert(RangeSectionBase == 0); + AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base), 0); + if (!AddrOffsetSectionBase) + AddrOffsetSectionBase = + toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); + RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); + } + + // In general, in DWARF v5 and beyond we derive the start of the unit's + // contribution to the string offsets table from the unit DIE's + // DW_AT_str_offsets_base attribute. Split DWARF units do not use this + // attribute, so we assume that there is a contribution to the string + // offsets table starting at offset 0 of the debug_str_offsets.dwo section. + // In both cases we need to determine the format of the contribution, + // which may differ from the unit's format. + DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, + isLittleEndian, 0); + if (IsDWO || getVersion() >= 5) { + auto StringOffsetOrError = + IsDWO ? determineStringOffsetsTableContributionDWO(DA) + : determineStringOffsetsTableContribution(DA); + if (!StringOffsetOrError) + return createStringError(errc::invalid_argument, + "invalid reference to or invalid content in " + ".debug_str_offsets[.dwo]: " + + toString(StringOffsetOrError.takeError())); + + StringOffsetsTableContribution = *StringOffsetOrError; + } + + // DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to + // describe address ranges. + if (getVersion() >= 5) { + if (IsDWO) + setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); + else + setRangesSection(&Context.getDWARFObj().getRnglistsSection(), + toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0)); + if (RangeSection->Data.size()) { + // Parse the range list table header. Individual range lists are + // extracted lazily. + DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, + isLittleEndian, 0); + auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase, + Header.getFormat()); + if (!TableOrError) + return createStringError(errc::invalid_argument, + "parsing a range list table: " + + toString(TableOrError.takeError())); + + RngListTable = TableOrError.get(); + + // In a split dwarf unit, there is no DW_AT_rnglists_base attribute. + // Adjust RangeSectionBase to point past the table header. + if (IsDWO && RngListTable) + RangeSectionBase = RngListTable->getHeaderSize(); + } + } + + // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for + // skeleton CU DIE, so that DWARF users not aware of it are not broken. + return Error::success(); +} + +bool DWARFUnit::parseDWO() { + if (IsDWO) + return false; + if (DWO.get()) + return false; + DWARFDie UnitDie = getUnitDIE(); + if (!UnitDie) + return false; + auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); + if (!DWOFileName) + return false; + auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); + SmallString<16> AbsolutePath; + if (sys::path::is_relative(*DWOFileName) && CompilationDir && + *CompilationDir) { + sys::path::append(AbsolutePath, *CompilationDir); + } + sys::path::append(AbsolutePath, *DWOFileName); + auto DWOId = getDWOId(); + if (!DWOId) + return false; + auto DWOContext = Context.getDWOContext(AbsolutePath); + if (!DWOContext) + return false; + + DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); + if (!DWOCU) + return false; + DWO = std::shared_ptr<DWARFCompileUnit>(std::move(DWOContext), DWOCU); + // Share .debug_addr and .debug_ranges section with compile unit in .dwo + DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); + if (getVersion() >= 5) { + DWO->setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); + DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, + isLittleEndian, 0); + if (auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase, + Header.getFormat())) + DWO->RngListTable = TableOrError.get(); + else + WithColor::error() << "parsing a range list table: " + << toString(TableOrError.takeError()) + << '\n'; + if (DWO->RngListTable) + DWO->RangeSectionBase = DWO->RngListTable->getHeaderSize(); + } else { + auto DWORangesBase = UnitDie.getRangesBaseAttribute(); + DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); + } + + return true; +} + +void DWARFUnit::clearDIEs(bool KeepCUDie) { + if (DieArray.size() > (unsigned)KeepCUDie) { + DieArray.resize((unsigned)KeepCUDie); + DieArray.shrink_to_fit(); + } +} + +Expected<DWARFAddressRangesVector> +DWARFUnit::findRnglistFromOffset(uint64_t Offset) { + if (getVersion() <= 4) { + DWARFDebugRangeList RangeList; + if (Error E = extractRangeList(Offset, RangeList)) + return std::move(E); + return RangeList.getAbsoluteRanges(getBaseAddress()); + } + if (RngListTable) { + DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, + isLittleEndian, RngListTable->getAddrSize()); + auto RangeListOrError = RngListTable->findList(RangesData, Offset); + if (RangeListOrError) + return RangeListOrError.get().getAbsoluteRanges(getBaseAddress(), *this); + return RangeListOrError.takeError(); + } + + return createStringError(errc::invalid_argument, + "missing or invalid range list table"); +} + +Expected<DWARFAddressRangesVector> +DWARFUnit::findRnglistFromIndex(uint32_t Index) { + if (auto Offset = getRnglistOffset(Index)) + return findRnglistFromOffset(*Offset + RangeSectionBase); + + if (RngListTable) + return createStringError(errc::invalid_argument, + "invalid range list table index %d", Index); + + return createStringError(errc::invalid_argument, + "missing or invalid range list table"); +} + +Expected<DWARFAddressRangesVector> DWARFUnit::collectAddressRanges() { + DWARFDie UnitDie = getUnitDIE(); + if (!UnitDie) + return createStringError(errc::invalid_argument, "No unit DIE"); + + // First, check if unit DIE describes address ranges for the whole unit. + auto CUDIERangesOrError = UnitDie.getAddressRanges(); + if (!CUDIERangesOrError) + return createStringError(errc::invalid_argument, + "decoding address ranges: %s", + toString(CUDIERangesOrError.takeError()).c_str()); + return *CUDIERangesOrError; +} + +void DWARFUnit::updateAddressDieMap(DWARFDie Die) { + if (Die.isSubroutineDIE()) { + auto DIERangesOrError = Die.getAddressRanges(); + if (DIERangesOrError) { + for (const auto &R : DIERangesOrError.get()) { + // Ignore 0-sized ranges. + if (R.LowPC == R.HighPC) + continue; + auto B = AddrDieMap.upper_bound(R.LowPC); + if (B != AddrDieMap.begin() && R.LowPC < (--B)->second.first) { + // The range is a sub-range of existing ranges, we need to split the + // existing range. + if (R.HighPC < B->second.first) + AddrDieMap[R.HighPC] = B->second; + if (R.LowPC > B->first) + AddrDieMap[B->first].first = R.LowPC; + } + AddrDieMap[R.LowPC] = std::make_pair(R.HighPC, Die); + } + } else + llvm::consumeError(DIERangesOrError.takeError()); + } + // Parent DIEs are added to the AddrDieMap prior to the Children DIEs to + // simplify the logic to update AddrDieMap. The child's range will always + // be equal or smaller than the parent's range. With this assumption, when + // adding one range into the map, it will at most split a range into 3 + // sub-ranges. + for (DWARFDie Child = Die.getFirstChild(); Child; Child = Child.getSibling()) + updateAddressDieMap(Child); +} + +DWARFDie DWARFUnit::getSubroutineForAddress(uint64_t Address) { + extractDIEsIfNeeded(false); + if (AddrDieMap.empty()) + updateAddressDieMap(getUnitDIE()); + auto R = AddrDieMap.upper_bound(Address); + if (R == AddrDieMap.begin()) + return DWARFDie(); + // upper_bound's previous item contains Address. + --R; + if (Address >= R->second.first) + return DWARFDie(); + return R->second.second; +} + +void +DWARFUnit::getInlinedChainForAddress(uint64_t Address, + SmallVectorImpl<DWARFDie> &InlinedChain) { + assert(InlinedChain.empty()); + // Try to look for subprogram DIEs in the DWO file. + parseDWO(); + // First, find the subroutine that contains the given address (the leaf + // of inlined chain). + DWARFDie SubroutineDIE = + (DWO ? *DWO : *this).getSubroutineForAddress(Address); + + if (!SubroutineDIE) + return; + + while (!SubroutineDIE.isSubprogramDIE()) { + if (SubroutineDIE.getTag() == DW_TAG_inlined_subroutine) + InlinedChain.push_back(SubroutineDIE); + SubroutineDIE = SubroutineDIE.getParent(); + } + InlinedChain.push_back(SubroutineDIE); +} + +const DWARFUnitIndex &llvm::getDWARFUnitIndex(DWARFContext &Context, + DWARFSectionKind Kind) { + if (Kind == DW_SECT_INFO) + return Context.getCUIndex(); + assert(Kind == DW_SECT_TYPES); + return Context.getTUIndex(); +} + +DWARFDie DWARFUnit::getParent(const DWARFDebugInfoEntry *Die) { + if (!Die) + return DWARFDie(); + const uint32_t Depth = Die->getDepth(); + // Unit DIEs always have a depth of zero and never have parents. + if (Depth == 0) + return DWARFDie(); + // Depth of 1 always means parent is the compile/type unit. + if (Depth == 1) + return getUnitDIE(); + // Look for previous DIE with a depth that is one less than the Die's depth. + const uint32_t ParentDepth = Depth - 1; + for (uint32_t I = getDIEIndex(Die) - 1; I > 0; --I) { + if (DieArray[I].getDepth() == ParentDepth) + return DWARFDie(this, &DieArray[I]); + } + return DWARFDie(); +} + +DWARFDie DWARFUnit::getSibling(const DWARFDebugInfoEntry *Die) { + if (!Die) + return DWARFDie(); + uint32_t Depth = Die->getDepth(); + // Unit DIEs always have a depth of zero and never have siblings. + if (Depth == 0) + return DWARFDie(); + // NULL DIEs don't have siblings. + if (Die->getAbbreviationDeclarationPtr() == nullptr) + return DWARFDie(); + + // Find the next DIE whose depth is the same as the Die's depth. + for (size_t I = getDIEIndex(Die) + 1, EndIdx = DieArray.size(); I < EndIdx; + ++I) { + if (DieArray[I].getDepth() == Depth) + return DWARFDie(this, &DieArray[I]); + } + return DWARFDie(); +} + +DWARFDie DWARFUnit::getPreviousSibling(const DWARFDebugInfoEntry *Die) { + if (!Die) + return DWARFDie(); + uint32_t Depth = Die->getDepth(); + // Unit DIEs always have a depth of zero and never have siblings. + if (Depth == 0) + return DWARFDie(); + + // Find the previous DIE whose depth is the same as the Die's depth. + for (size_t I = getDIEIndex(Die); I > 0;) { + --I; + if (DieArray[I].getDepth() == Depth - 1) + return DWARFDie(); + if (DieArray[I].getDepth() == Depth) + return DWARFDie(this, &DieArray[I]); + } + return DWARFDie(); +} + +DWARFDie DWARFUnit::getFirstChild(const DWARFDebugInfoEntry *Die) { + if (!Die->hasChildren()) + return DWARFDie(); + + // We do not want access out of bounds when parsing corrupted debug data. + size_t I = getDIEIndex(Die) + 1; + if (I >= DieArray.size()) + return DWARFDie(); + return DWARFDie(this, &DieArray[I]); +} + +DWARFDie DWARFUnit::getLastChild(const DWARFDebugInfoEntry *Die) { + if (!Die->hasChildren()) + return DWARFDie(); + + uint32_t Depth = Die->getDepth(); + for (size_t I = getDIEIndex(Die) + 1, EndIdx = DieArray.size(); I < EndIdx; + ++I) { + if (DieArray[I].getDepth() == Depth + 1 && + DieArray[I].getTag() == dwarf::DW_TAG_null) + return DWARFDie(this, &DieArray[I]); + assert(DieArray[I].getDepth() > Depth && "Not processing children?"); + } + return DWARFDie(); +} + +const DWARFAbbreviationDeclarationSet *DWARFUnit::getAbbreviations() const { + if (!Abbrevs) + Abbrevs = Abbrev->getAbbreviationDeclarationSet(Header.getAbbrOffset()); + return Abbrevs; +} + +llvm::Optional<object::SectionedAddress> DWARFUnit::getBaseAddress() { + if (BaseAddr) + return BaseAddr; + + DWARFDie UnitDie = getUnitDIE(); + Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); + BaseAddr = toSectionedAddress(PC); + return BaseAddr; +} + +Expected<StrOffsetsContributionDescriptor> +StrOffsetsContributionDescriptor::validateContributionSize( + DWARFDataExtractor &DA) { + uint8_t EntrySize = getDwarfOffsetByteSize(); + // In order to ensure that we don't read a partial record at the end of + // the section we validate for a multiple of the entry size. + uint64_t ValidationSize = alignTo(Size, EntrySize); + // Guard against overflow. + if (ValidationSize >= Size) + if (DA.isValidOffsetForDataOfSize((uint32_t)Base, ValidationSize)) + return *this; + return createStringError(errc::invalid_argument, "length exceeds section size"); +} + +// Look for a DWARF64-formatted contribution to the string offsets table +// starting at a given offset and record it in a descriptor. +static Expected<StrOffsetsContributionDescriptor> +parseDWARF64StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) { + if (!DA.isValidOffsetForDataOfSize(Offset, 16)) + return createStringError(errc::invalid_argument, "section offset exceeds section size"); + + if (DA.getU32(&Offset) != dwarf::DW_LENGTH_DWARF64) + return createStringError(errc::invalid_argument, "32 bit contribution referenced from a 64 bit unit"); + + uint64_t Size = DA.getU64(&Offset); + uint8_t Version = DA.getU16(&Offset); + (void)DA.getU16(&Offset); // padding + // The encoded length includes the 2-byte version field and the 2-byte + // padding, so we need to subtract them out when we populate the descriptor. + return StrOffsetsContributionDescriptor(Offset, Size - 4, Version, DWARF64); +} + +// Look for a DWARF32-formatted contribution to the string offsets table +// starting at a given offset and record it in a descriptor. +static Expected<StrOffsetsContributionDescriptor> +parseDWARF32StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) { + if (!DA.isValidOffsetForDataOfSize(Offset, 8)) + return createStringError(errc::invalid_argument, "section offset exceeds section size"); + + uint32_t ContributionSize = DA.getU32(&Offset); + if (ContributionSize >= dwarf::DW_LENGTH_lo_reserved) + return createStringError(errc::invalid_argument, "invalid length"); + + uint8_t Version = DA.getU16(&Offset); + (void)DA.getU16(&Offset); // padding + // The encoded length includes the 2-byte version field and the 2-byte + // padding, so we need to subtract them out when we populate the descriptor. + return StrOffsetsContributionDescriptor(Offset, ContributionSize - 4, Version, + DWARF32); +} + +static Expected<StrOffsetsContributionDescriptor> +parseDWARFStringOffsetsTableHeader(DWARFDataExtractor &DA, + llvm::dwarf::DwarfFormat Format, + uint64_t Offset) { + StrOffsetsContributionDescriptor Desc; + switch (Format) { + case dwarf::DwarfFormat::DWARF64: { + if (Offset < 16) + return createStringError(errc::invalid_argument, "insufficient space for 64 bit header prefix"); + auto DescOrError = parseDWARF64StringOffsetsTableHeader(DA, Offset - 16); + if (!DescOrError) + return DescOrError.takeError(); + Desc = *DescOrError; + break; + } + case dwarf::DwarfFormat::DWARF32: { + if (Offset < 8) + return createStringError(errc::invalid_argument, "insufficient space for 32 bit header prefix"); + auto DescOrError = parseDWARF32StringOffsetsTableHeader(DA, Offset - 8); + if (!DescOrError) + return DescOrError.takeError(); + Desc = *DescOrError; + break; + } + } + return Desc.validateContributionSize(DA); +} + +Expected<Optional<StrOffsetsContributionDescriptor>> +DWARFUnit::determineStringOffsetsTableContribution(DWARFDataExtractor &DA) { + uint64_t Offset; + if (IsDWO) { + Offset = 0; + if (DA.getData().data() == nullptr) + return None; + } else { + auto OptOffset = toSectionOffset(getUnitDIE().find(DW_AT_str_offsets_base)); + if (!OptOffset) + return None; + Offset = *OptOffset; + } + auto DescOrError = parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset); + if (!DescOrError) + return DescOrError.takeError(); + return *DescOrError; +} + +Expected<Optional<StrOffsetsContributionDescriptor>> +DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor & DA) { + uint64_t Offset = 0; + auto IndexEntry = Header.getIndexEntry(); + const auto *C = + IndexEntry ? IndexEntry->getOffset(DW_SECT_STR_OFFSETS) : nullptr; + if (C) + Offset = C->Offset; + if (getVersion() >= 5) { + if (DA.getData().data() == nullptr) + return None; + Offset += Header.getFormat() == dwarf::DwarfFormat::DWARF32 ? 8 : 16; + // Look for a valid contribution at the given offset. + auto DescOrError = parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset); + if (!DescOrError) + return DescOrError.takeError(); + return *DescOrError; + } + // Prior to DWARF v5, we derive the contribution size from the + // index table (in a package file). In a .dwo file it is simply + // the length of the string offsets section. + if (!IndexEntry) + return { + Optional<StrOffsetsContributionDescriptor>( + {0, StringOffsetSection.Data.size(), 4, DWARF32})}; + if (C) + return {Optional<StrOffsetsContributionDescriptor>( + {C->Offset, C->Length, 4, DWARF32})}; + return None; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp new file mode 100644 index 000000000000..f29c1e6cc5c7 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp @@ -0,0 +1,200 @@ +//===- DWARFUnitIndex.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +bool DWARFUnitIndex::Header::parse(DataExtractor IndexData, + uint64_t *OffsetPtr) { + if (!IndexData.isValidOffsetForDataOfSize(*OffsetPtr, 16)) + return false; + Version = IndexData.getU32(OffsetPtr); + NumColumns = IndexData.getU32(OffsetPtr); + NumUnits = IndexData.getU32(OffsetPtr); + NumBuckets = IndexData.getU32(OffsetPtr); + return Version <= 2; +} + +void DWARFUnitIndex::Header::dump(raw_ostream &OS) const { + OS << format("version = %u slots = %u\n\n", Version, NumBuckets); +} + +bool DWARFUnitIndex::parse(DataExtractor IndexData) { + bool b = parseImpl(IndexData); + if (!b) { + // Make sure we don't try to dump anything + Header.NumBuckets = 0; + // Release any partially initialized data. + ColumnKinds.reset(); + Rows.reset(); + } + return b; +} + +bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { + uint64_t Offset = 0; + if (!Header.parse(IndexData, &Offset)) + return false; + + if (!IndexData.isValidOffsetForDataOfSize( + Offset, Header.NumBuckets * (8 + 4) + + (2 * Header.NumUnits + 1) * 4 * Header.NumColumns)) + return false; + + Rows = std::make_unique<Entry[]>(Header.NumBuckets); + auto Contribs = + std::make_unique<Entry::SectionContribution *[]>(Header.NumUnits); + ColumnKinds = std::make_unique<DWARFSectionKind[]>(Header.NumColumns); + + // Read Hash Table of Signatures + for (unsigned i = 0; i != Header.NumBuckets; ++i) + Rows[i].Signature = IndexData.getU64(&Offset); + + // Read Parallel Table of Indexes + for (unsigned i = 0; i != Header.NumBuckets; ++i) { + auto Index = IndexData.getU32(&Offset); + if (!Index) + continue; + Rows[i].Index = this; + Rows[i].Contributions = + std::make_unique<Entry::SectionContribution[]>(Header.NumColumns); + Contribs[Index - 1] = Rows[i].Contributions.get(); + } + + // Read the Column Headers + for (unsigned i = 0; i != Header.NumColumns; ++i) { + ColumnKinds[i] = static_cast<DWARFSectionKind>(IndexData.getU32(&Offset)); + if (ColumnKinds[i] == InfoColumnKind) { + if (InfoColumn != -1) + return false; + InfoColumn = i; + } + } + + if (InfoColumn == -1) + return false; + + // Read Table of Section Offsets + for (unsigned i = 0; i != Header.NumUnits; ++i) { + auto *Contrib = Contribs[i]; + for (unsigned i = 0; i != Header.NumColumns; ++i) + Contrib[i].Offset = IndexData.getU32(&Offset); + } + + // Read Table of Section Sizes + for (unsigned i = 0; i != Header.NumUnits; ++i) { + auto *Contrib = Contribs[i]; + for (unsigned i = 0; i != Header.NumColumns; ++i) + Contrib[i].Length = IndexData.getU32(&Offset); + } + + return true; +} + +StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { +#define CASE(DS) \ + case DW_SECT_##DS: \ + return #DS; + switch (DS) { + CASE(INFO); + CASE(TYPES); + CASE(ABBREV); + CASE(LINE); + CASE(LOC); + CASE(STR_OFFSETS); + CASE(MACINFO); + CASE(MACRO); + } + llvm_unreachable("unknown DWARFSectionKind"); +} + +void DWARFUnitIndex::dump(raw_ostream &OS) const { + if (!*this) + return; + + Header.dump(OS); + OS << "Index Signature "; + for (unsigned i = 0; i != Header.NumColumns; ++i) + OS << ' ' << left_justify(getColumnHeader(ColumnKinds[i]), 24); + OS << "\n----- ------------------"; + for (unsigned i = 0; i != Header.NumColumns; ++i) + OS << " ------------------------"; + OS << '\n'; + for (unsigned i = 0; i != Header.NumBuckets; ++i) { + auto &Row = Rows[i]; + if (auto *Contribs = Row.Contributions.get()) { + OS << format("%5u 0x%016" PRIx64 " ", i + 1, Row.Signature); + for (unsigned i = 0; i != Header.NumColumns; ++i) { + auto &Contrib = Contribs[i]; + OS << format("[0x%08x, 0x%08x) ", Contrib.Offset, + Contrib.Offset + Contrib.Length); + } + OS << '\n'; + } + } +} + +const DWARFUnitIndex::Entry::SectionContribution * +DWARFUnitIndex::Entry::getOffset(DWARFSectionKind Sec) const { + uint32_t i = 0; + for (; i != Index->Header.NumColumns; ++i) + if (Index->ColumnKinds[i] == Sec) + return &Contributions[i]; + return nullptr; +} + +const DWARFUnitIndex::Entry::SectionContribution * +DWARFUnitIndex::Entry::getOffset() const { + return &Contributions[Index->InfoColumn]; +} + +const DWARFUnitIndex::Entry * +DWARFUnitIndex::getFromOffset(uint32_t Offset) const { + if (OffsetLookup.empty()) { + for (uint32_t i = 0; i != Header.NumBuckets; ++i) + if (Rows[i].Contributions) + OffsetLookup.push_back(&Rows[i]); + llvm::sort(OffsetLookup, [&](Entry *E1, Entry *E2) { + return E1->Contributions[InfoColumn].Offset < + E2->Contributions[InfoColumn].Offset; + }); + } + auto I = partition_point(OffsetLookup, [&](Entry *E2) { + return E2->Contributions[InfoColumn].Offset <= Offset; + }); + if (I == OffsetLookup.begin()) + return nullptr; + --I; + const auto *E = *I; + const auto &InfoContrib = E->Contributions[InfoColumn]; + if ((InfoContrib.Offset + InfoContrib.Length) <= Offset) + return nullptr; + return E; +} + +const DWARFUnitIndex::Entry *DWARFUnitIndex::getFromHash(uint64_t S) const { + uint64_t Mask = Header.NumBuckets - 1; + + auto H = S & Mask; + auto HP = ((S >> 32) & Mask) | 1; + while (Rows[H].getSignature() != S && Rows[H].getSignature() != 0) + H = (H + HP) & Mask; + + if (Rows[H].getSignature() != S) + return nullptr; + + return &Rows[H]; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp new file mode 100644 index 000000000000..bf499b6ee092 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -0,0 +1,1493 @@ +//===- DWARFVerifier.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <map> +#include <set> +#include <vector> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +DWARFVerifier::DieRangeInfo::address_range_iterator +DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { + auto Begin = Ranges.begin(); + auto End = Ranges.end(); + auto Pos = std::lower_bound(Begin, End, R); + + if (Pos != End) { + if (Pos->intersects(R)) + return std::move(Pos); + if (Pos != Begin) { + auto Iter = Pos - 1; + if (Iter->intersects(R)) + return std::move(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 { + auto I1 = Ranges.begin(), E1 = Ranges.end(); + auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end(); + if (I2 == E2) + return true; + + DWARFAddressRange R = *I2; + while (I1 != E1) { + bool Covered = I1->LowPC <= R.LowPC; + if (R.LowPC == R.HighPC || (Covered && R.HighPC <= I1->HighPC)) { + if (++I2 == E2) + return true; + R = *I2; + continue; + } + if (!Covered) + return false; + if (R.LowPC < I1->HighPC) + R.LowPC = I1->HighPC; + ++I1; + } + return false; +} + +bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const { + auto I1 = Ranges.begin(), E1 = Ranges.end(); + auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end(); + while (I1 != E1 && I2 != E2) { + if (I1->intersects(*I2)) + return true; + if (I1->LowPC < I2->LowPC) + ++I1; + else + ++I2; + } + return false; +} + +bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, + uint64_t *Offset, unsigned UnitIndex, + uint8_t &UnitType, bool &isUnitDWARF64) { + uint64_t AbbrOffset, Length; + uint8_t AddrSize = 0; + uint16_t Version; + bool Success = true; + + bool ValidLength = false; + bool ValidVersion = false; + bool ValidAddrSize = false; + bool ValidType = true; + bool ValidAbbrevOffset = true; + + uint64_t OffsetStart = *Offset; + Length = DebugInfoData.getU32(Offset); + if (Length == dwarf::DW_LENGTH_DWARF64) { + Length = DebugInfoData.getU64(Offset); + isUnitDWARF64 = true; + } + Version = DebugInfoData.getU16(Offset); + + if (Version >= 5) { + UnitType = DebugInfoData.getU8(Offset); + AddrSize = DebugInfoData.getU8(Offset); + AbbrOffset = isUnitDWARF64 ? DebugInfoData.getU64(Offset) : DebugInfoData.getU32(Offset); + ValidType = dwarf::isUnitType(UnitType); + } else { + UnitType = 0; + AbbrOffset = isUnitDWARF64 ? DebugInfoData.getU64(Offset) : DebugInfoData.getU32(Offset); + AddrSize = DebugInfoData.getU8(Offset); + } + + if (!DCtx.getDebugAbbrev()->getAbbreviationDeclarationSet(AbbrOffset)) + ValidAbbrevOffset = false; + + ValidLength = DebugInfoData.isValidOffset(OffsetStart + Length + 3); + ValidVersion = DWARFContext::isSupportedVersion(Version); + ValidAddrSize = AddrSize == 4 || AddrSize == 8; + if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset || + !ValidType) { + Success = false; + error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n", UnitIndex, + OffsetStart); + if (!ValidLength) + note() << "The length for this unit is too " + "large for the .debug_info provided.\n"; + if (!ValidVersion) + note() << "The 16 bit unit header version is not valid.\n"; + if (!ValidType) + note() << "The unit type encoding is not valid.\n"; + if (!ValidAbbrevOffset) + note() << "The offset into the .debug_abbrev section is " + "not valid.\n"; + if (!ValidAddrSize) + note() << "The address size is unsupported.\n"; + } + *Offset = OffsetStart + Length + (isUnitDWARF64 ? 12 : 4); + return Success; +} + +unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit) { + unsigned NumUnitErrors = 0; + unsigned NumDies = Unit.getNumDIEs(); + for (unsigned I = 0; I < NumDies; ++I) { + auto Die = Unit.getDIEAtIndex(I); + + if (Die.getTag() == DW_TAG_null) + continue; + + for (auto AttrValue : Die.attributes()) { + NumUnitErrors += verifyDebugInfoAttribute(Die, AttrValue); + NumUnitErrors += verifyDebugInfoForm(Die, AttrValue); + } + + NumUnitErrors += verifyDebugInfoCallSite(Die); + } + + DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false); + if (!Die) { + error() << "Compilation unit without DIE.\n"; + NumUnitErrors++; + return NumUnitErrors; + } + + if (!dwarf::isUnitType(Die.getTag())) { + error() << "Compilation unit root DIE is not a unit DIE: " + << dwarf::TagString(Die.getTag()) << ".\n"; + NumUnitErrors++; + } + + uint8_t UnitType = Unit.getUnitType(); + if (!DWARFUnit::isMatchingUnitTypeAndTag(UnitType, Die.getTag())) { + error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType) + << ") and root DIE (" << dwarf::TagString(Die.getTag()) + << ") do not match.\n"; + NumUnitErrors++; + } + + DieRangeInfo RI; + NumUnitErrors += verifyDieRanges(Die, RI); + + return NumUnitErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) { + if (Die.getTag() != DW_TAG_call_site && Die.getTag() != DW_TAG_GNU_call_site) + return 0; + + DWARFDie Curr = Die.getParent(); + for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) { + if (Curr.getTag() == DW_TAG_inlined_subroutine) { + error() << "Call site entry nested within inlined subroutine:"; + Curr.dump(OS); + return 1; + } + } + + if (!Curr.isValid()) { + error() << "Call site entry not nested within a valid subprogram:"; + Die.dump(OS); + return 1; + } + + Optional<DWARFFormValue> CallAttr = + Curr.find({DW_AT_call_all_calls, DW_AT_call_all_source_calls, + DW_AT_call_all_tail_calls, DW_AT_GNU_all_call_sites, + DW_AT_GNU_all_source_call_sites, + DW_AT_GNU_all_tail_call_sites}); + if (!CallAttr) { + error() << "Subprogram with call site entry has no DW_AT_call attribute:"; + Curr.dump(OS); + Die.dump(OS, /*indent*/ 1); + return 1; + } + + return 0; +} + +unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) { + unsigned NumErrors = 0; + if (Abbrev) { + const DWARFAbbreviationDeclarationSet *AbbrDecls = + Abbrev->getAbbreviationDeclarationSet(0); + for (auto AbbrDecl : *AbbrDecls) { + SmallDenseSet<uint16_t> AttributeSet; + for (auto Attribute : AbbrDecl.attributes()) { + auto Result = AttributeSet.insert(Attribute.Attr); + if (!Result.second) { + error() << "Abbreviation declaration contains multiple " + << AttributeString(Attribute.Attr) << " attributes.\n"; + AbbrDecl.dump(OS); + ++NumErrors; + } + } + } + } + return NumErrors; +} + +bool DWARFVerifier::handleDebugAbbrev() { + OS << "Verifying .debug_abbrev...\n"; + + const DWARFObject &DObj = DCtx.getDWARFObj(); + unsigned NumErrors = 0; + if (!DObj.getAbbrevSection().empty()) + NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrev()); + if (!DObj.getAbbrevDWOSection().empty()) + NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrevDWO()); + + return NumErrors == 0; +} + +unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S, + DWARFSectionKind SectionKind) { + const DWARFObject &DObj = DCtx.getDWARFObj(); + DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0); + unsigned NumDebugInfoErrors = 0; + uint64_t OffsetStart = 0, Offset = 0, UnitIdx = 0; + uint8_t UnitType = 0; + bool isUnitDWARF64 = false; + bool isHeaderChainValid = true; + bool hasDIE = DebugInfoData.isValidOffset(Offset); + DWARFUnitVector TypeUnitVector; + DWARFUnitVector CompileUnitVector; + while (hasDIE) { + OffsetStart = Offset; + if (!verifyUnitHeader(DebugInfoData, &Offset, UnitIdx, UnitType, + isUnitDWARF64)) { + isHeaderChainValid = false; + if (isUnitDWARF64) + break; + } else { + DWARFUnitHeader Header; + Header.extract(DCtx, DebugInfoData, &OffsetStart, SectionKind); + DWARFUnit *Unit; + switch (UnitType) { + case dwarf::DW_UT_type: + case dwarf::DW_UT_split_type: { + Unit = TypeUnitVector.addUnit(std::make_unique<DWARFTypeUnit>( + DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangesSection(), + &DObj.getLocSection(), DObj.getStrSection(), + DObj.getStrOffsetsSection(), &DObj.getAppleObjCSection(), + DObj.getLineSection(), DCtx.isLittleEndian(), false, + TypeUnitVector)); + break; + } + case dwarf::DW_UT_skeleton: + case dwarf::DW_UT_split_compile: + case dwarf::DW_UT_compile: + case dwarf::DW_UT_partial: + // UnitType = 0 means that we are verifying a compile unit in DWARF v4. + case 0: { + Unit = CompileUnitVector.addUnit(std::make_unique<DWARFCompileUnit>( + DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangesSection(), + &DObj.getLocSection(), DObj.getStrSection(), + DObj.getStrOffsetsSection(), &DObj.getAppleObjCSection(), + DObj.getLineSection(), DCtx.isLittleEndian(), false, + CompileUnitVector)); + break; + } + default: { llvm_unreachable("Invalid UnitType."); } + } + NumDebugInfoErrors += verifyUnitContents(*Unit); + } + hasDIE = DebugInfoData.isValidOffset(Offset); + ++UnitIdx; + } + if (UnitIdx == 0 && !hasDIE) { + warn() << "Section is empty.\n"; + isHeaderChainValid = true; + } + if (!isHeaderChainValid) + ++NumDebugInfoErrors; + NumDebugInfoErrors += verifyDebugInfoReferences(); + return NumDebugInfoErrors; +} + +bool DWARFVerifier::handleDebugInfo() { + const DWARFObject &DObj = DCtx.getDWARFObj(); + unsigned NumErrors = 0; + + OS << "Verifying .debug_info Unit Header Chain...\n"; + DObj.forEachInfoSections([&](const DWARFSection &S) { + NumErrors += verifyUnitSection(S, DW_SECT_INFO); + }); + + OS << "Verifying .debug_types Unit Header Chain...\n"; + DObj.forEachTypesSections([&](const DWARFSection &S) { + NumErrors += verifyUnitSection(S, DW_SECT_TYPES); + }); + return NumErrors == 0; +} + +unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, + DieRangeInfo &ParentRI) { + unsigned NumErrors = 0; + + if (!Die.isValid()) + return NumErrors; + + auto RangesOrError = Die.getAddressRanges(); + if (!RangesOrError) { + // FIXME: Report the error. + ++NumErrors; + llvm::consumeError(RangesOrError.takeError()); + return NumErrors; + } + + DWARFAddressRangesVector Ranges = RangesOrError.get(); + // Build RI for this DIE and check that ranges within this DIE do not + // overlap. + DieRangeInfo RI(Die); + + // TODO support object files better + // + // Some object file formats (i.e. non-MachO) support COMDAT. ELF in + // particular does so by placing each function into a section. The DWARF data + // for the function at that point uses a section relative DW_FORM_addrp for + // the DW_AT_low_pc and a DW_FORM_data4 for the offset as the DW_AT_high_pc. + // In such a case, when the Die is the CU, the ranges will overlap, and we + // will flag valid conflicting ranges as invalid. + // + // For such targets, we should read the ranges from the CU and partition them + // by the section id. The ranges within a particular section should be + // disjoint, although the ranges across sections may overlap. We would map + // the child die to the entity that it references and the section with which + // it is associated. The child would then be checked against the range + // information for the associated section. + // + // For now, simply elide the range verification for the CU DIEs if we are + // processing an object file. + + if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) { + for (auto Range : Ranges) { + if (!Range.valid()) { + ++NumErrors; + error() << "Invalid address range " << Range << "\n"; + continue; + } + + // Verify that ranges don't intersect. + const auto IntersectingRange = RI.insert(Range); + if (IntersectingRange != RI.Ranges.end()) { + ++NumErrors; + error() << "DIE has overlapping address ranges: " << Range << " and " + << *IntersectingRange << "\n"; + break; + } + } + } + + // Verify that children don't intersect. + const auto IntersectingChild = ParentRI.insert(RI); + if (IntersectingChild != ParentRI.Children.end()) { + ++NumErrors; + error() << "DIEs have overlapping address ranges:"; + dump(Die); + dump(IntersectingChild->Die) << '\n'; + } + + // Verify that ranges are contained within their parent. + bool ShouldBeContained = !Ranges.empty() && !ParentRI.Ranges.empty() && + !(Die.getTag() == DW_TAG_subprogram && + ParentRI.Die.getTag() == DW_TAG_subprogram); + if (ShouldBeContained && !ParentRI.contains(RI)) { + ++NumErrors; + error() << "DIE address ranges are not contained in its parent's ranges:"; + dump(ParentRI.Die); + dump(Die, 2) << '\n'; + } + + // Recursively check children. + for (DWARFDie Child : Die) + NumErrors += verifyDieRanges(Child, RI); + + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, + DWARFAttribute &AttrValue) { + unsigned NumErrors = 0; + auto ReportError = [&](const Twine &TitleMsg) { + ++NumErrors; + error() << TitleMsg << '\n'; + dump(Die) << '\n'; + }; + + const DWARFObject &DObj = DCtx.getDWARFObj(); + const auto Attr = AttrValue.Attr; + switch (Attr) { + case DW_AT_ranges: + // Make sure the offset in the DW_AT_ranges attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DObj.getRangesSection().Data.size()) + ReportError("DW_AT_ranges offset is beyond .debug_ranges bounds:"); + break; + } + ReportError("DIE has invalid DW_AT_ranges encoding:"); + break; + case DW_AT_stmt_list: + // Make sure the offset in the DW_AT_stmt_list attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DObj.getLineSection().Data.size()) + ReportError("DW_AT_stmt_list offset is beyond .debug_line bounds: " + + llvm::formatv("{0:x8}", *SectionOffset)); + break; + } + ReportError("DIE has invalid DW_AT_stmt_list encoding:"); + break; + case DW_AT_location: { + auto VerifyLocationExpr = [&](ArrayRef<uint8_t> D) { + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data(toStringRef(D), DCtx.isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getVersion(), + U->getAddressByteSize()); + bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.isError(); + }); + if (Error || !Expression.verify(U)) + ReportError("DIE contains invalid DWARF expression:"); + }; + if (Optional<ArrayRef<uint8_t>> Expr = AttrValue.Value.getAsBlock()) { + // Verify inlined location. + VerifyLocationExpr(*Expr); + } else if (auto LocOffset = AttrValue.Value.getAsSectionOffset()) { + // Verify location list. + if (auto DebugLoc = DCtx.getDebugLoc()) + if (auto LocList = DebugLoc->getLocationListAtOffset(*LocOffset)) + for (const auto &Entry : LocList->Entries) + VerifyLocationExpr(Entry.Loc); + } + break; + } + case DW_AT_specification: + case DW_AT_abstract_origin: { + if (auto ReferencedDie = Die.getAttributeValueAsReferencedDie(Attr)) { + auto DieTag = Die.getTag(); + auto RefTag = ReferencedDie.getTag(); + if (DieTag == RefTag) + break; + if (DieTag == DW_TAG_inlined_subroutine && RefTag == DW_TAG_subprogram) + break; + if (DieTag == DW_TAG_variable && RefTag == DW_TAG_member) + break; + // This might be reference to a function declaration. + if (DieTag == DW_TAG_GNU_call_site && RefTag == DW_TAG_subprogram) + break; + ReportError("DIE with tag " + TagString(DieTag) + " has " + + AttributeString(Attr) + + " that points to DIE with " + "incompatible tag " + + TagString(RefTag)); + } + break; + } + case DW_AT_type: { + DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type); + if (TypeDie && !isType(TypeDie.getTag())) { + ReportError("DIE has " + AttributeString(Attr) + + " with incompatible tag " + TagString(TypeDie.getTag())); + } + break; + } + default: + break; + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, + DWARFAttribute &AttrValue) { + const DWARFObject &DObj = DCtx.getDWARFObj(); + auto DieCU = Die.getDwarfUnit(); + unsigned NumErrors = 0; + const auto Form = AttrValue.Value.getForm(); + switch (Form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: { + // Verify all CU relative references are valid CU offsets. + Optional<uint64_t> RefVal = AttrValue.Value.getAsReference(); + assert(RefVal); + if (RefVal) { + auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset(); + auto CUOffset = AttrValue.Value.getRawUValue(); + if (CUOffset >= CUSize) { + ++NumErrors; + error() << FormEncodingString(Form) << " CU offset " + << format("0x%08" PRIx64, CUOffset) + << " is invalid (must be less than CU size of " + << format("0x%08" PRIx64, CUSize) << "):\n"; + Die.dump(OS, 0, DumpOpts); + dump(Die) << '\n'; + } else { + // Valid reference, but we will verify it points to an actual + // DIE later. + ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); + } + } + break; + } + case DW_FORM_ref_addr: { + // Verify all absolute DIE references have valid offsets in the + // .debug_info section. + Optional<uint64_t> RefVal = AttrValue.Value.getAsReference(); + assert(RefVal); + if (RefVal) { + if (*RefVal >= DieCU->getInfoSection().Data.size()) { + ++NumErrors; + error() << "DW_FORM_ref_addr offset beyond .debug_info " + "bounds:\n"; + dump(Die) << '\n'; + } else { + // Valid reference, but we will verify it points to an actual + // DIE later. + ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); + } + } + break; + } + case DW_FORM_strp: { + auto SecOffset = AttrValue.Value.getAsSectionOffset(); + assert(SecOffset); // DW_FORM_strp is a section offset. + if (SecOffset && *SecOffset >= DObj.getStrSection().size()) { + ++NumErrors; + error() << "DW_FORM_strp offset beyond .debug_str bounds:\n"; + dump(Die) << '\n'; + } + break; + } + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: { + auto Index = AttrValue.Value.getRawUValue(); + auto DieCU = Die.getDwarfUnit(); + // Check that we have a valid DWARF v5 string offsets table. + if (!DieCU->getStringOffsetsTableContribution()) { + ++NumErrors; + error() << FormEncodingString(Form) + << " used without a valid string offsets table:\n"; + dump(Die) << '\n'; + break; + } + // Check that the index is within the bounds of the section. + unsigned ItemSize = DieCU->getDwarfStringOffsetsByteSize(); + // Use a 64-bit type to calculate the offset to guard against overflow. + uint64_t Offset = + (uint64_t)DieCU->getStringOffsetsBase() + Index * ItemSize; + if (DObj.getStrOffsetsSection().Data.size() < Offset + ItemSize) { + ++NumErrors; + error() << FormEncodingString(Form) << " uses index " + << format("%" PRIu64, Index) << ", which is too large:\n"; + dump(Die) << '\n'; + break; + } + // Check that the string offset is valid. + uint64_t StringOffset = *DieCU->getStringOffsetSectionItem(Index); + if (StringOffset >= DObj.getStrSection().size()) { + ++NumErrors; + error() << FormEncodingString(Form) << " uses index " + << format("%" PRIu64, Index) + << ", but the referenced string" + " offset is beyond .debug_str bounds:\n"; + dump(Die) << '\n'; + } + break; + } + default: + break; + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugInfoReferences() { + // Take all references and make sure they point to an actual DIE by + // getting the DIE by offset and emitting an error + OS << "Verifying .debug_info references...\n"; + unsigned NumErrors = 0; + for (const std::pair<uint64_t, std::set<uint64_t>> &Pair : + ReferenceToDIEOffsets) { + if (DCtx.getDIEForOffset(Pair.first)) + continue; + ++NumErrors; + error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first) + << ". Offset is in between DIEs:\n"; + for (auto Offset : Pair.second) + dump(DCtx.getDIEForOffset(Offset)) << '\n'; + OS << "\n"; + } + return NumErrors; +} + +void DWARFVerifier::verifyDebugLineStmtOffsets() { + std::map<uint64_t, DWARFDie> StmtListToDie; + for (const auto &CU : DCtx.compile_units()) { + auto Die = CU->getUnitDIE(); + // Get the attribute value as a section offset. No need to produce an + // error here if the encoding isn't correct because we validate this in + // the .debug_info verifier. + auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list)); + if (!StmtSectionOffset) + continue; + const uint64_t LineTableOffset = *StmtSectionOffset; + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) { + if (!LineTable) { + ++NumDebugLineErrors; + error() << ".debug_line[" << format("0x%08" PRIx64, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + dump(Die) << '\n'; + continue; + } + } else { + // Make sure we don't get a valid line table back if the offset is wrong. + assert(LineTable == nullptr); + // Skip this line table as it isn't valid. No need to create an error + // here because we validate this in the .debug_info verifier. + continue; + } + auto Iter = StmtListToDie.find(LineTableOffset); + if (Iter != StmtListToDie.end()) { + ++NumDebugLineErrors; + error() << "two compile unit DIEs, " + << format("0x%08" PRIx64, Iter->second.getOffset()) << " and " + << format("0x%08" PRIx64, Die.getOffset()) + << ", have the same DW_AT_stmt_list section offset:\n"; + dump(Iter->second); + dump(Die) << '\n'; + // Already verified this line table before, no need to do it again. + continue; + } + StmtListToDie[LineTableOffset] = Die; + } +} + +void DWARFVerifier::verifyDebugLineRows() { + for (const auto &CU : DCtx.compile_units()) { + auto Die = CU->getUnitDIE(); + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + // If there is no line table we will have created an error in the + // .debug_info verifier or in verifyDebugLineStmtOffsets(). + if (!LineTable) + continue; + + // Verify prologue. + uint32_t 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.Address < PrevAddress) { + ++NumDebugLineErrors; + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "] row[" << RowIndex + << "] decreases in address from previous row:\n"; + + DWARFDebugLine::Row::dumpTableHeader(OS); + if (RowIndex > 0) + LineTable->Rows[RowIndex - 1].dump(OS); + Row.dump(OS); + OS << '\n'; + } + + // Verify file index. + if (!LineTable->hasFileAtIndex(Row.File)) { + ++NumDebugLineErrors; + bool isDWARF5 = LineTable->Prologue.getVersion() >= 5; + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "][" << RowIndex << "] has invalid file index " << Row.File + << " (valid values are [" << (isDWARF5 ? "0," : "1,") + << LineTable->Prologue.FileNames.size() + << (isDWARF5 ? ")" : "]") << "):\n"; + DWARFDebugLine::Row::dumpTableHeader(OS); + Row.dump(OS); + OS << '\n'; + } + if (Row.EndSequence) + PrevAddress = 0; + else + PrevAddress = Row.Address.Address; + ++RowIndex; + } + } +} + +DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D, + DIDumpOptions DumpOpts) + : OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false), + IsMachOObject(false) { + if (const auto *F = DCtx.getDWARFObj().getFile()) { + IsObjectFile = F->isRelocatableObject(); + IsMachOObject = F->isMachO(); + } +} + +bool DWARFVerifier::handleDebugLine() { + NumDebugLineErrors = 0; + OS << "Verifying .debug_line...\n"; + verifyDebugLineStmtOffsets(); + verifyDebugLineRows(); + return NumDebugLineErrors == 0; +} + +unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, + DataExtractor *StrData, + const char *SectionName) { + unsigned NumErrors = 0; + DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection, + DCtx.isLittleEndian(), 0); + AppleAcceleratorTable AccelTable(AccelSectionData, *StrData); + + OS << "Verifying " << SectionName << "...\n"; + + // Verify that the fixed part of the header is not too short. + if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) { + error() << "Section is too small to fit a section header.\n"; + return 1; + } + + // Verify that the section is not too short. + if (Error E = AccelTable.extract()) { + error() << toString(std::move(E)) << '\n'; + return 1; + } + + // Verify that all buckets have a valid hash index or are empty. + uint32_t NumBuckets = AccelTable.getNumBuckets(); + uint32_t NumHashes = AccelTable.getNumHashes(); + + uint64_t BucketsOffset = + AccelTable.getSizeHdr() + AccelTable.getHeaderDataLength(); + uint64_t HashesBase = BucketsOffset + NumBuckets * 4; + uint64_t OffsetsBase = HashesBase + NumHashes * 4; + for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) { + uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset); + if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) { + error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx, + HashIdx); + ++NumErrors; + } + } + uint32_t NumAtoms = AccelTable.getAtomsDesc().size(); + if (NumAtoms == 0) { + error() << "No atoms: failed to read HashData.\n"; + return 1; + } + if (!AccelTable.validateForms()) { + error() << "Unsupported form: failed to read HashData.\n"; + return 1; + } + + for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) { + uint64_t HashOffset = HashesBase + 4 * HashIdx; + uint64_t DataOffset = OffsetsBase + 4 * HashIdx; + uint32_t Hash = AccelSectionData.getU32(&HashOffset); + uint64_t HashDataOffset = AccelSectionData.getU32(&DataOffset); + if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset, + sizeof(uint64_t))) { + error() << format("Hash[%d] has invalid HashData offset: " + "0x%08" PRIx64 ".\n", + HashIdx, HashDataOffset); + ++NumErrors; + } + + uint64_t StrpOffset; + uint64_t StringOffset; + uint32_t StringCount = 0; + uint64_t Offset; + unsigned Tag; + while ((StrpOffset = AccelSectionData.getU32(&HashDataOffset)) != 0) { + const uint32_t NumHashDataObjects = + AccelSectionData.getU32(&HashDataOffset); + for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects; + ++HashDataIdx) { + std::tie(Offset, Tag) = AccelTable.readAtoms(&HashDataOffset); + auto Die = DCtx.getDIEForOffset(Offset); + if (!Die) { + const uint32_t BucketIdx = + NumBuckets ? (Hash % NumBuckets) : UINT32_MAX; + StringOffset = StrpOffset; + const char *Name = StrData->getCStr(&StringOffset); + if (!Name) + Name = "<NULL>"; + + error() << format( + "%s Bucket[%d] Hash[%d] = 0x%08x " + "Str[%u] = 0x%08" PRIx64 " DIE[%d] = 0x%08" PRIx64 " " + "is not a valid DIE offset for \"%s\".\n", + SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset, + HashDataIdx, Offset, Name); + + ++NumErrors; + continue; + } + if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) { + error() << "Tag " << dwarf::TagString(Tag) + << " in accelerator table does not match Tag " + << dwarf::TagString(Die.getTag()) << " of DIE[" << HashDataIdx + << "].\n"; + ++NumErrors; + } + } + ++StringCount; + } + } + return NumErrors; +} + +unsigned +DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) { + // A map from CU offset to the (first) Name Index offset which claims to index + // this CU. + DenseMap<uint64_t, uint64_t> CUMap; + const uint64_t NotIndexed = std::numeric_limits<uint64_t>::max(); + + CUMap.reserve(DCtx.getNumCompileUnits()); + for (const auto &CU : DCtx.compile_units()) + CUMap[CU->getOffset()] = NotIndexed; + + unsigned NumErrors = 0; + for (const DWARFDebugNames::NameIndex &NI : AccelTable) { + if (NI.getCUCount() == 0) { + error() << formatv("Name Index @ {0:x} does not index any CU\n", + NI.getUnitOffset()); + ++NumErrors; + continue; + } + for (uint32_t CU = 0, End = NI.getCUCount(); CU < End; ++CU) { + uint64_t Offset = NI.getCUOffset(CU); + auto Iter = CUMap.find(Offset); + + if (Iter == CUMap.end()) { + error() << formatv( + "Name Index @ {0:x} references a non-existing CU @ {1:x}\n", + NI.getUnitOffset(), Offset); + ++NumErrors; + continue; + } + + if (Iter->second != NotIndexed) { + error() << formatv("Name Index @ {0:x} references a CU @ {1:x}, but " + "this CU is already indexed by Name Index @ {2:x}\n", + NI.getUnitOffset(), Offset, Iter->second); + continue; + } + Iter->second = NI.getUnitOffset(); + } + } + + for (const auto &KV : CUMap) { + if (KV.second == NotIndexed) + warn() << formatv("CU @ {0:x} not covered by any Name Index\n", KV.first); + } + + return NumErrors; +} + +unsigned +DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, + const DataExtractor &StrData) { + struct BucketInfo { + uint32_t Bucket; + uint32_t Index; + + constexpr BucketInfo(uint32_t Bucket, uint32_t Index) + : Bucket(Bucket), Index(Index) {} + bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; }; + }; + + uint32_t NumErrors = 0; + if (NI.getBucketCount() == 0) { + warn() << formatv("Name Index @ {0:x} does not contain a hash table.\n", + NI.getUnitOffset()); + return NumErrors; + } + + // Build up a list of (Bucket, Index) pairs. We use this later to verify that + // each Name is reachable from the appropriate bucket. + std::vector<BucketInfo> BucketStarts; + BucketStarts.reserve(NI.getBucketCount() + 1); + for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) { + uint32_t Index = NI.getBucketArrayEntry(Bucket); + if (Index > NI.getNameCount()) { + error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid " + "value {2}. Valid range is [0, {3}].\n", + Bucket, NI.getUnitOffset(), Index, NI.getNameCount()); + ++NumErrors; + continue; + } + if (Index > 0) + BucketStarts.emplace_back(Bucket, Index); + } + + // If there were any buckets with invalid values, skip further checks as they + // will likely produce many errors which will only confuse the actual root + // problem. + if (NumErrors > 0) + return NumErrors; + + // Sort the list in the order of increasing "Index" entries. + array_pod_sort(BucketStarts.begin(), BucketStarts.end()); + + // Insert a sentinel entry at the end, so we can check that the end of the + // table is covered in the loop below. + BucketStarts.emplace_back(NI.getBucketCount(), NI.getNameCount() + 1); + + // Loop invariant: NextUncovered is the (1-based) index of the first Name + // which is not reachable by any of the buckets we processed so far (and + // hasn't been reported as uncovered). + uint32_t NextUncovered = 1; + for (const BucketInfo &B : BucketStarts) { + // Under normal circumstances B.Index be equal to NextUncovered, but it can + // be less if a bucket points to names which are already known to be in some + // bucket we processed earlier. In that case, we won't trigger this error, + // but report the mismatched hash value error instead. (We know the hash + // will not match because we have already verified that the name's hash + // puts it into the previous bucket.) + if (B.Index > NextUncovered) { + error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] " + "are not covered by the hash table.\n", + NI.getUnitOffset(), NextUncovered, B.Index - 1); + ++NumErrors; + } + uint32_t Idx = B.Index; + + // The rest of the checks apply only to non-sentinel entries. + if (B.Bucket == NI.getBucketCount()) + break; + + // This triggers if a non-empty bucket points to a name with a mismatched + // hash. Clients are likely to interpret this as an empty bucket, because a + // mismatched hash signals the end of a bucket, but if this is indeed an + // empty bucket, the producer should have signalled this by marking the + // bucket as empty. + uint32_t FirstHash = NI.getHashArrayEntry(Idx); + if (FirstHash % NI.getBucketCount() != B.Bucket) { + error() << formatv( + "Name Index @ {0:x}: Bucket {1} is not empty but points to a " + "mismatched hash value {2:x} (belonging to bucket {3}).\n", + NI.getUnitOffset(), B.Bucket, FirstHash, + FirstHash % NI.getBucketCount()); + ++NumErrors; + } + + // This find the end of this bucket and also verifies that all the hashes in + // this bucket are correct by comparing the stored hashes to the ones we + // compute ourselves. + while (Idx <= NI.getNameCount()) { + uint32_t Hash = NI.getHashArrayEntry(Idx); + if (Hash % NI.getBucketCount() != B.Bucket) + break; + + const char *Str = NI.getNameTableEntry(Idx).getString(); + if (caseFoldingDjbHash(Str) != Hash) { + error() << formatv("Name Index @ {0:x}: String ({1}) at index {2} " + "hashes to {3:x}, but " + "the Name Index hash is {4:x}\n", + NI.getUnitOffset(), Str, Idx, + caseFoldingDjbHash(Str), Hash); + ++NumErrors; + } + + ++Idx; + } + NextUncovered = std::max(NextUncovered, Idx); + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyNameIndexAttribute( + const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr, + DWARFDebugNames::AttributeEncoding AttrEnc) { + StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form); + if (FormName.empty()) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " + "unknown form: {3}.\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, + AttrEnc.Form); + return 1; + } + + if (AttrEnc.Index == DW_IDX_type_hash) { + if (AttrEnc.Form != dwarf::DW_FORM_data8) { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash " + "uses an unexpected form {2} (should be {3}).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8); + return 1; + } + } + + // A list of known index attributes and their expected form classes. + // DW_IDX_type_hash is handled specially in the check above, as it has a + // specific form (not just a form class) we should expect. + struct FormClassTable { + dwarf::Index Index; + DWARFFormValue::FormClass Class; + StringLiteral ClassName; + }; + static constexpr FormClassTable Table[] = { + {dwarf::DW_IDX_compile_unit, DWARFFormValue::FC_Constant, {"constant"}}, + {dwarf::DW_IDX_type_unit, DWARFFormValue::FC_Constant, {"constant"}}, + {dwarf::DW_IDX_die_offset, DWARFFormValue::FC_Reference, {"reference"}}, + {dwarf::DW_IDX_parent, DWARFFormValue::FC_Constant, {"constant"}}, + }; + + ArrayRef<FormClassTable> TableRef(Table); + auto Iter = find_if(TableRef, [AttrEnc](const FormClassTable &T) { + return T.Index == AttrEnc.Index; + }); + if (Iter == TableRef.end()) { + warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an " + "unknown index attribute: {2}.\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index); + return 0; + } + + if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " + "unexpected form {3} (expected form class {4}).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, + AttrEnc.Form, Iter->ClassName); + return 1; + } + return 0; +} + +unsigned +DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { + if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) { + warn() << formatv("Name Index @ {0:x}: Verifying indexes of type units is " + "not currently supported.\n", + NI.getUnitOffset()); + return 0; + } + + unsigned NumErrors = 0; + for (const auto &Abbrev : NI.getAbbrevs()) { + StringRef TagName = dwarf::TagString(Abbrev.Tag); + if (TagName.empty()) { + warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} references an " + "unknown tag: {2}.\n", + NI.getUnitOffset(), Abbrev.Code, Abbrev.Tag); + } + SmallSet<unsigned, 5> Attributes; + for (const auto &AttrEnc : Abbrev.Attributes) { + if (!Attributes.insert(AttrEnc.Index).second) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains " + "multiple {2} attributes.\n", + NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index); + ++NumErrors; + continue; + } + NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc); + } + + if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) { + error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units " + "and abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, + dwarf::DW_IDX_compile_unit); + ++NumErrors; + } + if (!Attributes.count(dwarf::DW_IDX_die_offset)) { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset); + ++NumErrors; + } + } + return NumErrors; +} + +static SmallVector<StringRef, 2> getNames(const DWARFDie &DIE, + bool IncludeLinkageName = true) { + SmallVector<StringRef, 2> Result; + if (const char *Str = DIE.getName(DINameKind::ShortName)) + Result.emplace_back(Str); + else if (DIE.getTag() == dwarf::DW_TAG_namespace) + Result.emplace_back("(anonymous namespace)"); + + if (IncludeLinkageName) { + if (const char *Str = DIE.getName(DINameKind::LinkageName)) { + if (Result.empty() || Result[0] != Str) + Result.emplace_back(Str); + } + } + + return Result; +} + +unsigned DWARFVerifier::verifyNameIndexEntries( + const DWARFDebugNames::NameIndex &NI, + const DWARFDebugNames::NameTableEntry &NTE) { + // Verifying type unit indexes not supported. + if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) + return 0; + + const char *CStr = NTE.getString(); + if (!CStr) { + error() << formatv( + "Name Index @ {0:x}: Unable to get string associated with name {1}.\n", + NI.getUnitOffset(), NTE.getIndex()); + return 1; + } + StringRef Str(CStr); + + unsigned NumErrors = 0; + unsigned NumEntries = 0; + uint64_t EntryID = NTE.getEntryOffset(); + uint64_t NextEntryID = EntryID; + Expected<DWARFDebugNames::Entry> EntryOr = NI.getEntry(&NextEntryID); + for (; EntryOr; ++NumEntries, EntryID = NextEntryID, + EntryOr = NI.getEntry(&NextEntryID)) { + uint32_t CUIndex = *EntryOr->getCUIndex(); + if (CUIndex > NI.getCUCount()) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an " + "invalid CU index ({2}).\n", + NI.getUnitOffset(), EntryID, CUIndex); + ++NumErrors; + continue; + } + uint64_t CUOffset = NI.getCUOffset(CUIndex); + uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset(); + DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset); + if (!DIE) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a " + "non-existing DIE @ {2:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset); + ++NumErrors; + continue; + } + if (DIE.getDwarfUnit()->getOffset() != CUOffset) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of " + "DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, CUOffset, + DIE.getDwarfUnit()->getOffset()); + ++NumErrors; + } + if (DIE.getTag() != EntryOr->tag()) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of " + "DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(), + DIE.getTag()); + ++NumErrors; + } + + auto EntryNames = getNames(DIE); + if (!is_contained(EntryNames, Str)) { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " + "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, Str, + make_range(EntryNames.begin(), EntryNames.end())); + ++NumErrors; + } + } + handleAllErrors(EntryOr.takeError(), + [&](const DWARFDebugNames::SentinelError &) { + if (NumEntries > 0) + return; + error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is " + "not associated with any entries.\n", + NI.getUnitOffset(), NTE.getIndex(), Str); + ++NumErrors; + }, + [&](const ErrorInfoBase &Info) { + error() + << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n", + NI.getUnitOffset(), NTE.getIndex(), Str, + Info.message()); + ++NumErrors; + }); + return NumErrors; +} + +static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) { + Optional<DWARFFormValue> Location = Die.findRecursively(DW_AT_location); + if (!Location) + return false; + + auto ContainsInterestingOperators = [&](ArrayRef<uint8_t> D) { + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data(toStringRef(D), DCtx.isLittleEndian(), U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + return any_of(Expression, [](DWARFExpression::Operation &Op) { + return !Op.isError() && (Op.getCode() == DW_OP_addr || + Op.getCode() == DW_OP_form_tls_address || + Op.getCode() == DW_OP_GNU_push_tls_address); + }); + }; + + if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) { + // Inlined location. + if (ContainsInterestingOperators(*Expr)) + return true; + } else if (Optional<uint64_t> Offset = Location->getAsSectionOffset()) { + // Location list. + if (const DWARFDebugLoc *DebugLoc = DCtx.getDebugLoc()) { + if (const DWARFDebugLoc::LocationList *LocList = + DebugLoc->getLocationListAtOffset(*Offset)) { + if (any_of(LocList->Entries, [&](const DWARFDebugLoc::Entry &E) { + return ContainsInterestingOperators(E.Loc); + })) + return true; + } + } + } + return false; +} + +unsigned DWARFVerifier::verifyNameIndexCompleteness( + const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) { + + // First check, if the Die should be indexed. The code follows the DWARF v5 + // wording as closely as possible. + + // "All non-defining declarations (that is, debugging information entries + // with a DW_AT_declaration attribute) are excluded." + if (Die.find(DW_AT_declaration)) + return 0; + + // "DW_TAG_namespace debugging information entries without a DW_AT_name + // attribute are included with the name “(anonymous namespace)”. + // All other debugging information entries without a DW_AT_name attribute + // are excluded." + // "If a subprogram or inlined subroutine is included, and has a + // DW_AT_linkage_name attribute, there will be an additional index entry for + // the linkage name." + auto IncludeLinkageName = Die.getTag() == DW_TAG_subprogram || + Die.getTag() == DW_TAG_inlined_subroutine; + auto EntryNames = getNames(Die, IncludeLinkageName); + if (EntryNames.empty()) + return 0; + + // We deviate from the specification here, which says: + // "The name index must contain an entry for each debugging information entry + // that defines a named subprogram, label, variable, type, or namespace, + // subject to ..." + // Instead whitelisting all TAGs representing a "type" or a "subprogram", to + // make sure we catch any missing items, we instead blacklist all TAGs that we + // know shouldn't be indexed. + switch (Die.getTag()) { + // Compile units and modules have names but shouldn't be indexed. + case DW_TAG_compile_unit: + case DW_TAG_module: + return 0; + + // Function and template parameters are not globally visible, so we shouldn't + // index them. + case DW_TAG_formal_parameter: + case DW_TAG_template_value_parameter: + case DW_TAG_template_type_parameter: + case DW_TAG_GNU_template_parameter_pack: + case DW_TAG_GNU_template_template_param: + return 0; + + // Object members aren't globally visible. + case DW_TAG_member: + return 0; + + // According to a strict reading of the specification, enumerators should not + // be indexed (and LLVM currently does not do that). However, this causes + // problems for the debuggers, so we may need to reconsider this. + case DW_TAG_enumerator: + return 0; + + // Imported declarations should not be indexed according to the specification + // and LLVM currently does not do that. + case DW_TAG_imported_declaration: + return 0; + + // "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging + // information entries without an address attribute (DW_AT_low_pc, + // DW_AT_high_pc, DW_AT_ranges, or DW_AT_entry_pc) are excluded." + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_label: + if (Die.findRecursively( + {DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc})) + break; + return 0; + + // "DW_TAG_variable debugging information entries with a DW_AT_location + // attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are + // included; otherwise, they are excluded." + // + // LLVM extension: We also add DW_OP_GNU_push_tls_address to this list. + case DW_TAG_variable: + if (isVariableIndexable(Die, DCtx)) + break; + return 0; + + default: + break; + } + + // Now we know that our Die should be present in the Index. Let's check if + // that's the case. + unsigned NumErrors = 0; + uint64_t DieUnitOffset = Die.getOffset() - Die.getDwarfUnit()->getOffset(); + for (StringRef Name : EntryNames) { + if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) { + return E.getDIEUnitOffset() == DieUnitOffset; + })) { + error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with " + "name {3} missing.\n", + NI.getUnitOffset(), Die.getOffset(), Die.getTag(), + Name); + ++NumErrors; + } + } + return NumErrors; +} + +unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, + const DataExtractor &StrData) { + unsigned NumErrors = 0; + DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), AccelSection, + DCtx.isLittleEndian(), 0); + DWARFDebugNames AccelTable(AccelSectionData, StrData); + + OS << "Verifying .debug_names...\n"; + + // This verifies that we can read individual name indices and their + // abbreviation tables. + if (Error E = AccelTable.extract()) { + error() << toString(std::move(E)) << '\n'; + return 1; + } + + NumErrors += verifyDebugNamesCULists(AccelTable); + for (const auto &NI : AccelTable) + NumErrors += verifyNameIndexBuckets(NI, StrData); + for (const auto &NI : AccelTable) + NumErrors += verifyNameIndexAbbrevs(NI); + + // Don't attempt Entry validation if any of the previous checks found errors + if (NumErrors > 0) + return NumErrors; + for (const auto &NI : AccelTable) + for (DWARFDebugNames::NameTableEntry NTE : NI) + NumErrors += verifyNameIndexEntries(NI, NTE); + + if (NumErrors > 0) + return NumErrors; + + for (const std::unique_ptr<DWARFUnit> &U : DCtx.compile_units()) { + if (const DWARFDebugNames::NameIndex *NI = + AccelTable.getCUNameIndex(U->getOffset())) { + auto *CU = cast<DWARFCompileUnit>(U.get()); + for (const DWARFDebugInfoEntry &Die : CU->dies()) + NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &Die), *NI); + } + } + return NumErrors; +} + +bool DWARFVerifier::handleAccelTables() { + const DWARFObject &D = DCtx.getDWARFObj(); + DataExtractor StrData(D.getStrSection(), DCtx.isLittleEndian(), 0); + unsigned NumErrors = 0; + if (!D.getAppleNamesSection().Data.empty()) + NumErrors += verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData, + ".apple_names"); + if (!D.getAppleTypesSection().Data.empty()) + NumErrors += verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData, + ".apple_types"); + if (!D.getAppleNamespacesSection().Data.empty()) + NumErrors += verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData, + ".apple_namespaces"); + if (!D.getAppleObjCSection().Data.empty()) + NumErrors += verifyAppleAccelTable(&D.getAppleObjCSection(), &StrData, + ".apple_objc"); + + if (!D.getNamesSection().Data.empty()) + NumErrors += verifyDebugNames(D.getNamesSection(), StrData); + return NumErrors == 0; +} + +raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); } + +raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); } + +raw_ostream &DWARFVerifier::note() const { return WithColor::note(OS); } + +raw_ostream &DWARFVerifier::dump(const DWARFDie &Die, unsigned indent) const { + Die.dump(OS, indent, DumpOpts); + return OS; +} diff --git a/llvm/lib/DebugInfo/GSYM/FileWriter.cpp b/llvm/lib/DebugInfo/GSYM/FileWriter.cpp new file mode 100644 index 000000000000..4b30dcb60a7b --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/FileWriter.cpp @@ -0,0 +1,78 @@ +//===- FileWriter.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/GSYM/FileWriter.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +using namespace llvm; +using namespace gsym; + +FileWriter::~FileWriter() { OS.flush(); } + +void FileWriter::writeSLEB(int64_t S) { + uint8_t Bytes[32]; + auto Length = encodeSLEB128(S, Bytes); + assert(Length < sizeof(Bytes)); + OS.write(reinterpret_cast<const char *>(Bytes), Length); +} + +void FileWriter::writeULEB(uint64_t U) { + uint8_t Bytes[32]; + auto Length = encodeULEB128(U, Bytes); + assert(Length < sizeof(Bytes)); + OS.write(reinterpret_cast<const char *>(Bytes), Length); +} + +void FileWriter::writeU8(uint8_t U) { + OS.write(reinterpret_cast<const char *>(&U), sizeof(U)); +} + +void FileWriter::writeU16(uint16_t U) { + const uint16_t Swapped = support::endian::byte_swap(U, ByteOrder); + OS.write(reinterpret_cast<const char *>(&Swapped), sizeof(Swapped)); +} + +void FileWriter::writeU32(uint32_t U) { + const uint32_t Swapped = support::endian::byte_swap(U, ByteOrder); + OS.write(reinterpret_cast<const char *>(&Swapped), sizeof(Swapped)); +} + +void FileWriter::writeU64(uint64_t U) { + const uint64_t Swapped = support::endian::byte_swap(U, ByteOrder); + OS.write(reinterpret_cast<const char *>(&Swapped), sizeof(Swapped)); +} + +void FileWriter::fixup32(uint32_t U, uint64_t Offset) { + const uint32_t Swapped = support::endian::byte_swap(U, ByteOrder); + OS.pwrite(reinterpret_cast<const char *>(&Swapped), sizeof(Swapped), + Offset); +} + +void FileWriter::writeData(llvm::ArrayRef<uint8_t> Data) { + OS.write(reinterpret_cast<const char *>(Data.data()), Data.size()); +} + +void FileWriter::writeNullTerminated(llvm::StringRef Str) { + OS << Str << '\0'; +} + +uint64_t FileWriter::tell() { + return OS.tell(); +} + +void FileWriter::alignTo(size_t Align) { + off_t Offset = OS.tell(); + off_t AlignedOffset = (Offset + Align - 1) / Align * Align; + if (AlignedOffset == Offset) + return; + off_t PadCount = AlignedOffset - Offset; + OS.write_zeros(PadCount); +} diff --git a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp new file mode 100644 index 000000000000..ad022fec9e32 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp @@ -0,0 +1,147 @@ +//===- FunctionInfo.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/GSYM/FunctionInfo.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/LineTable.h" +#include "llvm/DebugInfo/GSYM/InlineInfo.h" +#include "llvm/Support/DataExtractor.h" + +using namespace llvm; +using namespace gsym; + +/// FunctionInfo information type that is used to encode the optional data +/// that is associated with a FunctionInfo object. +enum InfoType : uint32_t { + EndOfList = 0u, + LineTableInfo = 1u, + InlineInfo = 2u +}; + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) { + OS << '[' << HEX64(FI.Range.Start) << '-' << HEX64(FI.Range.End) << "): " + << "Name=" << HEX32(FI.Name) << '\n' << FI.OptLineTable << FI.Inline; + return OS; +} + +llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data, + uint64_t BaseAddr) { + FunctionInfo FI; + FI.Range.Start = BaseAddr; + uint64_t Offset = 0; + if (!Data.isValidOffsetForDataOfSize(Offset, 4)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset); + FI.Range.End = FI.Range.Start + Data.getU32(&Offset); + if (!Data.isValidOffsetForDataOfSize(Offset, 4)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset); + FI.Name = Data.getU32(&Offset); + if (FI.Name == 0) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x", + Offset - 4, FI.Name); + bool Done = false; + while (!Done) { + if (!Data.isValidOffsetForDataOfSize(Offset, 4)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset); + const uint32_t IT = Data.getU32(&Offset); + if (!Data.isValidOffsetForDataOfSize(Offset, 4)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset); + const uint32_t InfoLength = Data.getU32(&Offset); + if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u", + Offset, IT); + DataExtractor InfoData(Data.getData().substr(Offset, InfoLength), + Data.isLittleEndian(), + Data.getAddressSize()); + switch (IT) { + case InfoType::EndOfList: + Done = true; + break; + + case InfoType::LineTableInfo: + if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr)) + FI.OptLineTable = std::move(LT.get()); + else + return LT.takeError(); + break; + + case InfoType::InlineInfo: + if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr)) + FI.Inline = std::move(II.get()); + else + return II.takeError(); + break; + + default: + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": unsupported InfoType %u", + Offset-8, IT); + } + Offset += InfoLength; + } + return std::move(FI); +} + +llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { + if (!isValid()) + return createStringError(std::errc::invalid_argument, + "attempted to encode invalid FunctionInfo object"); + // Align FunctionInfo data to a 4 byte alignment. + O.alignTo(4); + const uint64_t FuncInfoOffset = O.tell(); + // Write the size in bytes of this function as a uint32_t. This can be zero + // if we just have a symbol from a symbol table and that symbol has no size. + O.writeU32(size()); + // Write the name of this function as a uint32_t string table offset. + O.writeU32(Name); + + if (OptLineTable.hasValue()) { + O.writeU32(InfoType::LineTableInfo); + // Write a uint32_t length as zero for now, we will fix this up after + // writing the LineTable out with the number of bytes that were written. + O.writeU32(0); + const auto StartOffset = O.tell(); + llvm::Error err = OptLineTable->encode(O, Range.Start); + if (err) + return std::move(err); + const off_t Length = O.tell() - StartOffset; + if (Length > UINT32_MAX) + return createStringError(std::errc::invalid_argument, + "LineTable length is greater than UINT32_MAX"); + // Fixup the size of the LineTable data with the correct size. + O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4); + } + + // Write out the inline function info if we have any and if it is valid. + if (Inline.hasValue()) { + O.writeU32(InfoType::InlineInfo); + // Write a uint32_t length as zero for now, we will fix this up after + // writing the LineTable out with the number of bytes that were written. + O.writeU32(0); + const auto StartOffset = O.tell(); + llvm::Error err = Inline->encode(O, Range.Start); + if (err) + return std::move(err); + const off_t Length = O.tell() - StartOffset; + if (Length > UINT32_MAX) + return createStringError(std::errc::invalid_argument, + "InlineInfo length is greater than UINT32_MAX"); + // Fixup the size of the InlineInfo data with the correct size. + O.fixup32(static_cast<uint32_t>(Length), StartOffset - 4); + } + + // Terminate the data chunks with and end of list with zero size + O.writeU32(InfoType::EndOfList); + O.writeU32(0); + return FuncInfoOffset; +} diff --git a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp new file mode 100644 index 000000000000..f371426f2010 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp @@ -0,0 +1,275 @@ +//===- GsymCreator.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/GSYM/GsymCreator.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/Header.h" +#include "llvm/DebugInfo/GSYM/LineTable.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Support/raw_ostream.h" + +#include <algorithm> +#include <cassert> +#include <functional> +#include <vector> + +using namespace llvm; +using namespace gsym; + + +GsymCreator::GsymCreator() : StrTab(StringTableBuilder::ELF) { + insertFile(StringRef()); +} + +uint32_t GsymCreator::insertFile(StringRef Path, + llvm::sys::path::Style Style) { + llvm::StringRef directory = llvm::sys::path::parent_path(Path, Style); + llvm::StringRef filename = llvm::sys::path::filename(Path, Style); + FileEntry FE(insertString(directory), insertString(filename)); + + std::lock_guard<std::recursive_mutex> Guard(Mutex); + const auto NextIndex = Files.size(); + // Find FE in hash map and insert if not present. + auto R = FileEntryToIndex.insert(std::make_pair(FE, NextIndex)); + if (R.second) + Files.emplace_back(FE); + return R.first->second; +} + +llvm::Error GsymCreator::save(StringRef Path, + llvm::support::endianness ByteOrder) const { + std::error_code EC; + raw_fd_ostream OutStrm(Path, EC); + if (EC) + return llvm::errorCodeToError(EC); + FileWriter O(OutStrm, ByteOrder); + return encode(O); +} + +llvm::Error GsymCreator::encode(FileWriter &O) const { + std::lock_guard<std::recursive_mutex> Guard(Mutex); + if (Funcs.empty()) + return createStringError(std::errc::invalid_argument, + "no functions to encode"); + if (!Finalized) + return createStringError(std::errc::invalid_argument, + "GsymCreator wasn't finalized prior to encoding"); + + if (Funcs.size() > UINT32_MAX) + return createStringError(std::errc::invalid_argument, + "too many FunctionInfos"); + const uint64_t MinAddr = Funcs.front().startAddress(); + const uint64_t MaxAddr = Funcs.back().startAddress(); + const uint64_t AddrDelta = MaxAddr - MinAddr; + Header Hdr; + Hdr.Magic = GSYM_MAGIC; + Hdr.Version = GSYM_VERSION; + Hdr.AddrOffSize = 0; + Hdr.UUIDSize = static_cast<uint8_t>(UUID.size()); + Hdr.BaseAddress = MinAddr; + Hdr.NumAddresses = static_cast<uint32_t>(Funcs.size()); + Hdr.StrtabOffset = 0; // We will fix this up later. + Hdr.StrtabOffset = 0; // We will fix this up later. + memset(Hdr.UUID, 0, sizeof(Hdr.UUID)); + if (UUID.size() > sizeof(Hdr.UUID)) + return createStringError(std::errc::invalid_argument, + "invalid UUID size %u", (uint32_t)UUID.size()); + // Set the address offset size correctly in the GSYM header. + if (AddrDelta <= UINT8_MAX) + Hdr.AddrOffSize = 1; + else if (AddrDelta <= UINT16_MAX) + Hdr.AddrOffSize = 2; + else if (AddrDelta <= UINT32_MAX) + Hdr.AddrOffSize = 4; + else + Hdr.AddrOffSize = 8; + // Copy the UUID value if we have one. + if (UUID.size() > 0) + memcpy(Hdr.UUID, UUID.data(), UUID.size()); + // Write out the header. + llvm::Error Err = Hdr.encode(O); + if (Err) + return Err; + + // Write out the address offsets. + O.alignTo(Hdr.AddrOffSize); + for (const auto &FuncInfo : Funcs) { + uint64_t AddrOffset = FuncInfo.startAddress() - Hdr.BaseAddress; + switch(Hdr.AddrOffSize) { + case 1: O.writeU8(static_cast<uint8_t>(AddrOffset)); break; + case 2: O.writeU16(static_cast<uint16_t>(AddrOffset)); break; + case 4: O.writeU32(static_cast<uint32_t>(AddrOffset)); break; + case 8: O.writeU64(AddrOffset); break; + } + } + + // Write out all zeros for the AddrInfoOffsets. + O.alignTo(4); + const off_t AddrInfoOffsetsOffset = O.tell(); + for (size_t i = 0, n = Funcs.size(); i < n; ++i) + O.writeU32(0); + + // Write out the file table + O.alignTo(4); + assert(!Files.empty()); + assert(Files[0].Dir == 0); + assert(Files[0].Base == 0); + size_t NumFiles = Files.size(); + if (NumFiles > UINT32_MAX) + return createStringError(std::errc::invalid_argument, + "too many files"); + O.writeU32(static_cast<uint32_t>(NumFiles)); + for (auto File: Files) { + O.writeU32(File.Dir); + O.writeU32(File.Base); + } + + // Write out the sting table. + const off_t StrtabOffset = O.tell(); + StrTab.write(O.get_stream()); + const off_t StrtabSize = O.tell() - StrtabOffset; + std::vector<uint32_t> AddrInfoOffsets; + + // Write out the address infos for each function info. + for (const auto &FuncInfo : Funcs) { + if (Expected<uint64_t> OffsetOrErr = FuncInfo.encode(O)) + AddrInfoOffsets.push_back(OffsetOrErr.get()); + else + return OffsetOrErr.takeError(); + } + // Fixup the string table offset and size in the header + O.fixup32((uint32_t)StrtabOffset, offsetof(Header, StrtabOffset)); + O.fixup32((uint32_t)StrtabSize, offsetof(Header, StrtabSize)); + + // Fixup all address info offsets + uint64_t Offset = 0; + for (auto AddrInfoOffset: AddrInfoOffsets) { + O.fixup32(AddrInfoOffset, AddrInfoOffsetsOffset + Offset); + Offset += 4; + } + return ErrorSuccess(); +} + +llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { + std::lock_guard<std::recursive_mutex> Guard(Mutex); + if (Finalized) + return createStringError(std::errc::invalid_argument, + "already finalized"); + Finalized = true; + + // Sort function infos so we can emit sorted functions. + llvm::sort(Funcs.begin(), Funcs.end()); + + // Don't let the string table indexes change by finalizing in order. + StrTab.finalizeInOrder(); + + // Remove duplicates function infos that have both entries from debug info + // (DWARF or Breakpad) and entries from the SymbolTable. + // + // Also handle overlapping function. Usually there shouldn't be any, but they + // can and do happen in some rare cases. + // + // (a) (b) (c) + // ^ ^ ^ ^ + // |X |Y |X ^ |X + // | | | |Y | ^ + // | | | v v |Y + // v v v v + // + // In (a) and (b), Y is ignored and X will be reported for the full range. + // In (c), both functions will be included in the result and lookups for an + // address in the intersection will return Y because of binary search. + // + // Note that in case of (b), we cannot include Y in the result because then + // we wouldn't find any function for range (end of Y, end of X) + // with binary search + auto NumBefore = Funcs.size(); + auto Curr = Funcs.begin(); + auto Prev = Funcs.end(); + while (Curr != Funcs.end()) { + // Can't check for overlaps or same address ranges if we don't have a + // previous entry + if (Prev != Funcs.end()) { + if (Prev->Range.intersects(Curr->Range)) { + // Overlapping address ranges. + if (Prev->Range == Curr->Range) { + // Same address range. Check if one is from debug info and the other + // is from a symbol table. If so, then keep the one with debug info. + // Our sorting guarantees that entries with matching address ranges + // that have debug info are last in the sort. + if (*Prev == *Curr) { + // FunctionInfo entries match exactly (range, lines, inlines) + OS << "warning: duplicate function info entries, removing " + "duplicate:\n" + << *Curr << '\n'; + Curr = Funcs.erase(Prev); + } else { + if (!Prev->hasRichInfo() && Curr->hasRichInfo()) { + // Same address range, one with no debug info (symbol) and the + // next with debug info. Keep the latter. + Curr = Funcs.erase(Prev); + } else { + OS << "warning: same address range contains different debug " + << "info. Removing:\n" + << *Prev << "\nIn favor of this one:\n" + << *Curr << "\n"; + Curr = Funcs.erase(Prev); + } + } + } else { + // print warnings about overlaps + OS << "warning: function ranges overlap:\n" + << *Prev << "\n" + << *Curr << "\n"; + } + } else if (Prev->Range.size() == 0 && + Curr->Range.contains(Prev->Range.Start)) { + OS << "warning: removing symbol:\n" + << *Prev << "\nKeeping:\n" + << *Curr << "\n"; + Curr = Funcs.erase(Prev); + } + } + if (Curr == Funcs.end()) + break; + Prev = Curr++; + } + + OS << "Pruned " << NumBefore - Funcs.size() << " functions, ended with " + << Funcs.size() << " total\n"; + return Error::success(); +} + +uint32_t GsymCreator::insertString(StringRef S) { + std::lock_guard<std::recursive_mutex> Guard(Mutex); + if (S.empty()) + return 0; + return StrTab.add(S); +} + +void GsymCreator::addFunctionInfo(FunctionInfo &&FI) { + std::lock_guard<std::recursive_mutex> Guard(Mutex); + Funcs.emplace_back(FI); +} + +void GsymCreator::forEachFunctionInfo( + std::function<bool(FunctionInfo &)> const &Callback) { + std::lock_guard<std::recursive_mutex> Guard(Mutex); + for (auto &FI : Funcs) { + if (!Callback(FI)) + break; + } +} + +void GsymCreator::forEachFunctionInfo( + std::function<bool(const FunctionInfo &)> const &Callback) const { + std::lock_guard<std::recursive_mutex> Guard(Mutex); + for (const auto &FI : Funcs) { + if (!Callback(FI)) + break; + } +} diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp new file mode 100644 index 000000000000..1b448cf80b70 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp @@ -0,0 +1,265 @@ +//===- GsymReader.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/GSYM/GsymReader.h" + +#include <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> + +#include "llvm/DebugInfo/GSYM/GsymCreator.h" +#include "llvm/DebugInfo/GSYM/InlineInfo.h" +#include "llvm/DebugInfo/GSYM/LineTable.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace gsym; + +GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) : + MemBuffer(std::move(Buffer)), + Endian(support::endian::system_endianness()) {} + + GsymReader::GsymReader(GsymReader &&RHS) = default; + +GsymReader::~GsymReader() = default; + +llvm::Expected<GsymReader> GsymReader::openFile(StringRef Filename) { + // Open the input file and return an appropriate error if needed. + ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + auto Err = BuffOrErr.getError(); + if (Err) + return llvm::errorCodeToError(Err); + return create(BuffOrErr.get()); +} + +llvm::Expected<GsymReader> GsymReader::copyBuffer(StringRef Bytes) { + auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes"); + return create(MemBuffer); +} + +llvm::Expected<llvm::gsym::GsymReader> +GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) { + if (!MemBuffer.get()) + return createStringError(std::errc::invalid_argument, + "invalid memory buffer"); + GsymReader GR(std::move(MemBuffer)); + llvm::Error Err = GR.parse(); + if (Err) + return std::move(Err); + return std::move(GR); +} + +llvm::Error +GsymReader::parse() { + BinaryStreamReader FileData(MemBuffer->getBuffer(), + support::endian::system_endianness()); + // Check for the magic bytes. This file format is designed to be mmap'ed + // into a process and accessed as read only. This is done for performance + // and efficiency for symbolicating and parsing GSYM data. + if (FileData.readObject(Hdr)) + return createStringError(std::errc::invalid_argument, + "not enough data for a GSYM header"); + + const auto HostByteOrder = support::endian::system_endianness(); + switch (Hdr->Magic) { + case GSYM_MAGIC: + Endian = HostByteOrder; + break; + case GSYM_CIGAM: + // This is a GSYM file, but not native endianness. + Endian = sys::IsBigEndianHost ? support::little : support::big; + Swap.reset(new SwappedData); + break; + default: + return createStringError(std::errc::invalid_argument, + "not a GSYM file"); + } + + bool DataIsLittleEndian = HostByteOrder != support::little; + // Read a correctly byte swapped header if we need to. + if (Swap) { + DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4); + if (auto ExpectedHdr = Header::decode(Data)) + Swap->Hdr = ExpectedHdr.get(); + else + return ExpectedHdr.takeError(); + Hdr = &Swap->Hdr; + } + + // Detect errors in the header and report any that are found. If we make it + // past this without errors, we know we have a good magic value, a supported + // version number, verified address offset size and a valid UUID size. + if (Error Err = Hdr->checkForError()) + return Err; + + if (!Swap) { + // This is the native endianness case that is most common and optimized for + // efficient lookups. Here we just grab pointers to the native data and + // use ArrayRef objects to allow efficient read only access. + + // Read the address offsets. + if (FileData.padToAlignment(Hdr->AddrOffSize) || + FileData.readArray(AddrOffsets, + Hdr->NumAddresses * Hdr->AddrOffSize)) + return createStringError(std::errc::invalid_argument, + "failed to read address table"); + + // Read the address info offsets. + if (FileData.padToAlignment(4) || + FileData.readArray(AddrInfoOffsets, Hdr->NumAddresses)) + return createStringError(std::errc::invalid_argument, + "failed to read address info offsets table"); + + // Read the file table. + uint32_t NumFiles = 0; + if (FileData.readInteger(NumFiles) || FileData.readArray(Files, NumFiles)) + return createStringError(std::errc::invalid_argument, + "failed to read file table"); + + // Get the string table. + FileData.setOffset(Hdr->StrtabOffset); + if (FileData.readFixedString(StrTab.Data, Hdr->StrtabSize)) + return createStringError(std::errc::invalid_argument, + "failed to read string table"); +} else { + // This is the non native endianness case that is not common and not + // optimized for lookups. Here we decode the important tables into local + // storage and then set the ArrayRef objects to point to these swapped + // copies of the read only data so lookups can be as efficient as possible. + DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4); + + // Read the address offsets. + uint64_t Offset = alignTo(sizeof(Header), Hdr->AddrOffSize); + Swap->AddrOffsets.resize(Hdr->NumAddresses * Hdr->AddrOffSize); + switch (Hdr->AddrOffSize) { + case 1: + if (!Data.getU8(&Offset, Swap->AddrOffsets.data(), Hdr->NumAddresses)) + return createStringError(std::errc::invalid_argument, + "failed to read address table"); + break; + case 2: + if (!Data.getU16(&Offset, + reinterpret_cast<uint16_t *>(Swap->AddrOffsets.data()), + Hdr->NumAddresses)) + return createStringError(std::errc::invalid_argument, + "failed to read address table"); + break; + case 4: + if (!Data.getU32(&Offset, + reinterpret_cast<uint32_t *>(Swap->AddrOffsets.data()), + Hdr->NumAddresses)) + return createStringError(std::errc::invalid_argument, + "failed to read address table"); + break; + case 8: + if (!Data.getU64(&Offset, + reinterpret_cast<uint64_t *>(Swap->AddrOffsets.data()), + Hdr->NumAddresses)) + return createStringError(std::errc::invalid_argument, + "failed to read address table"); + } + AddrOffsets = ArrayRef<uint8_t>(Swap->AddrOffsets); + + // Read the address info offsets. + Offset = alignTo(Offset, 4); + Swap->AddrInfoOffsets.resize(Hdr->NumAddresses); + if (Data.getU32(&Offset, Swap->AddrInfoOffsets.data(), Hdr->NumAddresses)) + AddrInfoOffsets = ArrayRef<uint32_t>(Swap->AddrInfoOffsets); + else + return createStringError(std::errc::invalid_argument, + "failed to read address table"); + // Read the file table. + const uint32_t NumFiles = Data.getU32(&Offset); + if (NumFiles > 0) { + Swap->Files.resize(NumFiles); + if (Data.getU32(&Offset, &Swap->Files[0].Dir, NumFiles*2)) + Files = ArrayRef<FileEntry>(Swap->Files); + else + return createStringError(std::errc::invalid_argument, + "failed to read file table"); + } + // Get the string table. + StrTab.Data = MemBuffer->getBuffer().substr(Hdr->StrtabOffset, + Hdr->StrtabSize); + if (StrTab.Data.empty()) + return createStringError(std::errc::invalid_argument, + "failed to read string table"); + } + return Error::success(); + +} + +const Header &GsymReader::getHeader() const { + // The only way to get a GsymReader is from GsymReader::openFile(...) or + // GsymReader::copyBuffer() and the header must be valid and initialized to + // a valid pointer value, so the assert below should not trigger. + assert(Hdr); + return *Hdr; +} + +Optional<uint64_t> GsymReader::getAddress(size_t Index) const { + switch (Hdr->AddrOffSize) { + case 1: return addressForIndex<uint8_t>(Index); + case 2: return addressForIndex<uint16_t>(Index); + case 4: return addressForIndex<uint32_t>(Index); + case 8: return addressForIndex<uint64_t>(Index); + } + return llvm::None; +} + +Optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const { + const auto NumAddrInfoOffsets = AddrInfoOffsets.size(); + if (Index < NumAddrInfoOffsets) + return AddrInfoOffsets[Index]; + return llvm::None; +} + +Expected<uint64_t> +GsymReader::getAddressIndex(const uint64_t Addr) const { + if (Addr < Hdr->BaseAddress) + return createStringError(std::errc::invalid_argument, + "address 0x%" PRIx64 " not in GSYM", Addr); + const uint64_t AddrOffset = Addr - Hdr->BaseAddress; + switch (Hdr->AddrOffSize) { + case 1: return getAddressOffsetIndex<uint8_t>(AddrOffset); + case 2: return getAddressOffsetIndex<uint16_t>(AddrOffset); + case 4: return getAddressOffsetIndex<uint32_t>(AddrOffset); + case 8: return getAddressOffsetIndex<uint64_t>(AddrOffset); + default: break; + } + return createStringError(std::errc::invalid_argument, + "unsupported address offset size %u", + Hdr->AddrOffSize); +} + +llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const { + Expected<uint64_t> AddressIndex = getAddressIndex(Addr); + if (!AddressIndex) + return AddressIndex.takeError(); + // Address info offsets size should have been checked in parse(). + assert(*AddressIndex < AddrInfoOffsets.size()); + auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex]; + DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4); + if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) { + auto ExpectedFI = FunctionInfo::decode(Data, *OptAddr); + if (ExpectedFI) { + if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0) + return ExpectedFI; + return createStringError(std::errc::invalid_argument, + "address 0x%" PRIx64 " not in GSYM", Addr); + } + } + return createStringError(std::errc::invalid_argument, + "failed to extract address[%" PRIu64 "]", + *AddressIndex); +} diff --git a/llvm/lib/DebugInfo/GSYM/Header.cpp b/llvm/lib/DebugInfo/GSYM/Header.cpp new file mode 100644 index 000000000000..0b3fb9c49894 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/Header.cpp @@ -0,0 +1,109 @@ +//===- Header.cpp -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/GSYM/Header.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#define HEX8(v) llvm::format_hex(v, 4) +#define HEX16(v) llvm::format_hex(v, 6) +#define HEX32(v) llvm::format_hex(v, 10) +#define HEX64(v) llvm::format_hex(v, 18) + +using namespace llvm; +using namespace gsym; + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const Header &H) { + OS << "Header:\n"; + OS << " Magic = " << HEX32(H.Magic) << "\n"; + OS << " Version = " << HEX16(H.Version) << '\n'; + OS << " AddrOffSize = " << HEX8(H.AddrOffSize) << '\n'; + OS << " UUIDSize = " << HEX8(H.UUIDSize) << '\n'; + OS << " BaseAddress = " << HEX64(H.BaseAddress) << '\n'; + OS << " NumAddresses = " << HEX32(H.NumAddresses) << '\n'; + OS << " StrtabOffset = " << HEX32(H.StrtabOffset) << '\n'; + OS << " StrtabSize = " << HEX32(H.StrtabSize) << '\n'; + OS << " UUID = "; + for (uint8_t I = 0; I < H.UUIDSize; ++I) + OS << format_hex_no_prefix(H.UUID[I], 2); + OS << '\n'; + return OS; +} + +/// Check the header and detect any errors. +llvm::Error Header::checkForError() const { + if (Magic != GSYM_MAGIC) + return createStringError(std::errc::invalid_argument, + "invalid GSYM magic 0x%8.8x", Magic); + if (Version != GSYM_VERSION) + return createStringError(std::errc::invalid_argument, + "unsupported GSYM version %u", Version); + switch (AddrOffSize) { + case 1: break; + case 2: break; + case 4: break; + case 8: break; + default: + return createStringError(std::errc::invalid_argument, + "invalid address offset size %u", + AddrOffSize); + } + if (UUIDSize > GSYM_MAX_UUID_SIZE) + return createStringError(std::errc::invalid_argument, + "invalid UUID size %u", UUIDSize); + return Error::success(); +} + +llvm::Expected<Header> Header::decode(DataExtractor &Data) { + uint64_t Offset = 0; + // The header is stored as a single blob of data that has a fixed byte size. + if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Header))) + return createStringError(std::errc::invalid_argument, + "not enough data for a gsym::Header"); + Header H; + H.Magic = Data.getU32(&Offset); + H.Version = Data.getU16(&Offset); + H.AddrOffSize = Data.getU8(&Offset); + H.UUIDSize = Data.getU8(&Offset); + H.BaseAddress = Data.getU64(&Offset); + H.NumAddresses = Data.getU32(&Offset); + H.StrtabOffset = Data.getU32(&Offset); + H.StrtabSize = Data.getU32(&Offset); + Data.getU8(&Offset, H.UUID, GSYM_MAX_UUID_SIZE); + if (llvm::Error Err = H.checkForError()) + return std::move(Err); + return H; +} + +llvm::Error Header::encode(FileWriter &O) const { + // Users must verify the Header is valid prior to calling this funtion. + if (llvm::Error Err = checkForError()) + return Err; + O.writeU32(Magic); + O.writeU16(Version); + O.writeU8(AddrOffSize); + O.writeU8(UUIDSize); + O.writeU64(BaseAddress); + O.writeU32(NumAddresses); + O.writeU32(StrtabOffset); + O.writeU32(StrtabSize); + O.writeData(llvm::ArrayRef<uint8_t>(UUID)); + return Error::success(); +} + +bool llvm::gsym::operator==(const Header &LHS, const Header &RHS) { + return LHS.Magic == RHS.Magic && LHS.Version == RHS.Version && + LHS.AddrOffSize == RHS.AddrOffSize && LHS.UUIDSize == RHS.UUIDSize && + LHS.BaseAddress == RHS.BaseAddress && + LHS.NumAddresses == RHS.NumAddresses && + LHS.StrtabOffset == RHS.StrtabOffset && + LHS.StrtabSize == RHS.StrtabSize && + memcmp(LHS.UUID, RHS.UUID, LHS.UUIDSize) == 0; +} diff --git a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp new file mode 100644 index 000000000000..32ed2c709575 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp @@ -0,0 +1,159 @@ +//===- InlineInfo.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/GSYM/FileEntry.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/InlineInfo.h" +#include "llvm/Support/DataExtractor.h" +#include <algorithm> +#include <inttypes.h> + +using namespace llvm; +using namespace gsym; + + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) { + if (!II.isValid()) + return OS; + bool First = true; + for (auto Range : II.Ranges) { + if (First) + First = false; + else + OS << ' '; + OS << Range; + } + OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile + << ", CallLine = " << II.CallFile << '\n'; + for (const auto &Child : II.Children) + OS << Child; + return OS; +} + +static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr, + std::vector<const InlineInfo *> &InlineStack) { + if (II.Ranges.contains(Addr)) { + // If this is the top level that represents the concrete function, + // there will be no name and we shoud clear the inline stack. Otherwise + // we have found an inline call stack that we need to insert. + if (II.Name != 0) + InlineStack.insert(InlineStack.begin(), &II); + for (const auto &Child : II.Children) { + if (::getInlineStackHelper(Child, Addr, InlineStack)) + break; + } + return !InlineStack.empty(); + } + return false; +} + +llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr) const { + InlineArray Result; + if (getInlineStackHelper(*this, Addr, Result)) + return Result; + return llvm::None; +} + +/// Decode an InlineInfo in Data at the specified offset. +/// +/// A local helper function to decode InlineInfo objects. This function is +/// called recursively when parsing child InlineInfo objects. +/// +/// \param Data The data extractor to decode from. +/// \param Offset The offset within \a Data to decode from. +/// \param BaseAddr The base address to use when decoding address ranges. +/// \returns An InlineInfo or an error describing the issue that was +/// encountered during decoding. +static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset, + uint64_t BaseAddr) { + InlineInfo Inline; + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset); + Inline.Ranges.decode(Data, BaseAddr, Offset); + if (Inline.Ranges.empty()) + return Inline; + if (!Data.isValidOffsetForDataOfSize(Offset, 1)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children", + Offset); + bool HasChildren = Data.getU8(&Offset) != 0; + if (!Data.isValidOffsetForDataOfSize(Offset, 4)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset); + Inline.Name = Data.getU32(&Offset); + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset); + Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset); + Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); + if (HasChildren) { + // Child address ranges are encoded relative to the first address in the + // parent InlineInfo object. + const auto ChildBaseAddr = Inline.Ranges[0].Start; + while (true) { + llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr); + if (!Child) + return Child.takeError(); + // InlineInfo with empty Ranges termintes a child sibling chain. + if (Child.get().Ranges.empty()) + break; + Inline.Children.emplace_back(std::move(*Child)); + } + } + return Inline; +} + +llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data, + uint64_t BaseAddr) { + uint64_t Offset = 0; + return ::decode(Data, Offset, BaseAddr); +} + +llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const { + // Users must verify the InlineInfo is valid prior to calling this funtion. + // We don't want to emit any InlineInfo objects if they are not valid since + // it will waste space in the GSYM file. + if (!isValid()) + return createStringError(std::errc::invalid_argument, + "attempted to encode invalid InlineInfo object"); + Ranges.encode(O, BaseAddr); + bool HasChildren = !Children.empty(); + O.writeU8(HasChildren); + O.writeU32(Name); + O.writeULEB(CallFile); + O.writeULEB(CallLine); + if (HasChildren) { + // Child address ranges are encoded as relative to the first + // address in the Ranges for this object. This keeps the offsets + // small and allows for efficient encoding using ULEB offsets. + const uint64_t ChildBaseAddr = Ranges[0].Start; + for (const auto &Child : Children) { + // Make sure all child address ranges are contained in the parent address + // ranges. + for (const auto &ChildRange: Child.Ranges) { + if (!Ranges.contains(ChildRange)) + return createStringError(std::errc::invalid_argument, + "child range not contained in parent"); + } + llvm::Error Err = Child.encode(O, ChildBaseAddr); + if (Err) + return Err; + } + + // Terminate child sibling chain by emitting a zero. This zero will cause + // the decodeAll() function above to return false and stop the decoding + // of child InlineInfo objects that are siblings. + O.writeULEB(0); + } + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/GSYM/LineTable.cpp b/llvm/lib/DebugInfo/GSYM/LineTable.cpp new file mode 100644 index 000000000000..824c0041be9f --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/LineTable.cpp @@ -0,0 +1,287 @@ +//===- LineTable.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/GSYM/LineTable.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/Support/DataExtractor.h" + +using namespace llvm; +using namespace gsym; + +enum LineTableOpCode { + EndSequence = 0x00, ///< End of the line table. + SetFile = 0x01, ///< Set LineTableRow.file_idx, don't push a row. + AdvancePC = 0x02, ///< Increment LineTableRow.address, and push a row. + AdvanceLine = 0x03, ///< Set LineTableRow.file_line, don't push a row. + FirstSpecial = 0x04, ///< All special opcodes push a row. +}; + +struct DeltaInfo { + int64_t Delta; + uint32_t Count; + DeltaInfo(int64_t D, uint32_t C) : Delta(D), Count(C) {} +}; + +inline bool operator<(const DeltaInfo &LHS, int64_t Delta) { + return LHS.Delta < Delta; +} + +static bool encodeSpecial(int64_t MinLineDelta, int64_t MaxLineDelta, + int64_t LineDelta, uint64_t AddrDelta, + uint8_t &SpecialOp) { + if (LineDelta < MinLineDelta) + return false; + if (LineDelta > MaxLineDelta) + return false; + int64_t LineRange = MaxLineDelta - MinLineDelta + 1; + int64_t AdjustedOp = ((LineDelta - MinLineDelta) + AddrDelta * LineRange); + int64_t Op = AdjustedOp + FirstSpecial; + if (Op < 0) + return false; + if (Op > 255) + return false; + SpecialOp = (uint8_t)Op; + return true; +} + +typedef std::function<bool(const LineEntry &Row)> LineEntryCallback; + +static llvm::Error parse(DataExtractor &Data, uint64_t BaseAddr, + LineEntryCallback const &Callback) { + uint64_t Offset = 0; + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing LineTable MinDelta", Offset); + int64_t MinDelta = Data.getSLEB128(&Offset); + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing LineTable MaxDelta", Offset); + int64_t MaxDelta = Data.getSLEB128(&Offset); + int64_t LineRange = MaxDelta - MinDelta + 1; + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": missing LineTable FirstLine", Offset); + const uint32_t FirstLine = (uint32_t)Data.getULEB128(&Offset); + LineEntry Row(BaseAddr, 1, FirstLine); + bool Done = false; + while (!Done) { + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": EOF found before EndSequence", Offset); + uint8_t Op = Data.getU8(&Offset); + switch (Op) { + case EndSequence: + Done = true; + break; + case SetFile: + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": EOF found before SetFile value", + Offset); + Row.File = (uint32_t)Data.getULEB128(&Offset); + break; + case AdvancePC: + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": EOF found before AdvancePC value", + Offset); + Row.Addr += Data.getULEB128(&Offset); + // If the function callback returns false, we stop parsing. + if (Callback(Row) == false) + return Error::success(); + break; + case AdvanceLine: + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": EOF found before AdvanceLine value", + Offset); + Row.Line += Data.getSLEB128(&Offset); + break; + default: { + // A byte that contains both address and line increment. + uint8_t AdjustedOp = Op - FirstSpecial; + int64_t LineDelta = MinDelta + (AdjustedOp % LineRange); + uint64_t AddrDelta = (AdjustedOp / LineRange); + Row.Line += LineDelta; + Row.Addr += AddrDelta; + // If the function callback returns false, we stop parsing. + if (Callback(Row) == false) + return Error::success(); + break; + } + } + } + return Error::success(); +} + +llvm::Error LineTable::encode(FileWriter &Out, uint64_t BaseAddr) const { + // Users must verify the LineTable is valid prior to calling this funtion. + // We don't want to emit any LineTable objects if they are not valid since + // it will waste space in the GSYM file. + if (!isValid()) + return createStringError(std::errc::invalid_argument, + "attempted to encode invalid LineTable object"); + + int64_t MinLineDelta = INT64_MAX; + int64_t MaxLineDelta = INT64_MIN; + std::vector<DeltaInfo> DeltaInfos; + if (Lines.size() == 1) { + MinLineDelta = 0; + MaxLineDelta = 0; + } else { + int64_t PrevLine = 1; + bool First = true; + for (const auto &line_entry : Lines) { + if (First) + First = false; + else { + int64_t LineDelta = (int64_t)line_entry.Line - PrevLine; + auto End = DeltaInfos.end(); + auto Pos = std::lower_bound(DeltaInfos.begin(), End, LineDelta); + if (Pos != End && Pos->Delta == LineDelta) + ++Pos->Count; + else + DeltaInfos.insert(Pos, DeltaInfo(LineDelta, 1)); + if (LineDelta < MinLineDelta) + MinLineDelta = LineDelta; + if (LineDelta > MaxLineDelta) + MaxLineDelta = LineDelta; + } + PrevLine = (int64_t)line_entry.Line; + } + assert(MinLineDelta <= MaxLineDelta); + } + // Set the min and max line delta intelligently based on the counts of + // the line deltas. if our range is too large. + const int64_t MaxLineRange = 14; + if (MaxLineDelta - MinLineDelta > MaxLineRange) { + uint32_t BestIndex = 0; + uint32_t BestEndIndex = 0; + uint32_t BestCount = 0; + const size_t NumDeltaInfos = DeltaInfos.size(); + for (uint32_t I = 0; I < NumDeltaInfos; ++I) { + const int64_t FirstDelta = DeltaInfos[I].Delta; + uint32_t CurrCount = 0; + uint32_t J; + for (J = I; J < NumDeltaInfos; ++J) { + auto LineRange = DeltaInfos[J].Delta - FirstDelta; + if (LineRange > MaxLineRange) + break; + CurrCount += DeltaInfos[J].Count; + } + if (CurrCount > BestCount) { + BestIndex = I; + BestEndIndex = J - 1; + BestCount = CurrCount; + } + } + MinLineDelta = DeltaInfos[BestIndex].Delta; + MaxLineDelta = DeltaInfos[BestEndIndex].Delta; + } + if (MinLineDelta == MaxLineDelta && MinLineDelta > 0 && + MinLineDelta < MaxLineRange) + MinLineDelta = 0; + assert(MinLineDelta <= MaxLineDelta); + + // Initialize the line entry state as a starting point. All line entries + // will be deltas from this. + LineEntry Prev(BaseAddr, 1, Lines.front().Line); + + // Write out the min and max line delta as signed LEB128. + Out.writeSLEB(MinLineDelta); + Out.writeSLEB(MaxLineDelta); + // Write out the starting line number as a unsigned LEB128. + Out.writeULEB(Prev.Line); + + for (const auto &Curr : Lines) { + if (Curr.Addr < BaseAddr) + return createStringError(std::errc::invalid_argument, + "LineEntry has address 0x%" PRIx64 " which is " + "less than the function start address 0x%" + PRIx64, Curr.Addr, BaseAddr); + if (Curr.Addr < Prev.Addr) + return createStringError(std::errc::invalid_argument, + "LineEntry in LineTable not in ascending order"); + const uint64_t AddrDelta = Curr.Addr - Prev.Addr; + int64_t LineDelta = 0; + if (Curr.Line > Prev.Line) + LineDelta = Curr.Line - Prev.Line; + else if (Prev.Line > Curr.Line) + LineDelta = -((int32_t)(Prev.Line - Curr.Line)); + + // Set the file if it doesn't match the current one. + if (Curr.File != Prev.File) { + Out.writeU8(SetFile); + Out.writeULEB(Curr.File); + } + + uint8_t SpecialOp; + if (encodeSpecial(MinLineDelta, MaxLineDelta, LineDelta, AddrDelta, + SpecialOp)) { + // Advance the PC and line and push a row. + Out.writeU8(SpecialOp); + } else { + // We can't encode the address delta and line delta into + // a single special opcode, we must do them separately. + + // Advance the line. + if (LineDelta != 0) { + Out.writeU8(AdvanceLine); + Out.writeSLEB(LineDelta); + } + + // Advance the PC and push a row. + Out.writeU8(AdvancePC); + Out.writeULEB(AddrDelta); + } + Prev = Curr; + } + Out.writeU8(EndSequence); + return Error::success(); +} + +// Parse all line table entries into the "LineTable" vector. We can +// cache the results of this if needed, or we can call LineTable::lookup() +// below. +llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data, + uint64_t BaseAddr) { + LineTable LT; + llvm::Error Err = parse(Data, BaseAddr, [&](const LineEntry &Row) -> bool { + LT.Lines.push_back(Row); + return true; // Keep parsing by returning true. + }); + if (Err) + return std::move(Err); + return LT; +} +// Parse the line table on the fly and find the row we are looking for. +// We will need to determine if we need to cache the line table by calling +// LineTable::parseAllEntries(...) or just call this function each time. +// There is a CPU vs memory tradeoff we will need to determine. +LineEntry LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) { + LineEntry Result; + llvm::Error Err = parse(Data, BaseAddr, + [Addr, &Result](const LineEntry &Row) -> bool { + if (Addr < Row.Addr) + return false; // Stop parsing, result contains the line table row! + Result = Row; + if (Addr == Row.Addr) { + // Stop parsing, this is the row we are looking for since the address + // matches. + return false; + } + return true; // Keep parsing till we find the right row. + }); + return Result; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable <) { + for (const auto &LineEntry : LT) + OS << LineEntry << '\n'; + return OS; +} diff --git a/llvm/lib/DebugInfo/GSYM/Range.cpp b/llvm/lib/DebugInfo/GSYM/Range.cpp new file mode 100644 index 000000000000..19ab700fdd57 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/Range.cpp @@ -0,0 +1,102 @@ +//===- Range.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/GSYM/Range.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/Support/DataExtractor.h" +#include <algorithm> +#include <inttypes.h> + +using namespace llvm; +using namespace gsym; + + +void AddressRanges::insert(AddressRange Range) { + if (Range.size() == 0) + return; + + auto It = llvm::upper_bound(Ranges, Range); + auto It2 = It; + while (It2 != Ranges.end() && It2->Start < Range.End) + ++It2; + if (It != It2) { + Range.End = std::max(Range.End, It2[-1].End); + It = Ranges.erase(It, It2); + } + if (It != Ranges.begin() && Range.Start < It[-1].End) + It[-1].End = std::max(It[-1].End, Range.End); + else + Ranges.insert(It, Range); +} + +bool AddressRanges::contains(uint64_t Addr) const { + auto It = std::partition_point( + Ranges.begin(), Ranges.end(), + [=](const AddressRange &R) { return R.Start <= Addr; }); + return It != Ranges.begin() && Addr < It[-1].End; +} + +bool AddressRanges::contains(AddressRange Range) const { + if (Range.size() == 0) + return false; + auto It = std::partition_point( + Ranges.begin(), Ranges.end(), + [=](const AddressRange &R) { return R.Start <= Range.Start; }); + if (It == Ranges.begin()) + return false; + return Range.End <= It[-1].End; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const AddressRange &R) { + return OS << '[' << HEX64(R.Start) << " - " << HEX64(R.End) << ")"; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const AddressRanges &AR) { + size_t Size = AR.size(); + for (size_t I = 0; I < Size; ++I) { + if (I) + OS << ' '; + OS << AR[I]; + } + return OS; +} + +void AddressRange::encode(FileWriter &O, uint64_t BaseAddr) const { + assert(Start >= BaseAddr); + O.writeULEB(Start - BaseAddr); + O.writeULEB(size()); +} + +void AddressRange::decode(DataExtractor &Data, uint64_t BaseAddr, + uint64_t &Offset) { + const uint64_t AddrOffset = Data.getULEB128(&Offset); + const uint64_t Size = Data.getULEB128(&Offset); + const uint64_t StartAddr = BaseAddr + AddrOffset; + Start = StartAddr; + End = StartAddr + Size; +} + +void AddressRanges::encode(FileWriter &O, uint64_t BaseAddr) const { + O.writeULEB(Ranges.size()); + if (Ranges.empty()) + return; + for (auto Range : Ranges) + Range.encode(O, BaseAddr); +} + +void AddressRanges::decode(DataExtractor &Data, uint64_t BaseAddr, + uint64_t &Offset) { + clear(); + uint64_t NumRanges = Data.getULEB128(&Offset); + if (NumRanges == 0) + return; + Ranges.resize(NumRanges); + for (auto &Range : Ranges) + Range.decode(Data, BaseAddr, Offset); +} diff --git a/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp b/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp new file mode 100644 index 000000000000..c6fe764ab7e0 --- /dev/null +++ b/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp @@ -0,0 +1,381 @@ +//===- MSFBuilder.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <memory> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; + +static const uint32_t kSuperBlockBlock = 0; +static const uint32_t kFreePageMap0Block = 1; +static const uint32_t kFreePageMap1Block = 2; +static const uint32_t kNumReservedPages = 3; + +static const uint32_t kDefaultFreePageMap = kFreePageMap1Block; +static const uint32_t kDefaultBlockMapAddr = kNumReservedPages; + +MSFBuilder::MSFBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow, + BumpPtrAllocator &Allocator) + : Allocator(Allocator), IsGrowable(CanGrow), + FreePageMap(kDefaultFreePageMap), BlockSize(BlockSize), + BlockMapAddr(kDefaultBlockMapAddr), FreeBlocks(MinBlockCount, true) { + FreeBlocks[kSuperBlockBlock] = false; + FreeBlocks[kFreePageMap0Block] = false; + FreeBlocks[kFreePageMap1Block] = false; + FreeBlocks[BlockMapAddr] = false; +} + +Expected<MSFBuilder> MSFBuilder::create(BumpPtrAllocator &Allocator, + uint32_t BlockSize, + uint32_t MinBlockCount, bool CanGrow) { + if (!isValidBlockSize(BlockSize)) + return make_error<MSFError>(msf_error_code::invalid_format, + "The requested block size is unsupported"); + + return MSFBuilder(BlockSize, + std::max(MinBlockCount, msf::getMinimumBlockCount()), + CanGrow, Allocator); +} + +Error MSFBuilder::setBlockMapAddr(uint32_t Addr) { + if (Addr == BlockMapAddr) + return Error::success(); + + if (Addr >= FreeBlocks.size()) { + if (!IsGrowable) + return make_error<MSFError>(msf_error_code::insufficient_buffer, + "Cannot grow the number of blocks"); + FreeBlocks.resize(Addr + 1, true); + } + + if (!isBlockFree(Addr)) + return make_error<MSFError>( + msf_error_code::block_in_use, + "Requested block map address is already in use"); + FreeBlocks[BlockMapAddr] = true; + FreeBlocks[Addr] = false; + BlockMapAddr = Addr; + return Error::success(); +} + +void MSFBuilder::setFreePageMap(uint32_t Fpm) { FreePageMap = Fpm; } + +void MSFBuilder::setUnknown1(uint32_t Unk1) { Unknown1 = Unk1; } + +Error MSFBuilder::setDirectoryBlocksHint(ArrayRef<uint32_t> DirBlocks) { + for (auto B : DirectoryBlocks) + FreeBlocks[B] = true; + for (auto B : DirBlocks) { + if (!isBlockFree(B)) { + return make_error<MSFError>(msf_error_code::unspecified, + "Attempt to reuse an allocated block"); + } + FreeBlocks[B] = false; + } + + DirectoryBlocks = DirBlocks; + return Error::success(); +} + +Error MSFBuilder::allocateBlocks(uint32_t NumBlocks, + MutableArrayRef<uint32_t> Blocks) { + if (NumBlocks == 0) + return Error::success(); + + uint32_t NumFreeBlocks = FreeBlocks.count(); + if (NumFreeBlocks < NumBlocks) { + if (!IsGrowable) + return make_error<MSFError>(msf_error_code::insufficient_buffer, + "There are no free Blocks in the file"); + uint32_t AllocBlocks = NumBlocks - NumFreeBlocks; + 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. FPM blocks are marked as allocated regardless of whether or not + // they ultimately describe the status of blocks in the file. This means + // that not only are extraneous blocks at the end of the main FPM marked as + // allocated, but also blocks from the alternate FPM are always marked as + // allocated. + while (NextFpmBlock < NewBlockCount) { + NewBlockCount += 2; + FreeBlocks.resize(NewBlockCount, true); + FreeBlocks.reset(NextFpmBlock, NextFpmBlock + 2); + NextFpmBlock += BlockSize; + } + } + + int I = 0; + int Block = FreeBlocks.find_first(); + do { + assert(Block != -1 && "We ran out of Blocks!"); + + uint32_t NextBlock = static_cast<uint32_t>(Block); + Blocks[I++] = NextBlock; + FreeBlocks.reset(NextBlock); + Block = FreeBlocks.find_next(Block); + } while (--NumBlocks > 0); + return Error::success(); +} + +uint32_t MSFBuilder::getNumUsedBlocks() const { + return getTotalBlockCount() - getNumFreeBlocks(); +} + +uint32_t MSFBuilder::getNumFreeBlocks() const { return FreeBlocks.count(); } + +uint32_t MSFBuilder::getTotalBlockCount() const { return FreeBlocks.size(); } + +bool MSFBuilder::isBlockFree(uint32_t Idx) const { return FreeBlocks[Idx]; } + +Expected<uint32_t> MSFBuilder::addStream(uint32_t Size, + ArrayRef<uint32_t> Blocks) { + // Add a new stream mapped to the specified blocks. Verify that the specified + // blocks are both necessary and sufficient for holding the requested number + // of bytes, and verify that all requested blocks are free. + uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize); + if (ReqBlocks != Blocks.size()) + return make_error<MSFError>( + msf_error_code::invalid_format, + "Incorrect number of blocks for requested stream size"); + for (auto Block : Blocks) { + if (Block >= FreeBlocks.size()) + FreeBlocks.resize(Block + 1, true); + + if (!FreeBlocks.test(Block)) + return make_error<MSFError>( + msf_error_code::unspecified, + "Attempt to re-use an already allocated block"); + } + // Mark all the blocks occupied by the new stream as not free. + for (auto Block : Blocks) { + FreeBlocks.reset(Block); + } + StreamData.push_back(std::make_pair(Size, Blocks)); + return StreamData.size() - 1; +} + +Expected<uint32_t> MSFBuilder::addStream(uint32_t Size) { + uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize); + std::vector<uint32_t> NewBlocks; + NewBlocks.resize(ReqBlocks); + if (auto EC = allocateBlocks(ReqBlocks, NewBlocks)) + return std::move(EC); + StreamData.push_back(std::make_pair(Size, NewBlocks)); + return StreamData.size() - 1; +} + +Error MSFBuilder::setStreamSize(uint32_t Idx, uint32_t Size) { + uint32_t OldSize = getStreamSize(Idx); + if (OldSize == Size) + return Error::success(); + + uint32_t NewBlocks = bytesToBlocks(Size, BlockSize); + uint32_t OldBlocks = bytesToBlocks(OldSize, BlockSize); + + if (NewBlocks > OldBlocks) { + uint32_t AddedBlocks = NewBlocks - OldBlocks; + // If we're growing, we have to allocate new Blocks. + std::vector<uint32_t> AddedBlockList; + AddedBlockList.resize(AddedBlocks); + if (auto EC = allocateBlocks(AddedBlocks, AddedBlockList)) + return EC; + auto &CurrentBlocks = StreamData[Idx].second; + CurrentBlocks.insert(CurrentBlocks.end(), AddedBlockList.begin(), + AddedBlockList.end()); + } else if (OldBlocks > NewBlocks) { + // For shrinking, free all the Blocks in the Block map, update the stream + // data, then shrink the directory. + uint32_t RemovedBlocks = OldBlocks - NewBlocks; + auto CurrentBlocks = ArrayRef<uint32_t>(StreamData[Idx].second); + auto RemovedBlockList = CurrentBlocks.drop_front(NewBlocks); + for (auto P : RemovedBlockList) + FreeBlocks[P] = true; + StreamData[Idx].second = CurrentBlocks.drop_back(RemovedBlocks); + } + + StreamData[Idx].first = Size; + return Error::success(); +} + +uint32_t MSFBuilder::getNumStreams() const { return StreamData.size(); } + +uint32_t MSFBuilder::getStreamSize(uint32_t StreamIdx) const { + return StreamData[StreamIdx].first; +} + +ArrayRef<uint32_t> MSFBuilder::getStreamBlocks(uint32_t StreamIdx) const { + return StreamData[StreamIdx].second; +} + +uint32_t MSFBuilder::computeDirectoryByteSize() const { + // The directory has the following layout, where each item is a ulittle32_t: + // NumStreams + // StreamSizes[NumStreams] + // StreamBlocks[NumStreams][] + uint32_t Size = sizeof(ulittle32_t); // NumStreams + Size += StreamData.size() * sizeof(ulittle32_t); // StreamSizes + for (const auto &D : StreamData) { + uint32_t ExpectedNumBlocks = bytesToBlocks(D.first, BlockSize); + assert(ExpectedNumBlocks == D.second.size() && + "Unexpected number of blocks"); + Size += ExpectedNumBlocks * sizeof(ulittle32_t); + } + return Size; +} + +Expected<MSFLayout> MSFBuilder::generateLayout() { + SuperBlock *SB = Allocator.Allocate<SuperBlock>(); + MSFLayout L; + L.SB = SB; + + std::memcpy(SB->MagicBytes, Magic, sizeof(Magic)); + SB->BlockMapAddr = BlockMapAddr; + SB->BlockSize = BlockSize; + SB->NumDirectoryBytes = computeDirectoryByteSize(); + SB->FreeBlockMapBlock = FreePageMap; + SB->Unknown1 = Unknown1; + + uint32_t NumDirectoryBlocks = bytesToBlocks(SB->NumDirectoryBytes, BlockSize); + if (NumDirectoryBlocks > DirectoryBlocks.size()) { + // Our hint wasn't enough to satisfy the entire directory. Allocate + // remaining pages. + std::vector<uint32_t> ExtraBlocks; + uint32_t NumExtraBlocks = NumDirectoryBlocks - DirectoryBlocks.size(); + ExtraBlocks.resize(NumExtraBlocks); + if (auto EC = allocateBlocks(NumExtraBlocks, ExtraBlocks)) + return std::move(EC); + DirectoryBlocks.insert(DirectoryBlocks.end(), ExtraBlocks.begin(), + ExtraBlocks.end()); + } else if (NumDirectoryBlocks < DirectoryBlocks.size()) { + uint32_t NumUnnecessaryBlocks = DirectoryBlocks.size() - NumDirectoryBlocks; + for (auto B : + ArrayRef<uint32_t>(DirectoryBlocks).drop_back(NumUnnecessaryBlocks)) + FreeBlocks[B] = true; + DirectoryBlocks.resize(NumDirectoryBlocks); + } + + // Don't set the number of blocks in the file until after allocating Blocks + // for the directory, since the allocation might cause the file to need to + // grow. + SB->NumBlocks = FreeBlocks.size(); + + ulittle32_t *DirBlocks = Allocator.Allocate<ulittle32_t>(NumDirectoryBlocks); + std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks, + DirBlocks); + L.DirectoryBlocks = ArrayRef<ulittle32_t>(DirBlocks, NumDirectoryBlocks); + + // The stream sizes should be re-allocated as a stable pointer and the stream + // map should have each of its entries allocated as a separate stable pointer. + if (!StreamData.empty()) { + ulittle32_t *Sizes = Allocator.Allocate<ulittle32_t>(StreamData.size()); + L.StreamSizes = ArrayRef<ulittle32_t>(Sizes, StreamData.size()); + L.StreamMap.resize(StreamData.size()); + for (uint32_t I = 0; I < StreamData.size(); ++I) { + Sizes[I] = StreamData[I].first; + ulittle32_t *BlockList = + Allocator.Allocate<ulittle32_t>(StreamData[I].second.size()); + std::uninitialized_copy_n(StreamData[I].second.begin(), + StreamData[I].second.size(), BlockList); + L.StreamMap[I] = + ArrayRef<ulittle32_t>(BlockList, StreamData[I].second.size()); + } + } + + L.FreePageMap = FreeBlocks; + + return L; +} + +static void commitFpm(WritableBinaryStream &MsfBuffer, const MSFLayout &Layout, + BumpPtrAllocator &Allocator) { + 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); +} + +Expected<FileBufferByteStream> MSFBuilder::commit(StringRef Path, + MSFLayout &Layout) { + Expected<MSFLayout> L = generateLayout(); + if (!L) + return L.takeError(); + + Layout = std::move(*L); + + uint64_t FileSize = Layout.SB->BlockSize * Layout.SB->NumBlocks; + auto OutFileOrError = FileOutputBuffer::create(Path, FileSize); + if (auto EC = OutFileOrError.takeError()) + return std::move(EC); + + FileBufferByteStream Buffer(std::move(*OutFileOrError), + llvm::support::little); + BinaryStreamWriter Writer(Buffer); + + if (auto EC = Writer.writeObject(*Layout.SB)) + return std::move(EC); + + commitFpm(Buffer, Layout, Allocator); + + uint32_t BlockMapOffset = + msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize); + Writer.setOffset(BlockMapOffset); + if (auto EC = Writer.writeArray(Layout.DirectoryBlocks)) + return std::move(EC); + + auto DirStream = WritableMappedBlockStream::createDirectoryStream( + Layout, Buffer, Allocator); + BinaryStreamWriter DW(*DirStream); + if (auto EC = DW.writeInteger<uint32_t>(Layout.StreamSizes.size())) + return std::move(EC); + + if (auto EC = DW.writeArray(Layout.StreamSizes)) + return std::move(EC); + + for (const auto &Blocks : Layout.StreamMap) { + if (auto EC = DW.writeArray(Blocks)) + return std::move(EC); + } + + return std::move(Buffer); +} diff --git a/llvm/lib/DebugInfo/MSF/MSFCommon.cpp b/llvm/lib/DebugInfo/MSF/MSFCommon.cpp new file mode 100644 index 000000000000..fb4f0700059c --- /dev/null +++ b/llvm/lib/DebugInfo/MSF/MSFCommon.cpp @@ -0,0 +1,82 @@ +//===- MSFCommon.cpp - Common types and functions for MSF files -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <cstdint> +#include <cstring> + +using namespace llvm; +using namespace llvm::msf; + +Error llvm::msf::validateSuperBlock(const SuperBlock &SB) { + // Check the magic bytes. + if (std::memcmp(SB.MagicBytes, Magic, sizeof(Magic)) != 0) + return make_error<MSFError>(msf_error_code::invalid_format, + "MSF magic header doesn't match"); + + if (!isValidBlockSize(SB.BlockSize)) + return make_error<MSFError>(msf_error_code::invalid_format, + "Unsupported block size."); + + // We don't support directories whose sizes aren't a multiple of four bytes. + if (SB.NumDirectoryBytes % sizeof(support::ulittle32_t) != 0) + return make_error<MSFError>(msf_error_code::invalid_format, + "Directory size is not multiple of 4."); + + // The number of blocks which comprise the directory is a simple function of + // the number of bytes it contains. + uint64_t NumDirectoryBlocks = + bytesToBlocks(SB.NumDirectoryBytes, SB.BlockSize); + + // The directory, as we understand it, is a block which consists of a list of + // block numbers. It is unclear what would happen if the number of blocks + // couldn't fit on a single block. + if (NumDirectoryBlocks > SB.BlockSize / sizeof(support::ulittle32_t)) + return make_error<MSFError>(msf_error_code::invalid_format, + "Too many directory blocks."); + + if (SB.BlockMapAddr == 0) + return make_error<MSFError>(msf_error_code::invalid_format, + "Block 0 is reserved"); + + if (SB.BlockMapAddr >= SB.NumBlocks) + return make_error<MSFError>(msf_error_code::invalid_format, + "Block map address is invalid."); + + if (SB.FreeBlockMapBlock != 1 && SB.FreeBlockMapBlock != 2) + return make_error<MSFError>( + msf_error_code::invalid_format, + "The free block map isn't at block 1 or block 2."); + + return Error::success(); +} + +MSFStreamLayout llvm::msf::getFpmStreamLayout(const MSFLayout &Msf, + bool IncludeUnusedFpmData, + bool AltFpm) { + MSFStreamLayout FL; + uint32_t NumFpmIntervals = + getNumFpmIntervals(Msf, IncludeUnusedFpmData, AltFpm); + + uint32_t FpmBlock = AltFpm ? Msf.alternateFpmBlock() : Msf.mainFpmBlock(); + + for (uint32_t I = 0; I < NumFpmIntervals; ++I) { + FL.Blocks.push_back(support::ulittle32_t(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/llvm/lib/DebugInfo/MSF/MSFError.cpp b/llvm/lib/DebugInfo/MSF/MSFError.cpp new file mode 100644 index 000000000000..b368b802c564 --- /dev/null +++ b/llvm/lib/DebugInfo/MSF/MSFError.cpp @@ -0,0 +1,47 @@ +//===- MSFError.cpp - Error extensions for MSF files ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::msf; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class MSFErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.msf"; } + std::string message(int Condition) const override { + switch (static_cast<msf_error_code>(Condition)) { + case msf_error_code::unspecified: + return "An unknown error has occurred."; + case msf_error_code::insufficient_buffer: + return "The buffer is not large enough to read the requested number of " + "bytes."; + case msf_error_code::not_writable: + return "The specified stream is not writable."; + case msf_error_code::no_stream: + return "The specified stream does not exist."; + case msf_error_code::invalid_format: + return "The data is in an unexpected format."; + case msf_error_code::block_in_use: + return "The block is already in use."; + } + llvm_unreachable("Unrecognized msf_error_code"); + } +}; +} // namespace + +static llvm::ManagedStatic<MSFErrorCategory> MSFCategory; +const std::error_category &llvm::msf::MSFErrCategory() { return *MSFCategory; } + +char MSFError::ID; diff --git a/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp b/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp new file mode 100644 index 000000000000..5dc9c86b34fd --- /dev/null +++ b/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp @@ -0,0 +1,421 @@ +//===- MappedBlockStream.cpp - Reads stream data from an MSF file ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace llvm::msf; + +namespace { + +template <typename Base> class MappedBlockStreamImpl : public Base { +public: + template <typename... Args> + MappedBlockStreamImpl(Args &&... Params) + : Base(std::forward<Args>(Params)...) {} +}; + +} // end anonymous namespace + +using Interval = std::pair<uint32_t, uint32_t>; + +static Interval intersect(const Interval &I1, const Interval &I2) { + return std::make_pair(std::max(I1.first, I2.first), + std::min(I1.second, I2.second)); +} + +MappedBlockStream::MappedBlockStream(uint32_t BlockSize, + const MSFStreamLayout &Layout, + BinaryStreamRef MsfData, + BumpPtrAllocator &Allocator) + : BlockSize(BlockSize), StreamLayout(Layout), MsfData(MsfData), + Allocator(Allocator) {} + +std::unique_ptr<MappedBlockStream> MappedBlockStream::createStream( + uint32_t BlockSize, const MSFStreamLayout &Layout, BinaryStreamRef MsfData, + BumpPtrAllocator &Allocator) { + return std::make_unique<MappedBlockStreamImpl<MappedBlockStream>>( + BlockSize, Layout, MsfData, Allocator); +} + +std::unique_ptr<MappedBlockStream> MappedBlockStream::createIndexedStream( + const MSFLayout &Layout, BinaryStreamRef MsfData, uint32_t StreamIndex, + BumpPtrAllocator &Allocator) { + assert(StreamIndex < Layout.StreamMap.size() && "Invalid stream index"); + MSFStreamLayout SL; + SL.Blocks = Layout.StreamMap[StreamIndex]; + SL.Length = Layout.StreamSizes[StreamIndex]; + return std::make_unique<MappedBlockStreamImpl<MappedBlockStream>>( + Layout.SB->BlockSize, SL, MsfData, Allocator); +} + +std::unique_ptr<MappedBlockStream> +MappedBlockStream::createDirectoryStream(const MSFLayout &Layout, + BinaryStreamRef MsfData, + BumpPtrAllocator &Allocator) { + MSFStreamLayout SL; + SL.Blocks = Layout.DirectoryBlocks; + SL.Length = Layout.SB->NumDirectoryBytes; + return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator); +} + +std::unique_ptr<MappedBlockStream> +MappedBlockStream::createFpmStream(const MSFLayout &Layout, + BinaryStreamRef MsfData, + BumpPtrAllocator &Allocator) { + 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 = checkOffsetForRead(Offset, Size)) + return EC; + + if (tryReadContiguously(Offset, Size, Buffer)) + return Error::success(); + + auto CacheIter = CacheMap.find(Offset); + if (CacheIter != CacheMap.end()) { + // Try to find an alloc that was large enough for this request. + for (auto &Entry : CacheIter->second) { + if (Entry.size() >= Size) { + Buffer = Entry.slice(0, Size); + return Error::success(); + } + } + } + + // We couldn't find a buffer that started at the correct offset (the most + // common scenario). Try to see if there is a buffer that starts at some + // other offset but overlaps the desired range. + for (auto &CacheItem : CacheMap) { + Interval RequestExtent = std::make_pair(Offset, Offset + Size); + + // We already checked this one on the fast path above. + if (CacheItem.first == Offset) + continue; + // If the initial extent of the cached item is beyond the ending extent + // of the request, there is no overlap. + if (CacheItem.first >= Offset + Size) + continue; + + // We really only have to check the last item in the list, since we append + // in order of increasing length. + if (CacheItem.second.empty()) + continue; + + auto CachedAlloc = CacheItem.second.back(); + // If the initial extent of the request is beyond the ending extent of + // the cached item, there is no overlap. + Interval CachedExtent = + std::make_pair(CacheItem.first, CacheItem.first + CachedAlloc.size()); + if (RequestExtent.first >= CachedExtent.first + CachedExtent.second) + continue; + + Interval Intersection = intersect(CachedExtent, RequestExtent); + // Only use this if the entire request extent is contained in the cached + // extent. + if (Intersection != RequestExtent) + continue; + + uint32_t CacheRangeOffset = + AbsoluteDifference(CachedExtent.first, Intersection.first); + Buffer = CachedAlloc.slice(CacheRangeOffset, Size); + return Error::success(); + } + + // Otherwise allocate a large enough buffer in the pool, memcpy the data + // into it, and return an ArrayRef to that. Do not touch existing pool + // allocations, as existing clients may be holding a pointer which must + // not be invalidated. + uint8_t *WriteBuffer = static_cast<uint8_t *>(Allocator.Allocate(Size, 8)); + if (auto EC = readBytes(Offset, MutableArrayRef<uint8_t>(WriteBuffer, Size))) + return EC; + + if (CacheIter != CacheMap.end()) { + CacheIter->second.emplace_back(WriteBuffer, Size); + } else { + std::vector<CacheEntry> List; + List.emplace_back(WriteBuffer, Size); + CacheMap.insert(std::make_pair(Offset, List)); + } + Buffer = ArrayRef<uint8_t>(WriteBuffer, Size); + return Error::success(); +} + +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 = checkOffsetForRead(Offset, 1)) + return EC; + + uint32_t First = Offset / BlockSize; + uint32_t Last = First; + + while (Last < getNumBlocks() - 1) { + if (StreamLayout.Blocks[Last] != StreamLayout.Blocks[Last + 1] - 1) + break; + ++Last; + } + + uint32_t OffsetInFirstBlock = Offset % BlockSize; + uint32_t BytesFromFirstBlock = BlockSize - OffsetInFirstBlock; + uint32_t BlockSpan = Last - First + 1; + uint32_t ByteSpan = BytesFromFirstBlock + (BlockSpan - 1) * BlockSize; + + ArrayRef<uint8_t> BlockData; + uint32_t MsfOffset = blockToOffset(StreamLayout.Blocks[First], BlockSize); + if (auto EC = MsfData.readBytes(MsfOffset, BlockSize, BlockData)) + return EC; + + BlockData = BlockData.drop_front(OffsetInFirstBlock); + Buffer = ArrayRef<uint8_t>(BlockData.data(), ByteSpan); + return Error::success(); +} + +uint32_t MappedBlockStream::getLength() { return StreamLayout.Length; } + +bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) { + if (Size == 0) { + Buffer = ArrayRef<uint8_t>(); + return true; + } + // Attempt to fulfill the request with a reference directly into the stream. + // This can work even if the request crosses a block boundary, provided that + // all subsequent blocks are contiguous. For example, a 10k read with a 4k + // block size can be filled with a reference if, from the starting offset, + // 3 blocks in a row are contiguous. + uint32_t BlockNum = Offset / BlockSize; + uint32_t OffsetInBlock = Offset % BlockSize; + uint32_t BytesFromFirstBlock = std::min(Size, BlockSize - OffsetInBlock); + uint32_t NumAdditionalBlocks = + alignTo(Size - BytesFromFirstBlock, BlockSize) / BlockSize; + + uint32_t RequiredContiguousBlocks = NumAdditionalBlocks + 1; + uint32_t E = StreamLayout.Blocks[BlockNum]; + for (uint32_t I = 0; I < RequiredContiguousBlocks; ++I, ++E) { + if (StreamLayout.Blocks[I + BlockNum] != E) + return false; + } + + // Read out the entire block where the requested offset starts. Then drop + // bytes from the beginning so that the actual starting byte lines up with + // the requested starting byte. Then, since we know this is a contiguous + // cross-block span, explicitly resize the ArrayRef to cover the entire + // request length. + ArrayRef<uint8_t> BlockData; + uint32_t FirstBlockAddr = StreamLayout.Blocks[BlockNum]; + uint32_t MsfOffset = blockToOffset(FirstBlockAddr, BlockSize); + if (auto EC = MsfData.readBytes(MsfOffset, BlockSize, BlockData)) { + consumeError(std::move(EC)); + return false; + } + BlockData = BlockData.drop_front(OffsetInBlock); + Buffer = ArrayRef<uint8_t>(BlockData.data(), Size); + return true; +} + +Error MappedBlockStream::readBytes(uint32_t Offset, + MutableArrayRef<uint8_t> Buffer) { + uint32_t BlockNum = Offset / BlockSize; + uint32_t OffsetInBlock = Offset % BlockSize; + + // Make sure we aren't trying to read beyond the end of the stream. + if (auto EC = checkOffsetForRead(Offset, Buffer.size())) + return EC; + + uint32_t BytesLeft = Buffer.size(); + uint32_t BytesWritten = 0; + uint8_t *WriteBuffer = Buffer.data(); + while (BytesLeft > 0) { + uint32_t StreamBlockAddr = StreamLayout.Blocks[BlockNum]; + + ArrayRef<uint8_t> BlockData; + uint32_t Offset = blockToOffset(StreamBlockAddr, BlockSize); + if (auto EC = MsfData.readBytes(Offset, BlockSize, BlockData)) + return EC; + + const uint8_t *ChunkStart = BlockData.data() + OffsetInBlock; + uint32_t BytesInChunk = std::min(BytesLeft, BlockSize - OffsetInBlock); + ::memcpy(WriteBuffer + BytesWritten, ChunkStart, BytesInChunk); + + BytesWritten += BytesInChunk; + BytesLeft -= BytesInChunk; + ++BlockNum; + OffsetInBlock = 0; + } + + return Error::success(); +} + +void MappedBlockStream::invalidateCache() { CacheMap.shrink_and_clear(); } + +void MappedBlockStream::fixCacheAfterWrite(uint32_t Offset, + ArrayRef<uint8_t> Data) const { + // If this write overlapped a read which previously came from the pool, + // someone may still be holding a pointer to that alloc which is now invalid. + // Compute the overlapping range and update the cache entry, so any + // outstanding buffers are automatically updated. + for (const auto &MapEntry : CacheMap) { + // If the end of the written extent precedes the beginning of the cached + // extent, ignore this map entry. + if (Offset + Data.size() < MapEntry.first) + continue; + for (const auto &Alloc : MapEntry.second) { + // If the end of the cached extent precedes the beginning of the written + // extent, ignore this alloc. + if (MapEntry.first + Alloc.size() < Offset) + continue; + + // If we get here, they are guaranteed to overlap. + Interval WriteInterval = std::make_pair(Offset, Offset + Data.size()); + Interval CachedInterval = + std::make_pair(MapEntry.first, MapEntry.first + Alloc.size()); + // If they overlap, we need to write the new data into the overlapping + // range. + auto Intersection = intersect(WriteInterval, CachedInterval); + assert(Intersection.first <= Intersection.second); + + uint32_t Length = Intersection.second - Intersection.first; + uint32_t SrcOffset = + AbsoluteDifference(WriteInterval.first, Intersection.first); + uint32_t DestOffset = + AbsoluteDifference(CachedInterval.first, Intersection.first); + ::memcpy(Alloc.data() + DestOffset, Data.data() + SrcOffset, Length); + } + } +} + +WritableMappedBlockStream::WritableMappedBlockStream( + uint32_t BlockSize, const MSFStreamLayout &Layout, + WritableBinaryStreamRef MsfData, BumpPtrAllocator &Allocator) + : ReadInterface(BlockSize, Layout, MsfData, Allocator), + WriteInterface(MsfData) {} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createStream(uint32_t BlockSize, + const MSFStreamLayout &Layout, + WritableBinaryStreamRef MsfData, + BumpPtrAllocator &Allocator) { + return std::make_unique<MappedBlockStreamImpl<WritableMappedBlockStream>>( + BlockSize, Layout, MsfData, Allocator); +} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createIndexedStream(const MSFLayout &Layout, + WritableBinaryStreamRef MsfData, + uint32_t StreamIndex, + BumpPtrAllocator &Allocator) { + assert(StreamIndex < Layout.StreamMap.size() && "Invalid stream index"); + MSFStreamLayout SL; + SL.Blocks = Layout.StreamMap[StreamIndex]; + SL.Length = Layout.StreamSizes[StreamIndex]; + return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator); +} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createDirectoryStream( + const MSFLayout &Layout, WritableBinaryStreamRef MsfData, + BumpPtrAllocator &Allocator) { + MSFStreamLayout SL; + SL.Blocks = Layout.DirectoryBlocks; + SL.Length = Layout.SB->NumDirectoryBytes; + return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator); +} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createFpmStream(const MSFLayout &Layout, + WritableBinaryStreamRef MsfData, + 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, + ArrayRef<uint8_t> &Buffer) { + return ReadInterface.readBytes(Offset, Size, Buffer); +} + +Error WritableMappedBlockStream::readLongestContiguousChunk( + uint32_t Offset, ArrayRef<uint8_t> &Buffer) { + return ReadInterface.readLongestContiguousChunk(Offset, Buffer); +} + +uint32_t WritableMappedBlockStream::getLength() { + return ReadInterface.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 = checkOffsetForWrite(Offset, Buffer.size())) + return EC; + + uint32_t BlockNum = Offset / getBlockSize(); + uint32_t OffsetInBlock = Offset % getBlockSize(); + + uint32_t BytesLeft = Buffer.size(); + uint32_t BytesWritten = 0; + while (BytesLeft > 0) { + uint32_t StreamBlockAddr = getStreamLayout().Blocks[BlockNum]; + uint32_t BytesToWriteInChunk = + std::min(BytesLeft, getBlockSize() - OffsetInBlock); + + const uint8_t *Chunk = Buffer.data() + BytesWritten; + ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk); + uint32_t MsfOffset = blockToOffset(StreamBlockAddr, getBlockSize()); + MsfOffset += OffsetInBlock; + if (auto EC = WriteInterface.writeBytes(MsfOffset, ChunkData)) + return EC; + + BytesLeft -= BytesToWriteInChunk; + BytesWritten += BytesToWriteInChunk; + ++BlockNum; + OffsetInBlock = 0; + } + + ReadInterface.fixCacheAfterWrite(Offset, Buffer); + + return Error::success(); +} + +Error WritableMappedBlockStream::commit() { return WriteInterface.commit(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIADataStream.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIADataStream.cpp new file mode 100644 index 000000000000..8a806f298d0f --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIADataStream.cpp @@ -0,0 +1,56 @@ +//===- DIADataStream.cpp - DIA implementation of IPDBDataStream -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIADataStream.h" +#include "llvm/DebugInfo/PDB/DIA/DIAUtils.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIADataStream::DIADataStream(CComPtr<IDiaEnumDebugStreamData> DiaStreamData) + : StreamData(DiaStreamData) {} + +uint32_t DIADataStream::getRecordCount() const { + LONG Count = 0; + return (S_OK == StreamData->get_Count(&Count)) ? Count : 0; +} + +std::string DIADataStream::getName() const { + return invokeBstrMethod(*StreamData, &IDiaEnumDebugStreamData::get_name); +} + +llvm::Optional<DIADataStream::RecordType> +DIADataStream::getItemAtIndex(uint32_t Index) const { + RecordType Record; + DWORD RecordSize = 0; + StreamData->Item(Index, 0, &RecordSize, nullptr); + if (RecordSize == 0) + return llvm::Optional<RecordType>(); + + Record.resize(RecordSize); + if (S_OK != StreamData->Item(Index, RecordSize, &RecordSize, &Record[0])) + return llvm::Optional<RecordType>(); + return Record; +} + +bool DIADataStream::getNext(RecordType &Record) { + Record.clear(); + DWORD RecordSize = 0; + ULONG CountFetched = 0; + StreamData->Next(1, 0, &RecordSize, nullptr, &CountFetched); + if (RecordSize == 0) + return false; + + Record.resize(RecordSize); + if (S_OK == + StreamData->Next(1, RecordSize, &RecordSize, &Record[0], &CountFetched)) + return false; + return true; +} + +void DIADataStream::reset() { StreamData->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp new file mode 100644 index 000000000000..e4cb4daf94b1 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp @@ -0,0 +1,46 @@ +//==- DIAEnumDebugStreams.cpp - DIA Debug Stream Enumerator impl -*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h" +#include "llvm/DebugInfo/PDB/DIA/DIADataStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumDebugStreams::DIAEnumDebugStreams( + CComPtr<IDiaEnumDebugStreams> DiaEnumerator) + : Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumDebugStreams::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBDataStream> +DIAEnumDebugStreams::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaEnumDebugStreamData> Item; + VARIANT VarIndex; + VarIndex.vt = VT_I4; + VarIndex.lVal = Index; + if (S_OK != Enumerator->Item(VarIndex, &Item)) + return nullptr; + + return std::unique_ptr<IPDBDataStream>(new DIADataStream(Item)); +} + +std::unique_ptr<IPDBDataStream> DIAEnumDebugStreams::getNext() { + CComPtr<IDiaEnumDebugStreamData> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + return std::unique_ptr<IPDBDataStream>(new DIADataStream(Item)); +} + +void DIAEnumDebugStreams::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumFrameData.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumFrameData.cpp new file mode 100644 index 000000000000..8a181b448a27 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumFrameData.cpp @@ -0,0 +1,41 @@ +//==- DIAEnumFrameData.cpp ---------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumFrameData.h" +#include "llvm/DebugInfo/PDB/DIA/DIAFrameData.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" + +using namespace llvm::pdb; + +DIAEnumFrameData::DIAEnumFrameData(CComPtr<IDiaEnumFrameData> DiaEnumerator) + : Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumFrameData::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBFrameData> +DIAEnumFrameData::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaFrameData> Item; + if (S_OK != Enumerator->Item(Index, &Item)) + return nullptr; + + return std::unique_ptr<IPDBFrameData>(new DIAFrameData(Item)); +} + +std::unique_ptr<IPDBFrameData> DIAEnumFrameData::getNext() { + CComPtr<IDiaFrameData> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + return std::unique_ptr<IPDBFrameData>(new DIAFrameData(Item)); +} + +void DIAEnumFrameData::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumInjectedSources.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumInjectedSources.cpp new file mode 100644 index 000000000000..7226ab2ba0a0 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumInjectedSources.cpp @@ -0,0 +1,43 @@ +//==- DIAEnumSourceFiles.cpp - DIA Source File Enumerator impl ---*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumInjectedSources.h" +#include "llvm/DebugInfo/PDB/DIA/DIAInjectedSource.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumInjectedSources::DIAEnumInjectedSources( + CComPtr<IDiaEnumInjectedSources> DiaEnumerator) + : Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumInjectedSources::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBInjectedSource> +DIAEnumInjectedSources::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaInjectedSource> Item; + if (S_OK != Enumerator->Item(Index, &Item)) + return nullptr; + + return std::unique_ptr<IPDBInjectedSource>(new DIAInjectedSource(Item)); +} + +std::unique_ptr<IPDBInjectedSource> DIAEnumInjectedSources::getNext() { + CComPtr<IDiaInjectedSource> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + return std::unique_ptr<IPDBInjectedSource>(new DIAInjectedSource(Item)); +} + +void DIAEnumInjectedSources::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp new file mode 100644 index 000000000000..6f1d7733fb2d --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp @@ -0,0 +1,43 @@ +//==- DIAEnumLineNumbers.cpp - DIA Line Number Enumerator impl ---*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h" +#include "llvm/DebugInfo/PDB/DIA/DIALineNumber.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumLineNumbers::DIAEnumLineNumbers( + CComPtr<IDiaEnumLineNumbers> DiaEnumerator) + : Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumLineNumbers::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBLineNumber> +DIAEnumLineNumbers::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaLineNumber> Item; + if (S_OK != Enumerator->Item(Index, &Item)) + return nullptr; + + return std::unique_ptr<IPDBLineNumber>(new DIALineNumber(Item)); +} + +std::unique_ptr<IPDBLineNumber> DIAEnumLineNumbers::getNext() { + CComPtr<IDiaLineNumber> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + return std::unique_ptr<IPDBLineNumber>(new DIALineNumber(Item)); +} + +void DIAEnumLineNumbers::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSectionContribs.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSectionContribs.cpp new file mode 100644 index 000000000000..4f9b232a024a --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSectionContribs.cpp @@ -0,0 +1,46 @@ +//==- DIAEnumSectionContribs.cpp ---------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSectionContribs.h" +#include "llvm/DebugInfo/PDB/DIA/DIASectionContrib.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumSectionContribs::DIAEnumSectionContribs( + const DIASession &PDBSession, + CComPtr<IDiaEnumSectionContribs> DiaEnumerator) + : Session(PDBSession), Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumSectionContribs::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBSectionContrib> +DIAEnumSectionContribs::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaSectionContrib> Item; + if (S_OK != Enumerator->Item(Index, &Item)) + return nullptr; + + return std::unique_ptr<IPDBSectionContrib>( + new DIASectionContrib(Session, Item)); +} + +std::unique_ptr<IPDBSectionContrib> DIAEnumSectionContribs::getNext() { + CComPtr<IDiaSectionContrib> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + return std::unique_ptr<IPDBSectionContrib>( + new DIASectionContrib(Session, Item)); +} + +void DIAEnumSectionContribs::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp new file mode 100644 index 000000000000..943e9e1b4d58 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp @@ -0,0 +1,43 @@ +//==- DIAEnumSourceFiles.cpp - DIA Source File Enumerator impl ---*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSourceFiles.h" +#include "llvm/DebugInfo/PDB/DIA/DIASourceFile.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumSourceFiles::DIAEnumSourceFiles( + const DIASession &PDBSession, CComPtr<IDiaEnumSourceFiles> DiaEnumerator) + : Session(PDBSession), Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumSourceFiles::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<IPDBSourceFile> +DIAEnumSourceFiles::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaSourceFile> Item; + if (S_OK != Enumerator->Item(Index, &Item)) + return nullptr; + + return std::unique_ptr<IPDBSourceFile>(new DIASourceFile(Session, Item)); +} + +std::unique_ptr<IPDBSourceFile> DIAEnumSourceFiles::getNext() { + CComPtr<IDiaSourceFile> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + return std::unique_ptr<IPDBSourceFile>(new DIASourceFile(Session, Item)); +} + +void DIAEnumSourceFiles::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp new file mode 100644 index 000000000000..5153596d52ae --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp @@ -0,0 +1,47 @@ +//==- DIAEnumSymbols.cpp - DIA Symbol Enumerator impl ------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h" +#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAEnumSymbols::DIAEnumSymbols(const DIASession &PDBSession, + CComPtr<IDiaEnumSymbols> DiaEnumerator) + : Session(PDBSession), Enumerator(DiaEnumerator) {} + +uint32_t DIAEnumSymbols::getChildCount() const { + LONG Count = 0; + return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0; +} + +std::unique_ptr<PDBSymbol> +DIAEnumSymbols::getChildAtIndex(uint32_t Index) const { + CComPtr<IDiaSymbol> Item; + if (S_OK != Enumerator->Item(Index, &Item)) + return nullptr; + + std::unique_ptr<DIARawSymbol> RawSymbol(new DIARawSymbol(Session, Item)); + return std::unique_ptr<PDBSymbol>(PDBSymbol::create(Session, std::move(RawSymbol))); +} + +std::unique_ptr<PDBSymbol> DIAEnumSymbols::getNext() { + CComPtr<IDiaSymbol> Item; + ULONG NumFetched = 0; + if (S_OK != Enumerator->Next(1, &Item, &NumFetched)) + return nullptr; + + std::unique_ptr<DIARawSymbol> RawSymbol(new DIARawSymbol(Session, Item)); + return std::unique_ptr<PDBSymbol>( + PDBSymbol::create(Session, std::move(RawSymbol))); +} + +void DIAEnumSymbols::reset() { Enumerator->Reset(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAEnumTables.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumTables.cpp new file mode 100644 index 000000000000..335b575d6542 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAEnumTables.cpp @@ -0,0 +1,44 @@ +//===- DIAEnumTables.cpp - DIA Table Enumerator Impl ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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(); } diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAError.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAError.cpp new file mode 100644 index 000000000000..819651f77787 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAError.cpp @@ -0,0 +1,37 @@ +#include "llvm/DebugInfo/PDB/DIA/DIAError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::pdb; + +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class DIAErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.pdb.dia"; } + std::string message(int Condition) const override { + switch (static_cast<dia_error_code>(Condition)) { + case dia_error_code::could_not_create_impl: + return "Failed to connect to DIA at runtime. Verify that Visual Studio " + "is properly installed, or that msdiaXX.dll is in your PATH."; + case dia_error_code::invalid_file_format: + return "Unable to load PDB. The file has an unrecognized format."; + case dia_error_code::invalid_parameter: + return "The parameter is incorrect."; + case dia_error_code::already_loaded: + return "Unable to load the PDB or EXE, because it is already loaded."; + case dia_error_code::debug_info_mismatch: + return "The PDB file and the EXE file do not match."; + case dia_error_code::unspecified: + return "An unknown error has occurred."; + } + llvm_unreachable("Unrecognized DIAErrorCode"); + } +}; + +static llvm::ManagedStatic<DIAErrorCategory> DIACategory; +const std::error_category &llvm::pdb::DIAErrCategory() { return *DIACategory; } + +char DIAError::ID; diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAFrameData.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAFrameData.cpp new file mode 100644 index 000000000000..7975156b1abd --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAFrameData.cpp @@ -0,0 +1,52 @@ +//===- DIAFrameData.cpp - DIA impl. of IPDBFrameData -------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAFrameData.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/DebugInfo/PDB/DIA/DIAUtils.h" + +using namespace llvm::pdb; + +DIAFrameData::DIAFrameData(CComPtr<IDiaFrameData> DiaFrameData) + : FrameData(DiaFrameData) {} + +template <typename ArgType> +ArgType +PrivateGetDIAValue(IDiaFrameData *FrameData, + HRESULT (__stdcall IDiaFrameData::*Method)(ArgType *)) { + ArgType Value; + if (S_OK == (FrameData->*Method)(&Value)) + return static_cast<ArgType>(Value); + + return ArgType(); +} + +uint32_t DIAFrameData::getAddressOffset() const { + return PrivateGetDIAValue(FrameData, &IDiaFrameData::get_addressOffset); +} + +uint32_t DIAFrameData::getAddressSection() const { + return PrivateGetDIAValue(FrameData, &IDiaFrameData::get_addressSection); +} + +uint32_t DIAFrameData::getLengthBlock() const { + return PrivateGetDIAValue(FrameData, &IDiaFrameData::get_lengthBlock); +} + +std::string DIAFrameData::getProgram() const { + return invokeBstrMethod(*FrameData, &IDiaFrameData::get_program); +} + +uint32_t DIAFrameData::getRelativeVirtualAddress() const { + return PrivateGetDIAValue(FrameData, + &IDiaFrameData::get_relativeVirtualAddress); +} + +uint64_t DIAFrameData::getVirtualAddress() const { + return PrivateGetDIAValue(FrameData, &IDiaFrameData::get_virtualAddress); +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIAInjectedSource.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIAInjectedSource.cpp new file mode 100644 index 000000000000..032b230b5faa --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIAInjectedSource.cpp @@ -0,0 +1,62 @@ +//===- DIAInjectedSource.cpp - DIA impl for IPDBInjectedSource --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIAInjectedSource.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/DebugInfo/PDB/DIA/DIAUtils.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIAInjectedSource::DIAInjectedSource(CComPtr<IDiaInjectedSource> DiaSourceFile) + : SourceFile(DiaSourceFile) {} + +uint32_t DIAInjectedSource::getCrc32() const { + DWORD Crc; + return (S_OK == SourceFile->get_crc(&Crc)) ? Crc : 0; +} + +uint64_t DIAInjectedSource::getCodeByteSize() const { + ULONGLONG Size; + return (S_OK == SourceFile->get_length(&Size)) ? Size : 0; +} + +std::string DIAInjectedSource::getFileName() const { + return invokeBstrMethod(*SourceFile, &IDiaInjectedSource::get_filename); +} + +std::string DIAInjectedSource::getObjectFileName() const { + return invokeBstrMethod(*SourceFile, &IDiaInjectedSource::get_objectFilename); +} + +std::string DIAInjectedSource::getVirtualFileName() const { + return invokeBstrMethod(*SourceFile, + &IDiaInjectedSource::get_virtualFilename); +} + +uint32_t DIAInjectedSource::getCompression() const { + DWORD Compression = 0; + if (S_OK != SourceFile->get_sourceCompression(&Compression)) + return PDB_SourceCompression::None; + return static_cast<uint32_t>(Compression); +} + +std::string DIAInjectedSource::getCode() const { + DWORD DataSize; + if (S_OK != SourceFile->get_source(0, &DataSize, nullptr)) + return ""; + + std::vector<uint8_t> Buffer(DataSize); + if (S_OK != SourceFile->get_source(DataSize, &DataSize, Buffer.data())) + return ""; + assert(Buffer.size() == DataSize); + return std::string(reinterpret_cast<const char *>(Buffer.data()), + Buffer.size()); +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp new file mode 100644 index 000000000000..3af02ea36c7b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp @@ -0,0 +1,75 @@ +//===- DIALineNumber.cpp - DIA implementation of IPDBLineNumber -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIALineNumber.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIALineNumber::DIALineNumber(CComPtr<IDiaLineNumber> DiaLineNumber) + : LineNumber(DiaLineNumber) {} + +uint32_t DIALineNumber::getLineNumber() const { + DWORD Line = 0; + return (S_OK == LineNumber->get_lineNumber(&Line)) ? Line : 0; +} + +uint32_t DIALineNumber::getLineNumberEnd() const { + DWORD LineEnd = 0; + return (S_OK == LineNumber->get_lineNumberEnd(&LineEnd)) ? LineEnd : 0; +} + +uint32_t DIALineNumber::getColumnNumber() const { + DWORD Column = 0; + return (S_OK == LineNumber->get_columnNumber(&Column)) ? Column : 0; +} + +uint32_t DIALineNumber::getColumnNumberEnd() const { + DWORD ColumnEnd = 0; + return (S_OK == LineNumber->get_columnNumberEnd(&ColumnEnd)) ? ColumnEnd : 0; +} + +uint32_t DIALineNumber::getAddressSection() const { + DWORD Section = 0; + return (S_OK == LineNumber->get_addressSection(&Section)) ? Section : 0; +} + +uint32_t DIALineNumber::getAddressOffset() const { + DWORD Offset = 0; + return (S_OK == LineNumber->get_addressOffset(&Offset)) ? Offset : 0; +} + +uint32_t DIALineNumber::getRelativeVirtualAddress() const { + DWORD RVA = 0; + return (S_OK == LineNumber->get_relativeVirtualAddress(&RVA)) ? RVA : 0; +} + +uint64_t DIALineNumber::getVirtualAddress() const { + ULONGLONG Addr = 0; + return (S_OK == LineNumber->get_virtualAddress(&Addr)) ? Addr : 0; +} + +uint32_t DIALineNumber::getLength() const { + DWORD Length = 0; + return (S_OK == LineNumber->get_length(&Length)) ? Length : 0; +} + +uint32_t DIALineNumber::getSourceFileId() const { + DWORD Id = 0; + return (S_OK == LineNumber->get_sourceFileId(&Id)) ? Id : 0; +} + +uint32_t DIALineNumber::getCompilandId() const { + DWORD Id = 0; + return (S_OK == LineNumber->get_compilandId(&Id)) ? Id : 0; +} + +bool DIALineNumber::isStatement() const { + BOOL Statement = 0; + return (S_OK == LineNumber->get_statement(&Statement)) ? Statement : false; +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp new file mode 100644 index 000000000000..c2552f55703c --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp @@ -0,0 +1,1270 @@ +//===- DIARawSymbol.cpp - DIA implementation of IPDBRawSymbol ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h" +#include "llvm/DebugInfo/PDB/DIA/DIALineNumber.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/DebugInfo/PDB/DIA/DIAUtils.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +Variant VariantFromVARIANT(const VARIANT &V) { + Variant Result; + switch (V.vt) { + case VT_I1: + Result.Value.Int8 = V.cVal; + Result.Type = PDB_VariantType::Int8; + break; + case VT_I2: + Result.Value.Int16 = V.iVal; + Result.Type = PDB_VariantType::Int16; + break; + case VT_I4: + Result.Value.Int32 = V.intVal; + Result.Type = PDB_VariantType::Int32; + break; + case VT_I8: + Result.Value.Int64 = V.llVal; + Result.Type = PDB_VariantType::Int64; + break; + case VT_UI1: + Result.Value.UInt8 = V.bVal; + Result.Type = PDB_VariantType::UInt8; + break; + case VT_UI2: + Result.Value.UInt16 = V.uiVal; + Result.Type = PDB_VariantType::UInt16; + break; + case VT_UI4: + Result.Value.UInt32 = V.uintVal; + Result.Type = PDB_VariantType::UInt32; + break; + case VT_UI8: + Result.Value.UInt64 = V.ullVal; + Result.Type = PDB_VariantType::UInt64; + break; + case VT_BOOL: + Result.Value.Bool = (V.boolVal == VARIANT_TRUE) ? true : false; + Result.Type = PDB_VariantType::Bool; + break; + case VT_R4: + Result.Value.Single = V.fltVal; + Result.Type = PDB_VariantType::Single; + break; + case VT_R8: + Result.Value.Double = V.dblVal; + Result.Type = PDB_VariantType::Double; + break; + case VT_BSTR: { + const char *SrcBytes = reinterpret_cast<const char *>(V.bstrVal); + llvm::ArrayRef<char> SrcByteArray(SrcBytes, SysStringByteLen(V.bstrVal)); + std::string Result8; + if (!llvm::convertUTF16ToUTF8String(SrcByteArray, Result8)) + Result.Value.String = nullptr; + Result.Value.String = new char[Result8.length() + 1]; + ::strcpy(Result.Value.String, Result8.c_str()); + Result.Type = PDB_VariantType::String; + break; + } + default: + Result.Type = PDB_VariantType::Unknown; + break; + } + return Result; +} + +template <typename ArgType> +ArgType PrivateGetDIAValue(IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) { + ArgType Value; + if (S_OK == (Symbol->*Method)(&Value)) + return static_cast<ArgType>(Value); + + return ArgType(); +} + +template <typename ArgType, typename RetType> +RetType PrivateGetDIAValue(IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) { + ArgType Value; + if (S_OK == (Symbol->*Method)(&Value)) + return static_cast<RetType>(Value); + + return RetType(); +} + +std::string +PrivateGetDIAValue(IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(BSTR *)) { + return invokeBstrMethod(*Symbol, Method); +} + +codeview::GUID +PrivateGetDIAValue(IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(GUID *)) { + GUID Result; + if (S_OK != (Symbol->*Method)(&Result)) + return codeview::GUID(); + + static_assert(sizeof(codeview::GUID) == sizeof(GUID), + "GUID is the wrong size!"); + codeview::GUID IdResult; + ::memcpy(&IdResult, &Result, sizeof(GUID)); + return IdResult; +} + +template <typename PrintType, typename ArgType> +void DumpDIAValueAs(llvm::raw_ostream &OS, int Indent, StringRef Name, + IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) { + ArgType Value; + if (S_OK == (Symbol->*Method)(&Value)) + dumpSymbolField(OS, Name, static_cast<PrintType>(Value), Indent); +} + +void DumpDIAIdValue(llvm::raw_ostream &OS, int Indent, StringRef Name, + IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(DWORD *), + const IPDBSession &Session, PdbSymbolIdField FieldId, + PdbSymbolIdField ShowFlags, PdbSymbolIdField RecurseFlags) { + DWORD Value; + if (S_OK == (Symbol->*Method)(&Value)) + dumpSymbolIdField(OS, Name, Value, Indent, Session, FieldId, ShowFlags, + RecurseFlags); +} + +template <typename ArgType> +void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name, + IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) { + ArgType Value; + if (S_OK == (Symbol->*Method)(&Value)) + dumpSymbolField(OS, Name, Value, Indent); +} + +void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name, + IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(BSTR *)) { + BSTR Value = nullptr; + if (S_OK != (Symbol->*Method)(&Value)) + return; + const char *Bytes = reinterpret_cast<const char *>(Value); + ArrayRef<char> ByteArray(Bytes, ::SysStringByteLen(Value)); + std::string Result; + if (llvm::convertUTF16ToUTF8String(ByteArray, Result)) + dumpSymbolField(OS, Name, Result, Indent); + ::SysFreeString(Value); +} + +void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name, + IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(VARIANT *)) { + VARIANT Value; + Value.vt = VT_EMPTY; + if (S_OK != (Symbol->*Method)(&Value)) + return; + Variant V = VariantFromVARIANT(Value); + + dumpSymbolField(OS, Name, V, Indent); +} +} // namespace + +namespace llvm { +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const GUID &G) { + StringRef GuidBytes(reinterpret_cast<const char *>(&G), sizeof(G)); + codeview::detail::GuidAdapter A(GuidBytes); + A.format(OS, ""); + return OS; +} +} // namespace llvm + +DIARawSymbol::DIARawSymbol(const DIASession &PDBSession, + CComPtr<IDiaSymbol> DiaSymbol) + : Session(PDBSession), Symbol(DiaSymbol) {} + +#define RAW_ID_METHOD_DUMP(Stream, Method, Session, FieldId, ShowFlags, \ + RecurseFlags) \ + DumpDIAIdValue(Stream, Indent, StringRef{#Method}, Symbol, \ + &IDiaSymbol::get_##Method, Session, FieldId, ShowFlags, \ + RecurseFlags); + +#define RAW_METHOD_DUMP(Stream, Method) \ + DumpDIAValue(Stream, Indent, StringRef{#Method}, Symbol, \ + &IDiaSymbol::get_##Method); + +#define RAW_METHOD_DUMP_AS(Stream, Method, Type) \ + DumpDIAValueAs<Type>(Stream, Indent, StringRef{#Method}, Symbol, \ + &IDiaSymbol::get_##Method); + +void DIARawSymbol::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + RAW_ID_METHOD_DUMP(OS, symIndexId, Session, PdbSymbolIdField::SymIndexId, + ShowIdFields, RecurseIdFields); + RAW_METHOD_DUMP_AS(OS, symTag, PDB_SymType); + + RAW_METHOD_DUMP(OS, access); + RAW_METHOD_DUMP(OS, addressOffset); + RAW_METHOD_DUMP(OS, addressSection); + RAW_METHOD_DUMP(OS, age); + RAW_METHOD_DUMP(OS, arrayIndexTypeId); + RAW_METHOD_DUMP(OS, backEndMajor); + RAW_METHOD_DUMP(OS, backEndMinor); + RAW_METHOD_DUMP(OS, backEndBuild); + RAW_METHOD_DUMP(OS, backEndQFE); + RAW_METHOD_DUMP(OS, baseDataOffset); + RAW_METHOD_DUMP(OS, baseDataSlot); + RAW_METHOD_DUMP(OS, baseSymbolId); + RAW_METHOD_DUMP_AS(OS, baseType, PDB_BuiltinType); + RAW_METHOD_DUMP(OS, bitPosition); + RAW_METHOD_DUMP_AS(OS, callingConvention, PDB_CallingConv); + RAW_ID_METHOD_DUMP(OS, classParentId, Session, PdbSymbolIdField::ClassParent, + ShowIdFields, RecurseIdFields); + RAW_METHOD_DUMP(OS, compilerName); + RAW_METHOD_DUMP(OS, count); + RAW_METHOD_DUMP(OS, countLiveRanges); + RAW_METHOD_DUMP(OS, frontEndMajor); + RAW_METHOD_DUMP(OS, frontEndMinor); + RAW_METHOD_DUMP(OS, frontEndBuild); + RAW_METHOD_DUMP(OS, frontEndQFE); + RAW_ID_METHOD_DUMP(OS, lexicalParentId, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + RAW_METHOD_DUMP(OS, libraryName); + RAW_METHOD_DUMP(OS, liveRangeStartAddressOffset); + RAW_METHOD_DUMP(OS, liveRangeStartAddressSection); + RAW_METHOD_DUMP(OS, liveRangeStartRelativeVirtualAddress); + RAW_METHOD_DUMP(OS, localBasePointerRegisterId); + RAW_METHOD_DUMP(OS, lowerBoundId); + RAW_METHOD_DUMP(OS, memorySpaceKind); + RAW_METHOD_DUMP(OS, name); + RAW_METHOD_DUMP(OS, numberOfAcceleratorPointerTags); + RAW_METHOD_DUMP(OS, numberOfColumns); + RAW_METHOD_DUMP(OS, numberOfModifiers); + RAW_METHOD_DUMP(OS, numberOfRegisterIndices); + RAW_METHOD_DUMP(OS, numberOfRows); + RAW_METHOD_DUMP(OS, objectFileName); + RAW_METHOD_DUMP(OS, oemId); + RAW_METHOD_DUMP(OS, oemSymbolId); + RAW_METHOD_DUMP(OS, offsetInUdt); + RAW_METHOD_DUMP(OS, platform); + RAW_METHOD_DUMP(OS, rank); + RAW_METHOD_DUMP(OS, registerId); + RAW_METHOD_DUMP(OS, registerType); + RAW_METHOD_DUMP(OS, relativeVirtualAddress); + RAW_METHOD_DUMP(OS, samplerSlot); + RAW_METHOD_DUMP(OS, signature); + RAW_METHOD_DUMP(OS, sizeInUdt); + RAW_METHOD_DUMP(OS, slot); + RAW_METHOD_DUMP(OS, sourceFileName); + RAW_METHOD_DUMP(OS, stride); + RAW_METHOD_DUMP(OS, subTypeId); + RAW_METHOD_DUMP(OS, symbolsFileName); + RAW_METHOD_DUMP(OS, targetOffset); + RAW_METHOD_DUMP(OS, targetRelativeVirtualAddress); + RAW_METHOD_DUMP(OS, targetVirtualAddress); + RAW_METHOD_DUMP(OS, targetSection); + RAW_METHOD_DUMP(OS, textureSlot); + RAW_METHOD_DUMP(OS, timeStamp); + RAW_METHOD_DUMP(OS, token); + RAW_ID_METHOD_DUMP(OS, typeId, Session, PdbSymbolIdField::Type, ShowIdFields, + RecurseIdFields); + RAW_METHOD_DUMP(OS, uavSlot); + RAW_METHOD_DUMP(OS, undecoratedName); + RAW_ID_METHOD_DUMP(OS, unmodifiedTypeId, Session, + PdbSymbolIdField::UnmodifiedType, ShowIdFields, + RecurseIdFields); + RAW_METHOD_DUMP(OS, upperBoundId); + RAW_METHOD_DUMP(OS, virtualBaseDispIndex); + RAW_METHOD_DUMP(OS, virtualBaseOffset); + RAW_METHOD_DUMP(OS, virtualTableShapeId); + RAW_METHOD_DUMP_AS(OS, dataKind, PDB_DataKind); + RAW_METHOD_DUMP(OS, guid); + RAW_METHOD_DUMP(OS, offset); + RAW_METHOD_DUMP(OS, thisAdjust); + RAW_METHOD_DUMP(OS, virtualBasePointerOffset); + RAW_METHOD_DUMP_AS(OS, locationType, PDB_LocType); + RAW_METHOD_DUMP(OS, machineType); + RAW_METHOD_DUMP(OS, thunkOrdinal); + RAW_METHOD_DUMP(OS, length); + RAW_METHOD_DUMP(OS, liveRangeLength); + RAW_METHOD_DUMP(OS, virtualAddress); + RAW_METHOD_DUMP_AS(OS, udtKind, PDB_UdtType); + RAW_METHOD_DUMP(OS, constructor); + RAW_METHOD_DUMP(OS, customCallingConvention); + RAW_METHOD_DUMP(OS, farReturn); + RAW_METHOD_DUMP(OS, code); + RAW_METHOD_DUMP(OS, compilerGenerated); + RAW_METHOD_DUMP(OS, constType); + RAW_METHOD_DUMP(OS, editAndContinueEnabled); + RAW_METHOD_DUMP(OS, function); + RAW_METHOD_DUMP(OS, stride); + RAW_METHOD_DUMP(OS, noStackOrdering); + RAW_METHOD_DUMP(OS, hasAlloca); + RAW_METHOD_DUMP(OS, hasAssignmentOperator); + RAW_METHOD_DUMP(OS, isCTypes); + RAW_METHOD_DUMP(OS, hasCastOperator); + RAW_METHOD_DUMP(OS, hasDebugInfo); + RAW_METHOD_DUMP(OS, hasEH); + RAW_METHOD_DUMP(OS, hasEHa); + RAW_METHOD_DUMP(OS, hasInlAsm); + RAW_METHOD_DUMP(OS, framePointerPresent); + RAW_METHOD_DUMP(OS, inlSpec); + RAW_METHOD_DUMP(OS, interruptReturn); + RAW_METHOD_DUMP(OS, hasLongJump); + RAW_METHOD_DUMP(OS, hasManagedCode); + RAW_METHOD_DUMP(OS, hasNestedTypes); + RAW_METHOD_DUMP(OS, noInline); + RAW_METHOD_DUMP(OS, noReturn); + RAW_METHOD_DUMP(OS, optimizedCodeDebugInfo); + RAW_METHOD_DUMP(OS, overloadedOperator); + RAW_METHOD_DUMP(OS, hasSEH); + RAW_METHOD_DUMP(OS, hasSecurityChecks); + RAW_METHOD_DUMP(OS, hasSetJump); + RAW_METHOD_DUMP(OS, strictGSCheck); + RAW_METHOD_DUMP(OS, isAcceleratorGroupSharedLocal); + RAW_METHOD_DUMP(OS, isAcceleratorPointerTagLiveRange); + RAW_METHOD_DUMP(OS, isAcceleratorStubFunction); + RAW_METHOD_DUMP(OS, isAggregated); + RAW_METHOD_DUMP(OS, intro); + RAW_METHOD_DUMP(OS, isCVTCIL); + RAW_METHOD_DUMP(OS, isConstructorVirtualBase); + RAW_METHOD_DUMP(OS, isCxxReturnUdt); + RAW_METHOD_DUMP(OS, isDataAligned); + RAW_METHOD_DUMP(OS, isHLSLData); + RAW_METHOD_DUMP(OS, isHotpatchable); + RAW_METHOD_DUMP(OS, indirectVirtualBaseClass); + RAW_METHOD_DUMP(OS, isInterfaceUdt); + RAW_METHOD_DUMP(OS, intrinsic); + RAW_METHOD_DUMP(OS, isLTCG); + RAW_METHOD_DUMP(OS, isLocationControlFlowDependent); + RAW_METHOD_DUMP(OS, isMSILNetmodule); + RAW_METHOD_DUMP(OS, isMatrixRowMajor); + RAW_METHOD_DUMP(OS, managed); + RAW_METHOD_DUMP(OS, msil); + RAW_METHOD_DUMP(OS, isMultipleInheritance); + RAW_METHOD_DUMP(OS, isNaked); + RAW_METHOD_DUMP(OS, nested); + RAW_METHOD_DUMP(OS, isOptimizedAway); + RAW_METHOD_DUMP(OS, packed); + RAW_METHOD_DUMP(OS, isPointerBasedOnSymbolValue); + RAW_METHOD_DUMP(OS, isPointerToDataMember); + RAW_METHOD_DUMP(OS, isPointerToMemberFunction); + RAW_METHOD_DUMP(OS, pure); + RAW_METHOD_DUMP(OS, RValueReference); + RAW_METHOD_DUMP(OS, isRefUdt); + RAW_METHOD_DUMP(OS, reference); + RAW_METHOD_DUMP(OS, restrictedType); + RAW_METHOD_DUMP(OS, isReturnValue); + RAW_METHOD_DUMP(OS, isSafeBuffers); + RAW_METHOD_DUMP(OS, scoped); + RAW_METHOD_DUMP(OS, isSdl); + RAW_METHOD_DUMP(OS, isSingleInheritance); + RAW_METHOD_DUMP(OS, isSplitted); + RAW_METHOD_DUMP(OS, isStatic); + RAW_METHOD_DUMP(OS, isStripped); + RAW_METHOD_DUMP(OS, unalignedType); + RAW_METHOD_DUMP(OS, notReached); + RAW_METHOD_DUMP(OS, isValueUdt); + RAW_METHOD_DUMP(OS, virtual); + RAW_METHOD_DUMP(OS, virtualBaseClass); + RAW_METHOD_DUMP(OS, isVirtualInheritance); + RAW_METHOD_DUMP(OS, volatileType); + RAW_METHOD_DUMP(OS, wasInlined); + RAW_METHOD_DUMP(OS, unused); + RAW_METHOD_DUMP(OS, value); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findChildren(PDB_SymType Type) const { + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != + Symbol->findChildrenEx(EnumVal, nullptr, nsNone, &DiaEnumerator)) { + if (S_OK != Symbol->findChildren(EnumVal, nullptr, nsNone, &DiaEnumerator)) + return nullptr; + } + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findChildren(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags) const { + llvm::SmallVector<UTF16, 32> Name16; + llvm::convertUTF8ToUTF16String(Name, Name16); + + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + DWORD CompareFlags = static_cast<DWORD>(Flags); + wchar_t *Name16Str = reinterpret_cast<wchar_t *>(Name16.data()); + + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != + Symbol->findChildrenEx(EnumVal, Name16Str, CompareFlags, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findChildrenByAddr(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint32_t Section, + uint32_t Offset) const { + llvm::SmallVector<UTF16, 32> Name16; + llvm::convertUTF8ToUTF16String(Name, Name16); + + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + + DWORD CompareFlags = static_cast<DWORD>(Flags); + wchar_t *Name16Str = reinterpret_cast<wchar_t *>(Name16.data()); + + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != Symbol->findChildrenExByAddr(EnumVal, Name16Str, CompareFlags, + Section, Offset, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findChildrenByVA(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint64_t VA) const { + llvm::SmallVector<UTF16, 32> Name16; + llvm::convertUTF8ToUTF16String(Name, Name16); + + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + + DWORD CompareFlags = static_cast<DWORD>(Flags); + wchar_t *Name16Str = reinterpret_cast<wchar_t *>(Name16.data()); + + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != Symbol->findChildrenExByVA(EnumVal, Name16Str, CompareFlags, VA, + &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findChildrenByRVA(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint32_t RVA) const { + llvm::SmallVector<UTF16, 32> Name16; + llvm::convertUTF8ToUTF16String(Name, Name16); + + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + DWORD CompareFlags = static_cast<DWORD>(Flags); + wchar_t *Name16Str = reinterpret_cast<wchar_t *>(Name16.data()); + + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != Symbol->findChildrenExByRVA(EnumVal, Name16Str, CompareFlags, RVA, + &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findInlineFramesByAddr(uint32_t Section, uint32_t Offset) const { + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != Symbol->findInlineFramesByAddr(Section, Offset, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findInlineFramesByRVA(uint32_t RVA) const { + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != Symbol->findInlineFramesByRVA(RVA, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumSymbols> +DIARawSymbol::findInlineFramesByVA(uint64_t VA) const { + CComPtr<IDiaEnumSymbols> DiaEnumerator; + if (S_OK != Symbol->findInlineFramesByVA(VA, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumSymbols>(Session, DiaEnumerator); +} + +std::unique_ptr<IPDBEnumLineNumbers> DIARawSymbol::findInlineeLines() const { + CComPtr<IDiaEnumLineNumbers> DiaEnumerator; + if (S_OK != Symbol->findInlineeLines(&DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(DiaEnumerator); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIARawSymbol::findInlineeLinesByAddr(uint32_t Section, uint32_t Offset, + uint32_t Length) const { + CComPtr<IDiaEnumLineNumbers> DiaEnumerator; + if (S_OK != + Symbol->findInlineeLinesByAddr(Section, Offset, Length, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(DiaEnumerator); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIARawSymbol::findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const { + CComPtr<IDiaEnumLineNumbers> DiaEnumerator; + if (S_OK != Symbol->findInlineeLinesByRVA(RVA, Length, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(DiaEnumerator); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIARawSymbol::findInlineeLinesByVA(uint64_t VA, uint32_t Length) const { + CComPtr<IDiaEnumLineNumbers> DiaEnumerator; + if (S_OK != Symbol->findInlineeLinesByVA(VA, Length, &DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(DiaEnumerator); +} + +void DIARawSymbol::getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) const { + bytes.clear(); + + DWORD DataSize = 0; + Symbol->get_dataBytes(0, &DataSize, nullptr); + if (DataSize == 0) + return; + + bytes.resize(DataSize); + 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); +} + +uint32_t DIARawSymbol::getAddressOffset() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_addressOffset); +} + +uint32_t DIARawSymbol::getAddressSection() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_addressSection); +} + +uint32_t DIARawSymbol::getAge() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_age); +} + +SymIndexId DIARawSymbol::getArrayIndexTypeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_arrayIndexTypeId); +} + +void DIARawSymbol::getBackEndVersion(VersionInfo &Version) const { + Version.Major = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_backEndMajor); + Version.Minor = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_backEndMinor); + Version.Build = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_backEndBuild); + Version.QFE = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_backEndQFE); +} + +uint32_t DIARawSymbol::getBaseDataOffset() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_baseDataOffset); +} + +uint32_t DIARawSymbol::getBaseDataSlot() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_baseDataSlot); +} + +SymIndexId DIARawSymbol::getBaseSymbolId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_baseSymbolId); +} + +PDB_BuiltinType DIARawSymbol::getBuiltinType() const { + return PrivateGetDIAValue<DWORD, PDB_BuiltinType>(Symbol, + &IDiaSymbol::get_baseType); +} + +uint32_t DIARawSymbol::getBitPosition() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_bitPosition); +} + +PDB_CallingConv DIARawSymbol::getCallingConvention() const { + return PrivateGetDIAValue<DWORD, PDB_CallingConv>( + Symbol, &IDiaSymbol::get_callingConvention); +} + +SymIndexId DIARawSymbol::getClassParentId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_classParentId); +} + +std::string DIARawSymbol::getCompilerName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_compilerName); +} + +uint32_t DIARawSymbol::getCount() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_count); +} + +uint32_t DIARawSymbol::getCountLiveRanges() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_countLiveRanges); +} + +void DIARawSymbol::getFrontEndVersion(VersionInfo &Version) const { + Version.Major = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_frontEndMajor); + Version.Minor = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_frontEndMinor); + Version.Build = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_frontEndBuild); + Version.QFE = PrivateGetDIAValue(Symbol, &IDiaSymbol::get_frontEndQFE); +} + +PDB_Lang DIARawSymbol::getLanguage() const { + return PrivateGetDIAValue<DWORD, PDB_Lang>(Symbol, &IDiaSymbol::get_language); +} + +SymIndexId DIARawSymbol::getLexicalParentId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_lexicalParentId); +} + +std::string DIARawSymbol::getLibraryName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_libraryName); +} + +uint32_t DIARawSymbol::getLiveRangeStartAddressOffset() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_liveRangeStartAddressOffset); +} + +uint32_t DIARawSymbol::getLiveRangeStartAddressSection() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_liveRangeStartAddressSection); +} + +uint32_t DIARawSymbol::getLiveRangeStartRelativeVirtualAddress() const { + return PrivateGetDIAValue( + Symbol, &IDiaSymbol::get_liveRangeStartRelativeVirtualAddress); +} + +codeview::RegisterId DIARawSymbol::getLocalBasePointerRegisterId() const { + return PrivateGetDIAValue<DWORD, codeview::RegisterId>( + Symbol, &IDiaSymbol::get_localBasePointerRegisterId); +} + +SymIndexId DIARawSymbol::getLowerBoundId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_lowerBoundId); +} + +uint32_t DIARawSymbol::getMemorySpaceKind() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_memorySpaceKind); +} + +std::string DIARawSymbol::getName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_name); +} + +uint32_t DIARawSymbol::getNumberOfAcceleratorPointerTags() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_numberOfAcceleratorPointerTags); +} + +uint32_t DIARawSymbol::getNumberOfColumns() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_numberOfColumns); +} + +uint32_t DIARawSymbol::getNumberOfModifiers() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_numberOfModifiers); +} + +uint32_t DIARawSymbol::getNumberOfRegisterIndices() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_numberOfRegisterIndices); +} + +uint32_t DIARawSymbol::getNumberOfRows() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_numberOfRows); +} + +std::string DIARawSymbol::getObjectFileName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_objectFileName); +} + +uint32_t DIARawSymbol::getOemId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_oemId); +} + +SymIndexId DIARawSymbol::getOemSymbolId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_oemSymbolId); +} + +uint32_t DIARawSymbol::getOffsetInUdt() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_offsetInUdt); +} + +PDB_Cpu DIARawSymbol::getPlatform() const { + return PrivateGetDIAValue<DWORD, PDB_Cpu>(Symbol, &IDiaSymbol::get_platform); +} + +uint32_t DIARawSymbol::getRank() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_rank); +} + +codeview::RegisterId DIARawSymbol::getRegisterId() const { + return PrivateGetDIAValue<DWORD, codeview::RegisterId>( + Symbol, &IDiaSymbol::get_registerId); +} + +uint32_t DIARawSymbol::getRegisterType() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_registerType); +} + +uint32_t DIARawSymbol::getRelativeVirtualAddress() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_relativeVirtualAddress); +} + +uint32_t DIARawSymbol::getSamplerSlot() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_samplerSlot); +} + +uint32_t DIARawSymbol::getSignature() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_signature); +} + +uint32_t DIARawSymbol::getSizeInUdt() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_sizeInUdt); +} + +uint32_t DIARawSymbol::getSlot() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_slot); +} + +std::string DIARawSymbol::getSourceFileName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_sourceFileName); +} + +std::unique_ptr<IPDBLineNumber> DIARawSymbol::getSrcLineOnTypeDefn() const { + CComPtr<IDiaLineNumber> LineNumber; + if (FAILED(Symbol->getSrcLineOnTypeDefn(&LineNumber)) || !LineNumber) + return nullptr; + + return std::make_unique<DIALineNumber>(LineNumber); +} + +uint32_t DIARawSymbol::getStride() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_stride); +} + +SymIndexId DIARawSymbol::getSubTypeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_subTypeId); +} + +std::string DIARawSymbol::getSymbolsFileName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_symbolsFileName); +} + +SymIndexId DIARawSymbol::getSymIndexId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_symIndexId); +} + +uint32_t DIARawSymbol::getTargetOffset() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_targetOffset); +} + +uint32_t DIARawSymbol::getTargetRelativeVirtualAddress() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_targetRelativeVirtualAddress); +} + +uint64_t DIARawSymbol::getTargetVirtualAddress() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_targetVirtualAddress); +} + +uint32_t DIARawSymbol::getTargetSection() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_targetSection); +} + +uint32_t DIARawSymbol::getTextureSlot() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_textureSlot); +} + +uint32_t DIARawSymbol::getTimeStamp() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_timeStamp); +} + +uint32_t DIARawSymbol::getToken() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_token); +} + +SymIndexId DIARawSymbol::getTypeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_typeId); +} + +uint32_t DIARawSymbol::getUavSlot() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_uavSlot); +} + +std::string DIARawSymbol::getUndecoratedName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_undecoratedName); +} + +SymIndexId DIARawSymbol::getUnmodifiedTypeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_unmodifiedTypeId); +} + +SymIndexId DIARawSymbol::getUpperBoundId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_upperBoundId); +} + +Variant DIARawSymbol::getValue() const { + VARIANT Value; + Value.vt = VT_EMPTY; + if (S_OK != Symbol->get_value(&Value)) + return Variant(); + + return VariantFromVARIANT(Value); +} + +uint32_t DIARawSymbol::getVirtualBaseDispIndex() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualBaseDispIndex); +} + +uint32_t DIARawSymbol::getVirtualBaseOffset() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualBaseOffset); +} + +SymIndexId DIARawSymbol::getVirtualTableShapeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualTableShapeId); +} + +std::unique_ptr<PDBSymbolTypeBuiltin> +DIARawSymbol::getVirtualBaseTableType() const { + CComPtr<IDiaSymbol> TableType; + if (FAILED(Symbol->get_virtualBaseTableType(&TableType)) || !TableType) + return nullptr; + + auto RawVT = std::make_unique<DIARawSymbol>(Session, TableType); + auto Pointer = + PDBSymbol::createAs<PDBSymbolTypePointer>(Session, std::move(RawVT)); + return unique_dyn_cast<PDBSymbolTypeBuiltin>(Pointer->getPointeeType()); +} + +PDB_DataKind DIARawSymbol::getDataKind() const { + return PrivateGetDIAValue<DWORD, PDB_DataKind>(Symbol, + &IDiaSymbol::get_dataKind); +} + +PDB_SymType DIARawSymbol::getSymTag() const { + return PrivateGetDIAValue<DWORD, PDB_SymType>(Symbol, + &IDiaSymbol::get_symTag); +} + +codeview::GUID DIARawSymbol::getGuid() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_guid); +} + +int32_t DIARawSymbol::getOffset() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_offset); +} + +int32_t DIARawSymbol::getThisAdjust() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_thisAdjust); +} + +int32_t DIARawSymbol::getVirtualBasePointerOffset() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualBasePointerOffset); +} + +PDB_LocType DIARawSymbol::getLocationType() const { + return PrivateGetDIAValue<DWORD, PDB_LocType>(Symbol, + &IDiaSymbol::get_locationType); +} + +PDB_Machine DIARawSymbol::getMachineType() const { + return PrivateGetDIAValue<DWORD, PDB_Machine>(Symbol, + &IDiaSymbol::get_machineType); +} + +codeview::ThunkOrdinal DIARawSymbol::getThunkOrdinal() const { + return PrivateGetDIAValue<DWORD, codeview::ThunkOrdinal>( + Symbol, &IDiaSymbol::get_thunkOrdinal); +} + +uint64_t DIARawSymbol::getLength() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_length); +} + +uint64_t DIARawSymbol::getLiveRangeLength() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_liveRangeLength); +} + +uint64_t DIARawSymbol::getVirtualAddress() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualAddress); +} + +PDB_UdtType DIARawSymbol::getUdtKind() const { + return PrivateGetDIAValue<DWORD, PDB_UdtType>(Symbol, + &IDiaSymbol::get_udtKind); +} + +bool DIARawSymbol::hasConstructor() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_constructor); +} + +bool DIARawSymbol::hasCustomCallingConvention() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_customCallingConvention); +} + +bool DIARawSymbol::hasFarReturn() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_farReturn); +} + +bool DIARawSymbol::isCode() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_code); +} + +bool DIARawSymbol::isCompilerGenerated() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_compilerGenerated); +} + +bool DIARawSymbol::isConstType() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_constType); +} + +bool DIARawSymbol::isEditAndContinueEnabled() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_editAndContinueEnabled); +} + +bool DIARawSymbol::isFunction() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_function); +} + +bool DIARawSymbol::getAddressTaken() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_addressTaken); +} + +bool DIARawSymbol::getNoStackOrdering() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_noStackOrdering); +} + +bool DIARawSymbol::hasAlloca() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasAlloca); +} + +bool DIARawSymbol::hasAssignmentOperator() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasAssignmentOperator); +} + +bool DIARawSymbol::hasCTypes() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isCTypes); +} + +bool DIARawSymbol::hasCastOperator() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasCastOperator); +} + +bool DIARawSymbol::hasDebugInfo() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasDebugInfo); +} + +bool DIARawSymbol::hasEH() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasEH); +} + +bool DIARawSymbol::hasEHa() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasEHa); +} + +bool DIARawSymbol::hasInlAsm() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasInlAsm); +} + +bool DIARawSymbol::hasInlineAttribute() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_inlSpec); +} + +bool DIARawSymbol::hasInterruptReturn() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_interruptReturn); +} + +bool DIARawSymbol::hasFramePointer() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_framePointerPresent); +} + +bool DIARawSymbol::hasLongJump() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasLongJump); +} + +bool DIARawSymbol::hasManagedCode() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasManagedCode); +} + +bool DIARawSymbol::hasNestedTypes() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasNestedTypes); +} + +bool DIARawSymbol::hasNoInlineAttribute() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_noInline); +} + +bool DIARawSymbol::hasNoReturnAttribute() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_noReturn); +} + +bool DIARawSymbol::hasOptimizedCodeDebugInfo() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_optimizedCodeDebugInfo); +} + +bool DIARawSymbol::hasOverloadedOperator() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_overloadedOperator); +} + +bool DIARawSymbol::hasSEH() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasSEH); +} + +bool DIARawSymbol::hasSecurityChecks() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasSecurityChecks); +} + +bool DIARawSymbol::hasSetJump() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_hasSetJump); +} + +bool DIARawSymbol::hasStrictGSCheck() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_strictGSCheck); +} + +bool DIARawSymbol::isAcceleratorGroupSharedLocal() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_isAcceleratorGroupSharedLocal); +} + +bool DIARawSymbol::isAcceleratorPointerTagLiveRange() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_isAcceleratorPointerTagLiveRange); +} + +bool DIARawSymbol::isAcceleratorStubFunction() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isAcceleratorStubFunction); +} + +bool DIARawSymbol::isAggregated() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isAggregated); +} + +bool DIARawSymbol::isIntroVirtualFunction() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_intro); +} + +bool DIARawSymbol::isCVTCIL() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isCVTCIL); +} + +bool DIARawSymbol::isConstructorVirtualBase() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isConstructorVirtualBase); +} + +bool DIARawSymbol::isCxxReturnUdt() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isCxxReturnUdt); +} + +bool DIARawSymbol::isDataAligned() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isDataAligned); +} + +bool DIARawSymbol::isHLSLData() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isHLSLData); +} + +bool DIARawSymbol::isHotpatchable() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isHotpatchable); +} + +bool DIARawSymbol::isIndirectVirtualBaseClass() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_indirectVirtualBaseClass); +} + +bool DIARawSymbol::isInterfaceUdt() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isInterfaceUdt); +} + +bool DIARawSymbol::isIntrinsic() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_intrinsic); +} + +bool DIARawSymbol::isLTCG() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isLTCG); +} + +bool DIARawSymbol::isLocationControlFlowDependent() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_isLocationControlFlowDependent); +} + +bool DIARawSymbol::isMSILNetmodule() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isMSILNetmodule); +} + +bool DIARawSymbol::isMatrixRowMajor() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isMatrixRowMajor); +} + +bool DIARawSymbol::isManagedCode() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_managed); +} + +bool DIARawSymbol::isMSILCode() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_msil); +} + +bool DIARawSymbol::isMultipleInheritance() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isMultipleInheritance); +} + +bool DIARawSymbol::isNaked() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isNaked); +} + +bool DIARawSymbol::isNested() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_nested); +} + +bool DIARawSymbol::isOptimizedAway() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isOptimizedAway); +} + +bool DIARawSymbol::isPacked() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_packed); +} + +bool DIARawSymbol::isPointerBasedOnSymbolValue() const { + return PrivateGetDIAValue(Symbol, + &IDiaSymbol::get_isPointerBasedOnSymbolValue); +} + +bool DIARawSymbol::isPointerToDataMember() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isPointerToDataMember); +} + +bool DIARawSymbol::isPointerToMemberFunction() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isPointerToMemberFunction); +} + +bool DIARawSymbol::isPureVirtual() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_pure); +} + +bool DIARawSymbol::isRValueReference() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_RValueReference); +} + +bool DIARawSymbol::isRefUdt() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isRefUdt); +} + +bool DIARawSymbol::isReference() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_reference); +} + +bool DIARawSymbol::isRestrictedType() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_restrictedType); +} + +bool DIARawSymbol::isReturnValue() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isReturnValue); +} + +bool DIARawSymbol::isSafeBuffers() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isSafeBuffers); +} + +bool DIARawSymbol::isScoped() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_scoped); +} + +bool DIARawSymbol::isSdl() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isSdl); +} + +bool DIARawSymbol::isSingleInheritance() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isSingleInheritance); +} + +bool DIARawSymbol::isSplitted() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isSplitted); +} + +bool DIARawSymbol::isStatic() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isStatic); +} + +bool DIARawSymbol::hasPrivateSymbols() const { + // hasPrivateSymbols is the opposite of isStripped, but we expose + // hasPrivateSymbols as a more intuitive interface. + return !PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isStripped); +} + +bool DIARawSymbol::isUnalignedType() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_unalignedType); +} + +bool DIARawSymbol::isUnreached() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_notReached); +} + +bool DIARawSymbol::isValueUdt() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isValueUdt); +} + +bool DIARawSymbol::isVirtual() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtual); +} + +bool DIARawSymbol::isVirtualBaseClass() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualBaseClass); +} + +bool DIARawSymbol::isVirtualInheritance() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_isVirtualInheritance); +} + +bool DIARawSymbol::isVolatileType() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_volatileType); +} + +bool DIARawSymbol::wasInlined() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_wasInlined); +} + +std::string DIARawSymbol::getUnused() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_unused); +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIASectionContrib.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIASectionContrib.cpp new file mode 100644 index 000000000000..4f0e078e6712 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIASectionContrib.cpp @@ -0,0 +1,125 @@ +//===- DIASectionContrib.cpp - DIA impl. of IPDBSectionContrib ---- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIASectionContrib.h" +#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIASectionContrib::DIASectionContrib(const DIASession &PDBSession, + CComPtr<IDiaSectionContrib> DiaSection) + : Session(PDBSession), Section(DiaSection) {} + +std::unique_ptr<PDBSymbolCompiland> DIASectionContrib::getCompiland() const { + CComPtr<IDiaSymbol> Symbol; + if (FAILED(Section->get_compiland(&Symbol))) + return nullptr; + + auto RawSymbol = std::make_unique<DIARawSymbol>(Session, Symbol); + return PDBSymbol::createAs<PDBSymbolCompiland>(Session, std::move(RawSymbol)); +} + +template <typename ArgType> +ArgType +PrivateGetDIAValue(IDiaSectionContrib *Section, + HRESULT (__stdcall IDiaSectionContrib::*Method)(ArgType *)) { + ArgType Value; + if (S_OK == (Section->*Method)(&Value)) + return static_cast<ArgType>(Value); + + return ArgType(); +} + +uint32_t DIASectionContrib::getAddressSection() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_addressSection); +} + +uint32_t DIASectionContrib::getAddressOffset() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_addressOffset); +} + +uint64_t DIASectionContrib::getVirtualAddress() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_virtualAddress); +} + +uint32_t DIASectionContrib::getRelativeVirtualAddress() const { + return PrivateGetDIAValue(Section, + &IDiaSectionContrib::get_relativeVirtualAddress); +} + +uint32_t DIASectionContrib::getLength() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_length); +} + +bool DIASectionContrib::isNotPaged() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_notPaged); +} + +bool DIASectionContrib::hasCode() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_code); +} + +bool DIASectionContrib::hasCode16Bit() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_code16bit); +} + +bool DIASectionContrib::hasInitializedData() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_initializedData); +} + +bool DIASectionContrib::hasUninitializedData() const { + return PrivateGetDIAValue(Section, + &IDiaSectionContrib::get_uninitializedData); +} + +bool DIASectionContrib::isRemoved() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_remove); +} + +bool DIASectionContrib::hasComdat() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_comdat); +} + +bool DIASectionContrib::isDiscardable() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_discardable); +} + +bool DIASectionContrib::isNotCached() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_notCached); +} + +bool DIASectionContrib::isShared() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_share); +} + +bool DIASectionContrib::isExecutable() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_execute); +} + +bool DIASectionContrib::isReadable() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_read); +} + +bool DIASectionContrib::isWritable() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_write); +} + +uint32_t DIASectionContrib::getDataCrc32() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_dataCrc); +} + +uint32_t DIASectionContrib::getRelocationsCrc32() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_relocationsCrc); +} + +uint32_t DIASectionContrib::getCompilandId() const { + return PrivateGetDIAValue(Section, &IDiaSectionContrib::get_compilandId); +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp new file mode 100644 index 000000000000..64ffa776bbd6 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp @@ -0,0 +1,423 @@ +//===- DIASession.cpp - DIA implementation of IPDBSession -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumFrameData.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumInjectedSources.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSectionContribs.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" +#include "llvm/DebugInfo/PDB/DIA/DIASupport.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::pdb; + +template <typename... Ts> +static Error ErrorFromHResult(HRESULT Result, const char *Str, Ts &&... Args) { + SmallString<64> MessageStorage; + StringRef Context; + if (sizeof...(Args) > 0) { + MessageStorage = formatv(Str, std::forward<Ts>(Args)...).str(); + Context = MessageStorage; + } else + Context = Str; + + switch (Result) { + case E_PDB_NOT_FOUND: + return errorCodeToError(std::error_code(ENOENT, std::generic_category())); + case E_PDB_FORMAT: + return make_error<DIAError>(dia_error_code::invalid_file_format, Context); + case E_INVALIDARG: + return make_error<DIAError>(dia_error_code::invalid_parameter, Context); + case E_UNEXPECTED: + return make_error<DIAError>(dia_error_code::already_loaded, Context); + case E_PDB_INVALID_SIG: + case E_PDB_INVALID_AGE: + return make_error<DIAError>(dia_error_code::debug_info_mismatch, Context); + default: { + std::string S; + raw_string_ostream OS(S); + OS << "HRESULT: " << format_hex(static_cast<DWORD>(Result), 10, true) + << ": " << Context; + return make_error<DIAError>(dia_error_code::unspecified, OS.str()); + } + } +} + +static Error LoadDIA(CComPtr<IDiaDataSource> &DiaDataSource) { + if (SUCCEEDED(CoCreateInstance(CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, + IID_IDiaDataSource, + reinterpret_cast<LPVOID *>(&DiaDataSource)))) + return Error::success(); + +// If the CoCreateInstance call above failed, msdia*.dll is not registered. +// Try loading the DLL corresponding to the #included DIA SDK. +#if !defined(_MSC_VER) + return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading); +#else + const wchar_t *msdia_dll = L"msdia140.dll"; + HRESULT HR; + if (FAILED(HR = NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource, + reinterpret_cast<LPVOID *>(&DiaDataSource)))) + return ErrorFromHResult(HR, "Calling NoRegCoCreate"); + return Error::success(); +#endif +} + +DIASession::DIASession(CComPtr<IDiaSession> DiaSession) : Session(DiaSession) {} + +Error DIASession::createFromPdb(StringRef Path, + std::unique_ptr<IPDBSession> &Session) { + CComPtr<IDiaDataSource> DiaDataSource; + CComPtr<IDiaSession> DiaSession; + + // We assume that CoInitializeEx has already been called by the executable. + if (auto E = LoadDIA(DiaDataSource)) + return E; + + llvm::SmallVector<UTF16, 128> Path16; + if (!llvm::convertUTF8ToUTF16String(Path, Path16)) + return make_error<PDBError>(pdb_error_code::invalid_utf8_path, Path); + + const wchar_t *Path16Str = reinterpret_cast<const wchar_t *>(Path16.data()); + HRESULT HR; + if (FAILED(HR = DiaDataSource->loadDataFromPdb(Path16Str))) { + return ErrorFromHResult(HR, "Calling loadDataFromPdb {0}", Path); + } + + if (FAILED(HR = DiaDataSource->openSession(&DiaSession))) + return ErrorFromHResult(HR, "Calling openSession"); + + Session.reset(new DIASession(DiaSession)); + return Error::success(); +} + +Error DIASession::createFromExe(StringRef Path, + std::unique_ptr<IPDBSession> &Session) { + CComPtr<IDiaDataSource> DiaDataSource; + CComPtr<IDiaSession> DiaSession; + + // We assume that CoInitializeEx has already been called by the executable. + if (auto EC = LoadDIA(DiaDataSource)) + return EC; + + llvm::SmallVector<UTF16, 128> Path16; + if (!llvm::convertUTF8ToUTF16String(Path, Path16)) + return make_error<PDBError>(pdb_error_code::invalid_utf8_path, Path); + + const wchar_t *Path16Str = reinterpret_cast<const wchar_t *>(Path16.data()); + HRESULT HR; + if (FAILED(HR = DiaDataSource->loadDataForExe(Path16Str, nullptr, nullptr))) + return ErrorFromHResult(HR, "Calling loadDataForExe"); + + if (FAILED(HR = DiaDataSource->openSession(&DiaSession))) + return ErrorFromHResult(HR, "Calling openSession"); + + Session.reset(new DIASession(DiaSession)); + return Error::success(); +} + +uint64_t DIASession::getLoadAddress() const { + uint64_t LoadAddress; + bool success = (S_OK == Session->get_loadAddress(&LoadAddress)); + return (success) ? LoadAddress : 0; +} + +bool DIASession::setLoadAddress(uint64_t Address) { + return (S_OK == Session->put_loadAddress(Address)); +} + +std::unique_ptr<PDBSymbolExe> DIASession::getGlobalScope() { + CComPtr<IDiaSymbol> GlobalScope; + if (S_OK != Session->get_globalScope(&GlobalScope)) + return nullptr; + + auto RawSymbol = std::make_unique<DIARawSymbol>(*this, GlobalScope); + auto PdbSymbol(PDBSymbol::create(*this, std::move(RawSymbol))); + std::unique_ptr<PDBSymbolExe> ExeSymbol( + static_cast<PDBSymbolExe *>(PdbSymbol.release())); + return ExeSymbol; +} + +bool DIASession::addressForVA(uint64_t VA, uint32_t &Section, + uint32_t &Offset) const { + DWORD ArgSection, ArgOffset = 0; + if (S_OK == Session->addressForVA(VA, &ArgSection, &ArgOffset)) { + Section = static_cast<uint32_t>(ArgSection); + Offset = static_cast<uint32_t>(ArgOffset); + return true; + } + return false; +} + +bool DIASession::addressForRVA(uint32_t RVA, uint32_t &Section, + uint32_t &Offset) const { + DWORD ArgSection, ArgOffset = 0; + if (S_OK == Session->addressForRVA(RVA, &ArgSection, &ArgOffset)) { + Section = static_cast<uint32_t>(ArgSection); + Offset = static_cast<uint32_t>(ArgOffset); + return true; + } + return false; +} + +std::unique_ptr<PDBSymbol> +DIASession::getSymbolById(SymIndexId SymbolId) const { + CComPtr<IDiaSymbol> LocatedSymbol; + if (S_OK != Session->symbolById(SymbolId, &LocatedSymbol)) + return nullptr; + + auto RawSymbol = std::make_unique<DIARawSymbol>(*this, LocatedSymbol); + return PDBSymbol::create(*this, std::move(RawSymbol)); +} + +std::unique_ptr<PDBSymbol> +DIASession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const { + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + + CComPtr<IDiaSymbol> Symbol; + if (S_OK != Session->findSymbolByVA(Address, EnumVal, &Symbol)) { + ULONGLONG LoadAddr = 0; + if (S_OK != Session->get_loadAddress(&LoadAddr)) + return nullptr; + DWORD RVA = static_cast<DWORD>(Address - LoadAddr); + if (S_OK != Session->findSymbolByRVA(RVA, EnumVal, &Symbol)) + return nullptr; + } + auto RawSymbol = std::make_unique<DIARawSymbol>(*this, Symbol); + return PDBSymbol::create(*this, std::move(RawSymbol)); +} + +std::unique_ptr<PDBSymbol> DIASession::findSymbolByRVA(uint32_t RVA, + PDB_SymType Type) const { + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + + CComPtr<IDiaSymbol> Symbol; + if (S_OK != Session->findSymbolByRVA(RVA, EnumVal, &Symbol)) + return nullptr; + + auto RawSymbol = std::make_unique<DIARawSymbol>(*this, Symbol); + return PDBSymbol::create(*this, std::move(RawSymbol)); +} + +std::unique_ptr<PDBSymbol> +DIASession::findSymbolBySectOffset(uint32_t Sect, uint32_t Offset, + PDB_SymType Type) const { + enum SymTagEnum EnumVal = static_cast<enum SymTagEnum>(Type); + + CComPtr<IDiaSymbol> Symbol; + if (S_OK != Session->findSymbolByAddr(Sect, Offset, EnumVal, &Symbol)) + return nullptr; + + auto RawSymbol = std::make_unique<DIARawSymbol>(*this, Symbol); + return PDBSymbol::create(*this, std::move(RawSymbol)); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIASession::findLineNumbers(const PDBSymbolCompiland &Compiland, + const IPDBSourceFile &File) const { + const DIARawSymbol &RawCompiland = + static_cast<const DIARawSymbol &>(Compiland.getRawSymbol()); + const DIASourceFile &RawFile = static_cast<const DIASourceFile &>(File); + + CComPtr<IDiaEnumLineNumbers> LineNumbers; + if (S_OK != Session->findLines(RawCompiland.getDiaSymbol(), + RawFile.getDiaFile(), &LineNumbers)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(LineNumbers); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIASession::findLineNumbersByAddress(uint64_t Address, uint32_t Length) const { + CComPtr<IDiaEnumLineNumbers> LineNumbers; + if (S_OK != Session->findLinesByVA(Address, Length, &LineNumbers)) { + ULONGLONG LoadAddr = 0; + if (S_OK != Session->get_loadAddress(&LoadAddr)) + return nullptr; + DWORD RVA = static_cast<DWORD>(Address - LoadAddr); + if (S_OK != Session->findLinesByRVA(RVA, Length, &LineNumbers)) + return nullptr; + } + return std::make_unique<DIAEnumLineNumbers>(LineNumbers); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIASession::findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const { + CComPtr<IDiaEnumLineNumbers> LineNumbers; + if (S_OK != Session->findLinesByRVA(RVA, Length, &LineNumbers)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(LineNumbers); +} + +std::unique_ptr<IPDBEnumLineNumbers> +DIASession::findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset, + uint32_t Length) const { + CComPtr<IDiaEnumLineNumbers> LineNumbers; + if (S_OK != Session->findLinesByAddr(Section, Offset, Length, &LineNumbers)) + return nullptr; + + return std::make_unique<DIAEnumLineNumbers>(LineNumbers); +} + +std::unique_ptr<IPDBEnumSourceFiles> +DIASession::findSourceFiles(const PDBSymbolCompiland *Compiland, + llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + IDiaSymbol *DiaCompiland = nullptr; + CComBSTR Utf16Pattern; + if (!Pattern.empty()) + Utf16Pattern = CComBSTR(Pattern.data()); + + if (Compiland) + DiaCompiland = static_cast<const DIARawSymbol &>(Compiland->getRawSymbol()) + .getDiaSymbol(); + + Flags = static_cast<PDB_NameSearchFlags>( + Flags | PDB_NameSearchFlags::NS_FileNameExtMatch); + CComPtr<IDiaEnumSourceFiles> SourceFiles; + if (S_OK != + Session->findFile(DiaCompiland, Utf16Pattern.m_str, Flags, &SourceFiles)) + return nullptr; + return std::make_unique<DIAEnumSourceFiles>(*this, SourceFiles); +} + +std::unique_ptr<IPDBSourceFile> +DIASession::findOneSourceFile(const PDBSymbolCompiland *Compiland, + llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + auto SourceFiles = findSourceFiles(Compiland, Pattern, Flags); + if (!SourceFiles || SourceFiles->getChildCount() == 0) + return nullptr; + return SourceFiles->getNext(); +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> +DIASession::findCompilandsForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + auto File = findOneSourceFile(nullptr, Pattern, Flags); + if (!File) + return nullptr; + return File->getCompilands(); +} + +std::unique_ptr<PDBSymbolCompiland> +DIASession::findOneCompilandForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + auto Compilands = findCompilandsForSourceFile(Pattern, Flags); + if (!Compilands || Compilands->getChildCount() == 0) + return nullptr; + return Compilands->getNext(); +} + +std::unique_ptr<IPDBEnumSourceFiles> DIASession::getAllSourceFiles() const { + CComPtr<IDiaEnumSourceFiles> Files; + if (S_OK != Session->findFile(nullptr, nullptr, nsNone, &Files)) + return nullptr; + + return std::make_unique<DIAEnumSourceFiles>(*this, Files); +} + +std::unique_ptr<IPDBEnumSourceFiles> DIASession::getSourceFilesForCompiland( + const PDBSymbolCompiland &Compiland) const { + CComPtr<IDiaEnumSourceFiles> Files; + + const DIARawSymbol &RawSymbol = + static_cast<const DIARawSymbol &>(Compiland.getRawSymbol()); + if (S_OK != + Session->findFile(RawSymbol.getDiaSymbol(), nullptr, nsNone, &Files)) + return nullptr; + + return std::make_unique<DIAEnumSourceFiles>(*this, Files); +} + +std::unique_ptr<IPDBSourceFile> +DIASession::getSourceFileById(uint32_t FileId) const { + CComPtr<IDiaSourceFile> LocatedFile; + if (S_OK != Session->findFileById(FileId, &LocatedFile)) + return nullptr; + + return std::make_unique<DIASourceFile>(*this, LocatedFile); +} + +std::unique_ptr<IPDBEnumDataStreams> DIASession::getDebugStreams() const { + CComPtr<IDiaEnumDebugStreams> DiaEnumerator; + if (S_OK != Session->getEnumDebugStreams(&DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumDebugStreams>(DiaEnumerator); +} + +std::unique_ptr<IPDBEnumTables> DIASession::getEnumTables() const { + CComPtr<IDiaEnumTables> DiaEnumerator; + if (S_OK != Session->getEnumTables(&DiaEnumerator)) + return nullptr; + + return std::make_unique<DIAEnumTables>(DiaEnumerator); +} + +template <class T> static CComPtr<T> getTableEnumerator(IDiaSession &Session) { + CComPtr<T> Enumerator; + CComPtr<IDiaEnumTables> ET; + CComPtr<IDiaTable> Table; + ULONG Count = 0; + + if (Session.getEnumTables(&ET) != S_OK) + return nullptr; + + while (ET->Next(1, &Table, &Count) == S_OK && Count == 1) { + // There is only one table that matches the given iid + if (S_OK == Table->QueryInterface(__uuidof(T), (void **)&Enumerator)) + break; + Table.Release(); + } + return Enumerator; +} +std::unique_ptr<IPDBEnumInjectedSources> +DIASession::getInjectedSources() const { + CComPtr<IDiaEnumInjectedSources> Files = + getTableEnumerator<IDiaEnumInjectedSources>(*Session); + if (!Files) + return nullptr; + + return std::make_unique<DIAEnumInjectedSources>(Files); +} + +std::unique_ptr<IPDBEnumSectionContribs> +DIASession::getSectionContribs() const { + CComPtr<IDiaEnumSectionContribs> Sections = + getTableEnumerator<IDiaEnumSectionContribs>(*Session); + if (!Sections) + return nullptr; + + return std::make_unique<DIAEnumSectionContribs>(*this, Sections); +} + +std::unique_ptr<IPDBEnumFrameData> +DIASession::getFrameData() const { + CComPtr<IDiaEnumFrameData> FD = + getTableEnumerator<IDiaEnumFrameData>(*Session); + if (!FD) + return nullptr; + + return std::make_unique<DIAEnumFrameData>(FD); +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp new file mode 100644 index 000000000000..21e757c3a060 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp @@ -0,0 +1,63 @@ +//===- DIASourceFile.cpp - DIA implementation of IPDBSourceFile -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIASourceFile.h" +#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/DebugInfo/PDB/DIA/DIAUtils.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" + +using namespace llvm; +using namespace llvm::pdb; + +DIASourceFile::DIASourceFile(const DIASession &PDBSession, + CComPtr<IDiaSourceFile> DiaSourceFile) + : Session(PDBSession), SourceFile(DiaSourceFile) {} + +std::string DIASourceFile::getFileName() const { + return invokeBstrMethod(*SourceFile, &IDiaSourceFile::get_fileName); +} + +uint32_t DIASourceFile::getUniqueId() const { + DWORD Id; + return (S_OK == SourceFile->get_uniqueId(&Id)) ? Id : 0; +} + +std::string DIASourceFile::getChecksum() const { + DWORD ByteSize = 0; + HRESULT Result = SourceFile->get_checksum(0, &ByteSize, nullptr); + if (ByteSize == 0) + return std::string(); + std::vector<BYTE> ChecksumBytes(ByteSize); + Result = SourceFile->get_checksum(ByteSize, &ByteSize, &ChecksumBytes[0]); + if (S_OK != Result) + return std::string(); + return std::string(ChecksumBytes.begin(), ChecksumBytes.end()); +} + +PDB_Checksum DIASourceFile::getChecksumType() const { + DWORD Type; + HRESULT Result = SourceFile->get_checksumType(&Type); + if (S_OK != Result) + return PDB_Checksum::None; + return static_cast<PDB_Checksum>(Type); +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> +DIASourceFile::getCompilands() const { + CComPtr<IDiaEnumSymbols> DiaEnumerator; + HRESULT Result = SourceFile->get_compilands(&DiaEnumerator); + if (S_OK != Result) + return nullptr; + + auto Enumerator = std::unique_ptr<IPDBEnumSymbols>( + new DIAEnumSymbols(Session, DiaEnumerator)); + return std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>>( + new ConcreteSymbolEnumerator<PDBSymbolCompiland>(std::move(Enumerator))); +} diff --git a/llvm/lib/DebugInfo/PDB/DIA/DIATable.cpp b/llvm/lib/DebugInfo/PDB/DIA/DIATable.cpp new file mode 100644 index 000000000000..33d74abd740e --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/DIA/DIATable.cpp @@ -0,0 +1,50 @@ +//===- DIATable.cpp - DIA implementation of IPDBTable -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIATable.h" +#include "llvm/DebugInfo/PDB/DIA/DIAUtils.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 { + return invokeBstrMethod(*Table, &IDiaTable::get_name); +} + +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/llvm/lib/DebugInfo/PDB/GenericError.cpp b/llvm/lib/DebugInfo/PDB/GenericError.cpp new file mode 100644 index 000000000000..0e4cba3174b2 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/GenericError.cpp @@ -0,0 +1,48 @@ +//===- Error.cpp - system_error extensions for PDB --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class PDBErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.pdb"; } + std::string message(int Condition) const override { + switch (static_cast<pdb_error_code>(Condition)) { + case pdb_error_code::unspecified: + return "An unknown error has occurred."; + case pdb_error_code::dia_sdk_not_present: + return "LLVM was not compiled with support for DIA. This usually means " + "that you are not using MSVC, or your Visual Studio " + "installation is corrupt."; + case pdb_error_code::dia_failed_loading: + return "DIA is only supported when using MSVC."; + case pdb_error_code::invalid_utf8_path: + return "The PDB file path is an invalid UTF8 sequence."; + case pdb_error_code::signature_out_of_date: + return "The signature does not match; the file(s) might be out of date."; + case pdb_error_code::no_matching_pch: + return "No matching precompiled header could be located."; + } + llvm_unreachable("Unrecognized generic_error_code"); + } +}; +} // namespace + +static llvm::ManagedStatic<PDBErrorCategory> PDBCategory; +const std::error_category &llvm::pdb::PDBErrCategory() { return *PDBCategory; } + +char PDBError::ID; diff --git a/llvm/lib/DebugInfo/PDB/IPDBSourceFile.cpp b/llvm/lib/DebugInfo/PDB/IPDBSourceFile.cpp new file mode 100644 index 000000000000..113ee04bab95 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/IPDBSourceFile.cpp @@ -0,0 +1,34 @@ +//===- IPDBSourceFile.cpp - base interface for a PDB source file ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> +#include <string> + +using namespace llvm; +using namespace llvm::pdb; + +IPDBSourceFile::~IPDBSourceFile() = default; + +void IPDBSourceFile::dump(raw_ostream &OS, int Indent) const { + OS.indent(Indent); + PDB_Checksum ChecksumType = getChecksumType(); + OS << "["; + if (ChecksumType != PDB_Checksum::None) { + OS << ChecksumType << ": "; + std::string Checksum = getChecksum(); + for (uint8_t c : Checksum) + OS << format_hex_no_prefix(c, 2, true); + } else + OS << "No checksum"; + OS << "] " << getFileName() << "\n"; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp new file mode 100644 index 000000000000..5095efcdee3c --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp @@ -0,0 +1,93 @@ +//===- DbiModuleDescriptor.cpp - PDB module information -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <cstdint> + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::support; + +DbiModuleDescriptor::DbiModuleDescriptor() = default; + +DbiModuleDescriptor::DbiModuleDescriptor(const DbiModuleDescriptor &Info) = + default; + +DbiModuleDescriptor::~DbiModuleDescriptor() = default; + +Error DbiModuleDescriptor::initialize(BinaryStreamRef Stream, + DbiModuleDescriptor &Info) { + BinaryStreamReader Reader(Stream); + if (auto EC = Reader.readObject(Info.Layout)) + return EC; + + if (auto EC = Reader.readCString(Info.ModuleName)) + return EC; + + if (auto EC = Reader.readCString(Info.ObjFileName)) + return EC; + return Error::success(); +} + +bool DbiModuleDescriptor::hasECInfo() const { + return (Layout->Flags & ModInfoFlags::HasECFlagMask) != 0; +} + +uint16_t DbiModuleDescriptor::getTypeServerIndex() const { + return (Layout->Flags & ModInfoFlags::TypeServerIndexMask) >> + ModInfoFlags::TypeServerIndexShift; +} + +const SectionContrib &DbiModuleDescriptor::getSectionContrib() const { + return Layout->SC; +} + +uint16_t DbiModuleDescriptor::getModuleStreamIndex() const { + return Layout->ModDiStream; +} + +uint32_t DbiModuleDescriptor::getSymbolDebugInfoByteSize() const { + return Layout->SymBytes; +} + +uint32_t DbiModuleDescriptor::getC11LineInfoByteSize() const { + return Layout->C11Bytes; +} + +uint32_t DbiModuleDescriptor::getC13LineInfoByteSize() const { + return Layout->C13Bytes; +} + +uint32_t DbiModuleDescriptor::getNumberOfFiles() const { + return Layout->NumFiles; +} + +uint32_t DbiModuleDescriptor::getSourceFileNameIndex() const { + return Layout->SrcFileNameNI; +} + +uint32_t DbiModuleDescriptor::getPdbFilePathNameIndex() const { + return Layout->PdbFilePathNI; +} + +StringRef DbiModuleDescriptor::getModuleName() const { return ModuleName; } + +StringRef DbiModuleDescriptor::getObjFileName() const { return ObjFileName; } + +uint32_t DbiModuleDescriptor::getRecordLength() const { + uint32_t M = ModuleName.str().size() + 1; + uint32_t O = ObjFileName.str().size() + 1; + uint32_t Size = sizeof(ModuleInfoHeader) + M + O; + Size = alignTo(Size, 4); + return Size; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp new file mode 100644 index 000000000000..419734771ccd --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp @@ -0,0 +1,191 @@ +//===- DbiModuleDescriptorBuilder.cpp - PDB Mod Info Creation ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#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/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +static uint32_t calculateDiSymbolStreamSize(uint32_t SymbolByteSize, + uint32_t C13Size) { + uint32_t Size = sizeof(uint32_t); // Signature + Size += alignTo(SymbolByteSize, 4); // Symbol Data + Size += 0; // TODO: Layout.C11Bytes + Size += C13Size; // C13 Debug Info Size + Size += sizeof(uint32_t); // GlobalRefs substream size (always 0) + Size += 0; // GlobalRefs substream bytes + return Size; +} + +DbiModuleDescriptorBuilder::DbiModuleDescriptorBuilder(StringRef ModuleName, + uint32_t ModIndex, + msf::MSFBuilder &Msf) + : MSF(Msf), ModuleName(ModuleName) { + ::memset(&Layout, 0, sizeof(Layout)); + Layout.Mod = ModIndex; +} + +DbiModuleDescriptorBuilder::~DbiModuleDescriptorBuilder() {} + +uint16_t DbiModuleDescriptorBuilder::getStreamIndex() const { + return Layout.ModDiStream; +} + +void DbiModuleDescriptorBuilder::setObjFileName(StringRef Name) { + ObjFileName = Name; +} + +void DbiModuleDescriptorBuilder::setPdbFilePathNI(uint32_t NI) { + PdbFilePathNI = NI; +} + +void DbiModuleDescriptorBuilder::setFirstSectionContrib( + const SectionContrib &SC) { + Layout.SC = SC; +} + +void DbiModuleDescriptorBuilder::addSymbol(CVSymbol Symbol) { + // Defer to the bulk API. It does the same thing. + addSymbolsInBulk(Symbol.data()); +} + +void DbiModuleDescriptorBuilder::addSymbolsInBulk( + ArrayRef<uint8_t> BulkSymbols) { + // Do nothing for empty runs of symbols. + if (BulkSymbols.empty()) + return; + + Symbols.push_back(BulkSymbols); + // Symbols written to a PDB file are required to be 4 byte aligned. The same + // is not true of object files. + assert(BulkSymbols.size() % alignOf(CodeViewContainer::Pdb) == 0 && + "Invalid Symbol alignment!"); + SymbolByteSize += BulkSymbols.size(); +} + +void DbiModuleDescriptorBuilder::addSourceFile(StringRef Path) { + SourceFiles.push_back(Path); +} + +uint32_t DbiModuleDescriptorBuilder::calculateC13DebugInfoSize() const { + uint32_t Result = 0; + for (const auto &Builder : C13Builders) { + assert(Builder && "Empty C13 Fragment Builder!"); + Result += Builder->calculateSerializedLength(); + } + return Result; +} + +uint32_t DbiModuleDescriptorBuilder::calculateSerializedLength() const { + uint32_t L = sizeof(Layout); + uint32_t M = ModuleName.size() + 1; + uint32_t O = ObjFileName.size() + 1; + return alignTo(L + M + O, sizeof(uint32_t)); +} + +void DbiModuleDescriptorBuilder::finalize() { + Layout.FileNameOffs = 0; // TODO: Fix this + Layout.Flags = 0; // TODO: Fix this + Layout.C11Bytes = 0; + Layout.C13Bytes = calculateC13DebugInfoSize(); + (void)Layout.Mod; // Set in constructor + (void)Layout.ModDiStream; // Set in finalizeMsfLayout + Layout.NumFiles = SourceFiles.size(); + Layout.PdbFilePathNI = PdbFilePathNI; + Layout.SrcFileNameNI = 0; + + // This value includes both the signature field as well as the record bytes + // from the symbol stream. + Layout.SymBytes = + Layout.ModDiStream == kInvalidStreamIndex ? 0 : getNextSymbolOffset(); +} + +Error DbiModuleDescriptorBuilder::finalizeMsfLayout() { + this->Layout.ModDiStream = kInvalidStreamIndex; + uint32_t C13Size = calculateC13DebugInfoSize(); + if (!C13Size && !SymbolByteSize) + return Error::success(); + auto ExpectedSN = + MSF.addStream(calculateDiSymbolStreamSize(SymbolByteSize, C13Size)); + if (!ExpectedSN) + return ExpectedSN.takeError(); + Layout.ModDiStream = *ExpectedSN; + return Error::success(); +} + +Error DbiModuleDescriptorBuilder::commit(BinaryStreamWriter &ModiWriter, + const msf::MSFLayout &MsfLayout, + WritableBinaryStreamRef MsfBuffer) { + // We write the Modi record to the `ModiWriter`, but we additionally write its + // symbol stream to a brand new stream. + if (auto EC = ModiWriter.writeObject(Layout)) + return EC; + if (auto EC = ModiWriter.writeCString(ModuleName)) + return EC; + if (auto EC = ModiWriter.writeCString(ObjFileName)) + return EC; + if (auto EC = ModiWriter.padToAlignment(sizeof(uint32_t))) + return EC; + + if (Layout.ModDiStream != kInvalidStreamIndex) { + auto NS = WritableMappedBlockStream::createIndexedStream( + MsfLayout, MsfBuffer, Layout.ModDiStream, MSF.getAllocator()); + WritableBinaryStreamRef Ref(*NS); + BinaryStreamWriter SymbolWriter(Ref); + // Write the symbols. + if (auto EC = + SymbolWriter.writeInteger<uint32_t>(COFF::DEBUG_SECTION_MAGIC)) + return EC; + for (ArrayRef<uint8_t> Syms : Symbols) { + if (auto EC = SymbolWriter.writeBytes(Syms)) + return EC; + } + assert(SymbolWriter.getOffset() % alignOf(CodeViewContainer::Pdb) == 0 && + "Invalid debug section alignment!"); + // TODO: Write C11 Line data + for (const auto &Builder : C13Builders) { + assert(Builder && "Empty C13 Fragment Builder!"); + if (auto EC = Builder->commit(SymbolWriter)) + return EC; + } + + // TODO: Figure out what GlobalRefs substream actually is and populate it. + if (auto EC = SymbolWriter.writeInteger<uint32_t>(0)) + return EC; + if (SymbolWriter.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::stream_too_long); + } + return Error::success(); +} + +void DbiModuleDescriptorBuilder::addDebugSubsection( + std::shared_ptr<DebugSubsection> Subsection) { + assert(Subsection); + C13Builders.push_back(std::make_unique<DebugSubsectionRecordBuilder>( + std::move(Subsection), CodeViewContainer::Pdb)); +} + +void DbiModuleDescriptorBuilder::addDebugSubsection( + const DebugSubsectionRecord &SubsectionContents) { + C13Builders.push_back(std::make_unique<DebugSubsectionRecordBuilder>( + SubsectionContents, CodeViewContainer::Pdb)); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp new file mode 100644 index 000000000000..5cf014e881cd --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp @@ -0,0 +1,279 @@ +//===- DbiModuleList.cpp - PDB module information list --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace llvm::pdb; + +DbiModuleSourceFilesIterator::DbiModuleSourceFilesIterator( + const DbiModuleList &Modules, uint32_t Modi, uint16_t Filei) + : Modules(&Modules), Modi(Modi), Filei(Filei) { + setValue(); +} + +bool DbiModuleSourceFilesIterator:: +operator==(const DbiModuleSourceFilesIterator &R) const { + // incompatible iterators are never equal + if (!isCompatible(R)) + return false; + + // If they're compatible, and they're both ends, then they're equal. + if (isEnd() && R.isEnd()) + return true; + + // If one is an end and the other is not, they're not equal. + if (isEnd() != R.isEnd()) + return false; + + // Now we know: + // - They're compatible + // - They're not *both* end iterators + // - Their endness is the same. + // Thus, they're compatible iterators pointing to a valid file on the same + // module. All we need to check are the file indices. + assert(Modules == R.Modules); + assert(Modi == R.Modi); + assert(!isEnd()); + assert(!R.isEnd()); + + return (Filei == R.Filei); +} + +bool DbiModuleSourceFilesIterator:: +operator<(const DbiModuleSourceFilesIterator &R) const { + assert(isCompatible(R)); + + // It's not sufficient to compare the file indices, because default + // constructed iterators could be equal to iterators with valid indices. To + // account for this, early-out if they're equal. + if (*this == R) + return false; + + return Filei < R.Filei; +} + +std::ptrdiff_t DbiModuleSourceFilesIterator:: +operator-(const DbiModuleSourceFilesIterator &R) const { + assert(isCompatible(R)); + assert(!(*this < R)); + + // If they're both end iterators, the distance is 0. + if (isEnd() && R.isEnd()) + return 0; + + assert(!R.isEnd()); + + // At this point, R cannot be end, but *this can, which means that *this + // might be a universal end iterator with none of its fields set. So in that + // case have to rely on R as the authority to figure out how many files there + // are to compute the distance. + uint32_t Thisi = Filei; + if (isEnd()) { + uint32_t RealModi = R.Modi; + Thisi = R.Modules->getSourceFileCount(RealModi); + } + + assert(Thisi >= R.Filei); + return Thisi - R.Filei; +} + +DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator:: +operator+=(std::ptrdiff_t N) { + assert(!isEnd()); + + Filei += N; + assert(Filei <= Modules->getSourceFileCount(Modi)); + setValue(); + return *this; +} + +DbiModuleSourceFilesIterator &DbiModuleSourceFilesIterator:: +operator-=(std::ptrdiff_t N) { + // Note that we can subtract from an end iterator, but not a universal end + // iterator. + assert(!isUniversalEnd()); + + assert(N <= Filei); + + Filei -= N; + return *this; +} + +void DbiModuleSourceFilesIterator::setValue() { + if (isEnd()) { + ThisValue = ""; + return; + } + + uint32_t Off = Modules->ModuleInitialFileIndex[Modi] + Filei; + auto ExpectedValue = Modules->getFileName(Off); + if (!ExpectedValue) { + consumeError(ExpectedValue.takeError()); + Filei = Modules->getSourceFileCount(Modi); + } else + ThisValue = *ExpectedValue; +} + +bool DbiModuleSourceFilesIterator::isEnd() const { + if (isUniversalEnd()) + return true; + + assert(Modules); + assert(Modi <= Modules->getModuleCount()); + assert(Filei <= Modules->getSourceFileCount(Modi)); + + if (Modi == Modules->getModuleCount()) + return true; + if (Filei == Modules->getSourceFileCount(Modi)) + return true; + return false; +} + +bool DbiModuleSourceFilesIterator::isUniversalEnd() const { return !Modules; } + +bool DbiModuleSourceFilesIterator::isCompatible( + const DbiModuleSourceFilesIterator &R) const { + // Universal iterators are compatible with any other iterator. + if (isUniversalEnd() || R.isUniversalEnd()) + return true; + + // At this point, neither iterator is a universal end iterator, although one + // or both might be non-universal end iterators. Regardless, the module index + // is valid, so they are compatible if and only if they refer to the same + // module. + return Modi == R.Modi; +} + +Error DbiModuleList::initialize(BinaryStreamRef ModInfo, + BinaryStreamRef FileInfo) { + if (auto EC = initializeModInfo(ModInfo)) + return EC; + if (auto EC = initializeFileInfo(FileInfo)) + return EC; + + return Error::success(); +} + +Error DbiModuleList::initializeModInfo(BinaryStreamRef ModInfo) { + ModInfoSubstream = ModInfo; + + if (ModInfo.getLength() == 0) + return Error::success(); + + BinaryStreamReader Reader(ModInfo); + + if (auto EC = Reader.readArray(Descriptors, ModInfo.getLength())) + return EC; + + return Error::success(); +} + +Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) { + FileInfoSubstream = FileInfo; + + if (FileInfo.getLength() == 0) + return Error::success(); + + BinaryStreamReader FISR(FileInfo); + if (auto EC = FISR.readObject(FileInfoHeader)) + return EC; + + // First is an array of `NumModules` module indices. This does not seem to be + // used for anything meaningful, so we ignore it. + FixedStreamArray<support::ulittle16_t> ModuleIndices; + if (auto EC = FISR.readArray(ModuleIndices, FileInfoHeader->NumModules)) + return EC; + if (auto EC = FISR.readArray(ModFileCountArray, FileInfoHeader->NumModules)) + return EC; + + // Compute the real number of source files. We can't trust the value in + // `FileInfoHeader->NumSourceFiles` because it is a unit16, and the sum of all + // source file counts might be larger than a unit16. So we compute the real + // count by summing up the individual counts. + uint32_t NumSourceFiles = 0; + for (auto Count : ModFileCountArray) + NumSourceFiles += Count; + + // In the reference implementation, this array is where the pointer documented + // at the definition of ModuleInfoHeader::FileNameOffs points to. Note that + // although the field in ModuleInfoHeader is ignored this array is not, as it + // is the authority on where each filename begins in the names buffer. + if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles)) + return EC; + + if (auto EC = FISR.readStreamRef(NamesBuffer)) + return EC; + + auto DescriptorIter = Descriptors.begin(); + uint32_t NextFileIndex = 0; + ModuleInitialFileIndex.resize(FileInfoHeader->NumModules); + ModuleDescriptorOffsets.resize(FileInfoHeader->NumModules); + for (size_t I = 0; I < FileInfoHeader->NumModules; ++I) { + assert(DescriptorIter != Descriptors.end()); + ModuleInitialFileIndex[I] = NextFileIndex; + ModuleDescriptorOffsets[I] = DescriptorIter.offset(); + + NextFileIndex += ModFileCountArray[I]; + ++DescriptorIter; + } + + assert(DescriptorIter == Descriptors.end()); + assert(NextFileIndex == NumSourceFiles); + + return Error::success(); +} + +uint32_t DbiModuleList::getModuleCount() const { + return FileInfoHeader->NumModules; +} + +uint32_t DbiModuleList::getSourceFileCount() const { + return FileNameOffsets.size(); +} + +uint16_t DbiModuleList::getSourceFileCount(uint32_t Modi) const { + return ModFileCountArray[Modi]; +} + +DbiModuleDescriptor DbiModuleList::getModuleDescriptor(uint32_t Modi) const { + assert(Modi < getModuleCount()); + uint32_t Offset = ModuleDescriptorOffsets[Modi]; + auto Iter = Descriptors.at(Offset); + assert(Iter != Descriptors.end()); + return *Iter; +} + +iterator_range<DbiModuleSourceFilesIterator> +DbiModuleList::source_files(uint32_t Modi) const { + return make_range<DbiModuleSourceFilesIterator>( + DbiModuleSourceFilesIterator(*this, Modi, 0), + DbiModuleSourceFilesIterator()); +} + +Expected<StringRef> DbiModuleList::getFileName(uint32_t Index) const { + BinaryStreamReader Names(NamesBuffer); + if (Index >= getSourceFileCount()) + return make_error<RawError>(raw_error_code::index_out_of_bounds); + + uint32_t FileOffset = FileNameOffsets[Index]; + Names.setOffset(FileOffset); + StringRef Name; + if (auto EC = Names.readCString(Name)) + return std::move(EC); + return Name; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp new file mode 100644 index 000000000000..4eb16804171d --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp @@ -0,0 +1,383 @@ +//===- DbiStream.cpp - PDB Dbi Stream (Stream 3) Access -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/ADT/StringRef.h" +#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/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/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +template <typename ContribType> +static Error loadSectionContribs(FixedStreamArray<ContribType> &Output, + BinaryStreamReader &Reader) { + if (Reader.bytesRemaining() % sizeof(ContribType) != 0) + return make_error<RawError>( + raw_error_code::corrupt_file, + "Invalid number of bytes of section contributions"); + + uint32_t Count = Reader.bytesRemaining() / sizeof(ContribType); + if (auto EC = Reader.readArray(Output, Count)) + return EC; + return Error::success(); +} + +DbiStream::DbiStream(std::unique_ptr<BinaryStream> Stream) + : Stream(std::move(Stream)), Header(nullptr) {} + +DbiStream::~DbiStream() = default; + +Error DbiStream::reload(PDBFile *Pdb) { + BinaryStreamReader Reader(*Stream); + + if (Stream->getLength() < sizeof(DbiStreamHeader)) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI Stream does not contain a header."); + if (auto EC = Reader.readObject(Header)) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI Stream does not contain a header."); + + if (Header->VersionSignature != -1) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid DBI version signature."); + + // Require at least version 7, which should be present in all PDBs + // produced in the last decade and allows us to avoid having to + // special case all kinds of complicated arcane formats. + if (Header->VersionHeader < PdbDbiV70) + return make_error<RawError>(raw_error_code::feature_unsupported, + "Unsupported DBI version."); + + if (Stream->getLength() != + sizeof(DbiStreamHeader) + Header->ModiSubstreamSize + + Header->SecContrSubstreamSize + Header->SectionMapSize + + Header->FileInfoSize + Header->TypeServerSize + + Header->OptionalDbgHdrSize + Header->ECSubstreamSize) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI Length does not equal sum of substreams."); + + // Only certain substreams are guaranteed to be aligned. Validate + // them here. + if (Header->ModiSubstreamSize % sizeof(uint32_t) != 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI MODI substream not aligned."); + if (Header->SecContrSubstreamSize % sizeof(uint32_t) != 0) + return make_error<RawError>( + raw_error_code::corrupt_file, + "DBI section contribution substream not aligned."); + if (Header->SectionMapSize % sizeof(uint32_t) != 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI section map substream not aligned."); + if (Header->FileInfoSize % sizeof(uint32_t) != 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI file info substream not aligned."); + if (Header->TypeServerSize % sizeof(uint32_t) != 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI type server substream not aligned."); + + if (auto EC = Reader.readSubstream(ModiSubstream, Header->ModiSubstreamSize)) + return EC; + + if (auto EC = Reader.readSubstream(SecContrSubstream, + Header->SecContrSubstreamSize)) + return EC; + if (auto EC = Reader.readSubstream(SecMapSubstream, Header->SectionMapSize)) + return EC; + if (auto EC = Reader.readSubstream(FileInfoSubstream, Header->FileInfoSize)) + return EC; + if (auto EC = + Reader.readSubstream(TypeServerMapSubstream, Header->TypeServerSize)) + return EC; + if (auto EC = Reader.readSubstream(ECSubstream, Header->ECSubstreamSize)) + return EC; + if (auto EC = Reader.readArray( + DbgStreams, Header->OptionalDbgHdrSize / sizeof(ulittle16_t))) + return EC; + + if (auto EC = Modules.initialize(ModiSubstream.StreamData, + FileInfoSubstream.StreamData)) + return EC; + + if (auto EC = initializeSectionContributionData()) + return EC; + if (auto EC = initializeSectionHeadersData(Pdb)) + return EC; + if (auto EC = initializeSectionMapData()) + return EC; + if (auto EC = initializeOldFpoRecords(Pdb)) + return EC; + if (auto EC = initializeNewFpoRecords(Pdb)) + return EC; + + if (Reader.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Found unexpected bytes in DBI Stream."); + + if (!ECSubstream.empty()) { + BinaryStreamReader ECReader(ECSubstream.StreamData); + if (auto EC = ECNames.reload(ECReader)) + return EC; + } + + return Error::success(); +} + +PdbRaw_DbiVer DbiStream::getDbiVersion() const { + uint32_t Value = Header->VersionHeader; + return static_cast<PdbRaw_DbiVer>(Value); +} + +uint32_t DbiStream::getAge() const { return Header->Age; } + +uint16_t DbiStream::getPublicSymbolStreamIndex() const { + return Header->PublicSymbolStreamIndex; +} + +uint16_t DbiStream::getGlobalSymbolStreamIndex() const { + return Header->GlobalSymbolStreamIndex; +} + +uint16_t DbiStream::getFlags() const { return Header->Flags; } + +bool DbiStream::isIncrementallyLinked() const { + return (Header->Flags & DbiFlags::FlagIncrementalMask) != 0; +} + +bool DbiStream::hasCTypes() const { + return (Header->Flags & DbiFlags::FlagHasCTypesMask) != 0; +} + +bool DbiStream::isStripped() const { + return (Header->Flags & DbiFlags::FlagStrippedMask) != 0; +} + +uint16_t DbiStream::getBuildNumber() const { return Header->BuildNumber; } + +uint16_t DbiStream::getBuildMajorVersion() const { + return (Header->BuildNumber & DbiBuildNo::BuildMajorMask) >> + DbiBuildNo::BuildMajorShift; +} + +uint16_t DbiStream::getBuildMinorVersion() const { + return (Header->BuildNumber & DbiBuildNo::BuildMinorMask) >> + DbiBuildNo::BuildMinorShift; +} + +uint16_t DbiStream::getPdbDllRbld() const { return Header->PdbDllRbld; } + +uint32_t DbiStream::getPdbDllVersion() const { return Header->PdbDllVersion; } + +uint32_t DbiStream::getSymRecordStreamIndex() const { + return Header->SymRecordStreamIndex; +} + +PDB_Machine DbiStream::getMachineType() const { + uint16_t Machine = Header->MachineType; + return static_cast<PDB_Machine>(Machine); +} + +FixedStreamArray<object::coff_section> DbiStream::getSectionHeaders() const { + return SectionHeaders; +} + +bool DbiStream::hasOldFpoRecords() const { return OldFpoStream != nullptr; } + +FixedStreamArray<object::FpoData> DbiStream::getOldFpoRecords() const { + return OldFpoRecords; +} + +bool DbiStream::hasNewFpoRecords() const { return NewFpoStream != nullptr; } + +const DebugFrameDataSubsectionRef &DbiStream::getNewFpoRecords() const { + return NewFpoRecords; +} + +const DbiModuleList &DbiStream::modules() const { return Modules; } + +FixedStreamArray<SecMapEntry> DbiStream::getSectionMap() const { + return SectionMap; +} + +void DbiStream::visitSectionContributions( + ISectionContribVisitor &Visitor) const { + if (!SectionContribs.empty()) { + assert(SectionContribVersion == DbiSecContribVer60); + for (auto &SC : SectionContribs) + Visitor.visit(SC); + } else if (!SectionContribs2.empty()) { + assert(SectionContribVersion == DbiSecContribV2); + for (auto &SC : SectionContribs2) + Visitor.visit(SC); + } +} + +Expected<StringRef> DbiStream::getECName(uint32_t NI) const { + return ECNames.getStringForID(NI); +} + +Error DbiStream::initializeSectionContributionData() { + if (SecContrSubstream.empty()) + return Error::success(); + + BinaryStreamReader SCReader(SecContrSubstream.StreamData); + if (auto EC = SCReader.readEnum(SectionContribVersion)) + return EC; + + if (SectionContribVersion == DbiSecContribVer60) + return loadSectionContribs<SectionContrib>(SectionContribs, SCReader); + if (SectionContribVersion == DbiSecContribV2) + return loadSectionContribs<SectionContrib2>(SectionContribs2, SCReader); + + return make_error<RawError>(raw_error_code::feature_unsupported, + "Unsupported DBI Section Contribution version"); +} + +// Initializes this->SectionHeaders. +Error DbiStream::initializeSectionHeadersData(PDBFile *Pdb) { + Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = + createIndexedStreamForHeaderType(Pdb, DbgHeaderType::SectionHdr); + if (auto EC = ExpectedStream.takeError()) + return EC; + + auto &SHS = *ExpectedStream; + if (!SHS) + return Error::success(); + + size_t StreamLen = SHS->getLength(); + if (StreamLen % sizeof(object::coff_section)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupted section header stream."); + + size_t NumSections = StreamLen / sizeof(object::coff_section); + BinaryStreamReader Reader(*SHS); + if (auto EC = Reader.readArray(SectionHeaders, NumSections)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Could not read a bitmap."); + + SectionHeaderStream = std::move(SHS); + return Error::success(); +} + +// Initializes this->Fpos. +Error DbiStream::initializeOldFpoRecords(PDBFile *Pdb) { + Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = + createIndexedStreamForHeaderType(Pdb, DbgHeaderType::FPO); + if (auto EC = ExpectedStream.takeError()) + return EC; + + auto &FS = *ExpectedStream; + if (!FS) + return Error::success(); + + size_t StreamLen = FS->getLength(); + if (StreamLen % sizeof(object::FpoData)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupted Old FPO stream."); + + size_t NumRecords = StreamLen / sizeof(object::FpoData); + BinaryStreamReader Reader(*FS); + if (auto EC = Reader.readArray(OldFpoRecords, NumRecords)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupted Old FPO stream."); + OldFpoStream = std::move(FS); + return Error::success(); +} + +Error DbiStream::initializeNewFpoRecords(PDBFile *Pdb) { + Expected<std::unique_ptr<msf::MappedBlockStream>> ExpectedStream = + createIndexedStreamForHeaderType(Pdb, DbgHeaderType::NewFPO); + if (auto EC = ExpectedStream.takeError()) + return EC; + + auto &FS = *ExpectedStream; + if (!FS) + return Error::success(); + + if (auto EC = NewFpoRecords.initialize(*FS)) + return EC; + + NewFpoStream = std::move(FS); + return Error::success(); +} + +Expected<std::unique_ptr<msf::MappedBlockStream>> +DbiStream::createIndexedStreamForHeaderType(PDBFile *Pdb, + DbgHeaderType Type) const { + if (!Pdb) + return nullptr; + + if (DbgStreams.empty()) + return nullptr; + + uint32_t StreamNum = getDebugStreamIndex(Type); + + // This means there is no such stream. + if (StreamNum == kInvalidStreamIndex) + return nullptr; + + return Pdb->safelyCreateIndexedStream(StreamNum); +} + +BinarySubstreamRef DbiStream::getSectionContributionData() const { + return SecContrSubstream; +} + +BinarySubstreamRef DbiStream::getSecMapSubstreamData() const { + return SecMapSubstream; +} + +BinarySubstreamRef DbiStream::getModiSubstreamData() const { + return ModiSubstream; +} + +BinarySubstreamRef DbiStream::getFileInfoSubstreamData() const { + return FileInfoSubstream; +} + +BinarySubstreamRef DbiStream::getTypeServerMapSubstreamData() const { + return TypeServerMapSubstream; +} + +BinarySubstreamRef DbiStream::getECSubstreamData() const { return ECSubstream; } + +Error DbiStream::initializeSectionMapData() { + if (SecMapSubstream.empty()) + return Error::success(); + + BinaryStreamReader SMReader(SecMapSubstream.StreamData); + const SecMapHeader *Header; + if (auto EC = SMReader.readObject(Header)) + return EC; + if (auto EC = SMReader.readArray(SectionMap, Header->SecCount)) + return EC; + return Error::success(); +} + +uint32_t DbiStream::getDebugStreamIndex(DbgHeaderType Type) const { + uint16_t T = static_cast<uint16_t>(Type); + if (T >= DbgStreams.size()) + return kInvalidStreamIndex; + return DbgStreams[T]; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp new file mode 100644 index 000000000000..0e00c2f7ff98 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -0,0 +1,455 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder &Msf) + : Msf(Msf), Allocator(Msf.getAllocator()), Age(1), BuildNumber(0), + PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86), + Header(nullptr) {} + +DbiStreamBuilder::~DbiStreamBuilder() {} + +void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; } + +void DbiStreamBuilder::setAge(uint32_t A) { Age = A; } + +void DbiStreamBuilder::setBuildNumber(uint16_t B) { BuildNumber = B; } + +void DbiStreamBuilder::setBuildNumber(uint8_t Major, uint8_t Minor) { + BuildNumber = (uint16_t(Major) << DbiBuildNo::BuildMajorShift) & + DbiBuildNo::BuildMajorMask; + BuildNumber |= (uint16_t(Minor) << DbiBuildNo::BuildMinorShift) & + DbiBuildNo::BuildMinorMask; + BuildNumber |= DbiBuildNo::NewVersionFormatMask; +} + +void DbiStreamBuilder::setPdbDllVersion(uint16_t V) { PdbDllVersion = V; } + +void DbiStreamBuilder::setPdbDllRbld(uint16_t R) { PdbDllRbld = R; } + +void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; } + +void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; } + +void DbiStreamBuilder::setMachineType(COFF::MachineTypes M) { + // These enums are mirrors of each other, so we can just cast the value. + MachineType = static_cast<pdb::PDB_Machine>(static_cast<unsigned>(M)); +} + +void DbiStreamBuilder::setSectionMap(ArrayRef<SecMapEntry> SecMap) { + SectionMap = SecMap; +} + +void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index) { + GlobalsStreamIndex = Index; +} + +void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index) { + SymRecordStreamIndex = Index; +} + +void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index) { + PublicsStreamIndex = Index; +} + +void DbiStreamBuilder::addNewFpoData(const codeview::FrameData &FD) { + if (!NewFpoData.hasValue()) + NewFpoData.emplace(false); + + NewFpoData->addFrameData(FD); +} + +void DbiStreamBuilder::addOldFpoData(const object::FpoData &FD) { + OldFpoData.push_back(FD); +} + +Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type, + ArrayRef<uint8_t> Data) { + assert(Type != DbgHeaderType::NewFPO && + "NewFPO data should be written via addFrameData()!"); + + DbgStreams[(int)Type].emplace(); + DbgStreams[(int)Type]->Size = Data.size(); + DbgStreams[(int)Type]->WriteFn = [Data](BinaryStreamWriter &Writer) { + return Writer.writeArray(Data); + }; + return Error::success(); +} + +uint32_t DbiStreamBuilder::addECName(StringRef Name) { + return ECNamesBuilder.insert(Name); +} + +uint32_t DbiStreamBuilder::calculateSerializedLength() const { + // For now we only support serializing the header. + return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() + + calculateModiSubstreamSize() + calculateSectionContribsStreamSize() + + calculateSectionMapStreamSize() + calculateDbgStreamsSize() + + ECNamesBuilder.calculateSerializedSize(); +} + +Expected<DbiModuleDescriptorBuilder &> +DbiStreamBuilder::addModuleInfo(StringRef ModuleName) { + uint32_t Index = ModiList.size(); + ModiList.push_back( + std::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf)); + return *ModiList.back(); +} + +Error DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder &Module, + StringRef File) { + uint32_t Index = SourceFileNames.size(); + SourceFileNames.insert(std::make_pair(File, Index)); + Module.addSourceFile(File); + return Error::success(); +} + +Expected<uint32_t> DbiStreamBuilder::getSourceFileNameIndex(StringRef File) { + auto NameIter = SourceFileNames.find(File); + if (NameIter == SourceFileNames.end()) + return make_error<RawError>(raw_error_code::no_entry, + "The specified source file was not found"); + return NameIter->getValue(); +} + +uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const { + uint32_t Size = 0; + for (const auto &M : ModiList) + Size += M->calculateSerializedLength(); + return Size; +} + +uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const { + if (SectionContribs.empty()) + return 0; + return sizeof(enum PdbRaw_DbiSecContribVer) + + sizeof(SectionContribs[0]) * SectionContribs.size(); +} + +uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const { + if (SectionMap.empty()) + return 0; + return sizeof(SecMapHeader) + sizeof(SecMapEntry) * SectionMap.size(); +} + +uint32_t DbiStreamBuilder::calculateNamesOffset() const { + uint32_t Offset = 0; + Offset += sizeof(ulittle16_t); // NumModules + Offset += sizeof(ulittle16_t); // NumSourceFiles + Offset += ModiList.size() * sizeof(ulittle16_t); // ModIndices + Offset += ModiList.size() * sizeof(ulittle16_t); // ModFileCounts + uint32_t NumFileInfos = 0; + for (const auto &M : ModiList) + NumFileInfos += M->source_files().size(); + Offset += NumFileInfos * sizeof(ulittle32_t); // FileNameOffsets + return Offset; +} + +uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const { + uint32_t Size = calculateNamesOffset(); + Size += calculateNamesBufferSize(); + return alignTo(Size, sizeof(uint32_t)); +} + +uint32_t DbiStreamBuilder::calculateNamesBufferSize() const { + uint32_t Size = 0; + for (const auto &F : SourceFileNames) { + Size += F.getKeyLength() + 1; // Names[I]; + } + return Size; +} + +uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const { + return DbgStreams.size() * sizeof(uint16_t); +} + +Error DbiStreamBuilder::generateFileInfoSubstream() { + uint32_t Size = calculateFileInfoSubstreamSize(); + auto Data = Allocator.Allocate<uint8_t>(Size); + uint32_t NamesOffset = calculateNamesOffset(); + + FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size), + llvm::support::little); + + WritableBinaryStreamRef MetadataBuffer = + WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset); + BinaryStreamWriter MetadataWriter(MetadataBuffer); + + uint16_t ModiCount = std::min<uint32_t>(UINT16_MAX, ModiList.size()); + uint16_t FileCount = std::min<uint32_t>(UINT16_MAX, SourceFileNames.size()); + if (auto EC = MetadataWriter.writeInteger(ModiCount)) // NumModules + return EC; + if (auto EC = MetadataWriter.writeInteger(FileCount)) // NumSourceFiles + return EC; + for (uint16_t I = 0; I < ModiCount; ++I) { + if (auto EC = MetadataWriter.writeInteger(I)) // Mod Indices + return EC; + } + for (const auto &MI : ModiList) { + FileCount = static_cast<uint16_t>(MI->source_files().size()); + if (auto EC = MetadataWriter.writeInteger(FileCount)) // Mod File Counts + return EC; + } + + // Before writing the FileNameOffsets array, write the NamesBuffer array. + // A side effect of this is that this will actually compute the various + // file name offsets, so we can then go back and write the FileNameOffsets + // array to the other substream. + NamesBuffer = WritableBinaryStreamRef(FileInfoBuffer).drop_front(NamesOffset); + BinaryStreamWriter NameBufferWriter(NamesBuffer); + for (auto &Name : SourceFileNames) { + Name.second = NameBufferWriter.getOffset(); + if (auto EC = NameBufferWriter.writeCString(Name.getKey())) + return EC; + } + + for (const auto &MI : ModiList) { + for (StringRef Name : MI->source_files()) { + auto Result = SourceFileNames.find(Name); + if (Result == SourceFileNames.end()) + return make_error<RawError>(raw_error_code::no_entry, + "The source file was not found."); + if (auto EC = MetadataWriter.writeInteger(Result->second)) + return EC; + } + } + + if (auto EC = NameBufferWriter.padToAlignment(sizeof(uint32_t))) + return EC; + + if (NameBufferWriter.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::invalid_format, + "The names buffer contained unexpected data."); + + if (MetadataWriter.bytesRemaining() > sizeof(uint32_t)) + return make_error<RawError>( + raw_error_code::invalid_format, + "The metadata buffer contained unexpected data."); + + return Error::success(); +} + +Error DbiStreamBuilder::finalize() { + if (Header) + return Error::success(); + + for (auto &MI : ModiList) + MI->finalize(); + + if (auto EC = generateFileInfoSubstream()) + return EC; + + DbiStreamHeader *H = Allocator.Allocate<DbiStreamHeader>(); + ::memset(H, 0, sizeof(DbiStreamHeader)); + H->VersionHeader = *VerHeader; + H->VersionSignature = -1; + H->Age = Age; + H->BuildNumber = BuildNumber; + H->Flags = Flags; + H->PdbDllRbld = PdbDllRbld; + H->PdbDllVersion = PdbDllVersion; + H->MachineType = static_cast<uint16_t>(MachineType); + + H->ECSubstreamSize = ECNamesBuilder.calculateSerializedSize(); + H->FileInfoSize = FileInfoBuffer.getLength(); + H->ModiSubstreamSize = calculateModiSubstreamSize(); + H->OptionalDbgHdrSize = DbgStreams.size() * sizeof(uint16_t); + H->SecContrSubstreamSize = calculateSectionContribsStreamSize(); + H->SectionMapSize = calculateSectionMapStreamSize(); + H->TypeServerSize = 0; + H->SymRecordStreamIndex = SymRecordStreamIndex; + H->PublicSymbolStreamIndex = PublicsStreamIndex; + H->MFCTypeServerIndex = 0; // Not sure what this is, but link.exe writes 0. + H->GlobalSymbolStreamIndex = GlobalsStreamIndex; + + Header = H; + return Error::success(); +} + +Error DbiStreamBuilder::finalizeMsfLayout() { + if (NewFpoData.hasValue()) { + DbgStreams[(int)DbgHeaderType::NewFPO].emplace(); + DbgStreams[(int)DbgHeaderType::NewFPO]->Size = + NewFpoData->calculateSerializedSize(); + DbgStreams[(int)DbgHeaderType::NewFPO]->WriteFn = + [this](BinaryStreamWriter &Writer) { + return NewFpoData->commit(Writer); + }; + } + + if (!OldFpoData.empty()) { + DbgStreams[(int)DbgHeaderType::FPO].emplace(); + DbgStreams[(int)DbgHeaderType::FPO]->Size = + sizeof(object::FpoData) * OldFpoData.size(); + DbgStreams[(int)DbgHeaderType::FPO]->WriteFn = + [this](BinaryStreamWriter &Writer) { + return Writer.writeArray(makeArrayRef(OldFpoData)); + }; + } + + for (auto &S : DbgStreams) { + if (!S.hasValue()) + continue; + auto ExpectedIndex = Msf.addStream(S->Size); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + S->StreamNumber = *ExpectedIndex; + } + + for (auto &MI : ModiList) { + if (auto EC = MI->finalizeMsfLayout()) + return EC; + } + + uint32_t Length = calculateSerializedLength(); + if (auto EC = Msf.setStreamSize(StreamDBI, Length)) + return EC; + return Error::success(); +} + +static uint16_t toSecMapFlags(uint32_t Flags) { + uint16_t Ret = 0; + if (Flags & COFF::IMAGE_SCN_MEM_READ) + Ret |= static_cast<uint16_t>(OMFSegDescFlags::Read); + if (Flags & COFF::IMAGE_SCN_MEM_WRITE) + Ret |= static_cast<uint16_t>(OMFSegDescFlags::Write); + if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) + Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute); + if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) + Ret |= static_cast<uint16_t>(OMFSegDescFlags::Execute); + if (!(Flags & COFF::IMAGE_SCN_MEM_16BIT)) + Ret |= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit); + + // This seems always 1. + Ret |= static_cast<uint16_t>(OMFSegDescFlags::IsSelector); + + return Ret; +} + +// 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. +// I don't know why a PDB file contains both a COFF section header and +// a Section Map, but it seems it must be present in a PDB. +std::vector<SecMapEntry> DbiStreamBuilder::createSectionMap( + ArrayRef<llvm::object::coff_section> SecHdrs) { + std::vector<SecMapEntry> Ret; + int Idx = 0; + + auto Add = [&]() -> SecMapEntry & { + Ret.emplace_back(); + auto &Entry = Ret.back(); + memset(&Entry, 0, sizeof(Entry)); + + Entry.Frame = Idx + 1; + + // We don't know the meaning of these fields yet. + Entry.SecName = UINT16_MAX; + Entry.ClassName = UINT16_MAX; + + return Entry; + }; + + for (auto &Hdr : SecHdrs) { + auto &Entry = Add(); + Entry.Flags = toSecMapFlags(Hdr.Characteristics); + Entry.SecByteLength = Hdr.VirtualSize; + ++Idx; + } + + // The last entry is for absolute symbols. + auto &Entry = Add(); + Entry.Flags = static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit) | + static_cast<uint16_t>(OMFSegDescFlags::IsAbsoluteAddress); + Entry.SecByteLength = UINT32_MAX; + + return Ret; +} + +Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout, + WritableBinaryStreamRef MsfBuffer) { + if (auto EC = finalize()) + return EC; + + auto DbiS = WritableMappedBlockStream::createIndexedStream( + Layout, MsfBuffer, StreamDBI, Allocator); + + BinaryStreamWriter Writer(*DbiS); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + for (auto &M : ModiList) { + if (auto EC = M->commit(Writer, Layout, MsfBuffer)) + return EC; + } + + if (!SectionContribs.empty()) { + if (auto EC = Writer.writeEnum(DbiSecContribVer60)) + return EC; + if (auto EC = Writer.writeArray(makeArrayRef(SectionContribs))) + return EC; + } + + if (!SectionMap.empty()) { + ulittle16_t Size = static_cast<ulittle16_t>(SectionMap.size()); + SecMapHeader SMHeader = {Size, Size}; + if (auto EC = Writer.writeObject(SMHeader)) + return EC; + if (auto EC = Writer.writeArray(SectionMap)) + return EC; + } + + if (auto EC = Writer.writeStreamRef(FileInfoBuffer)) + return EC; + + if (auto EC = ECNamesBuilder.commit(Writer)) + return EC; + + for (auto &Stream : DbgStreams) { + uint16_t StreamNumber = kInvalidStreamIndex; + if (Stream.hasValue()) + StreamNumber = Stream->StreamNumber; + if (auto EC = Writer.writeInteger(StreamNumber)) + return EC; + } + + for (auto &Stream : DbgStreams) { + if (!Stream.hasValue()) + continue; + assert(Stream->StreamNumber != kInvalidStreamIndex); + + auto WritableStream = WritableMappedBlockStream::createIndexedStream( + Layout, MsfBuffer, Stream->StreamNumber, Allocator); + BinaryStreamWriter DbgStreamWriter(*WritableStream); + + if (auto EC = Stream->WriteFn(DbgStreamWriter)) + return EC; + } + + if (Writer.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::invalid_format, + "Unexpected bytes found in DBI Stream"); + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp b/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp new file mode 100644 index 000000000000..f5125393695b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp @@ -0,0 +1,37 @@ +//===- EnumTables.cpp - Enum to string conversion tables --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" + +using namespace llvm; +using namespace llvm::pdb; + +#define PDB_ENUM_CLASS_ENT(enum_class, enum) \ + { #enum, std::underlying_type < enum_class > ::type(enum_class::enum) } + +#define PDB_ENUM_ENT(ns, enum) \ + { #enum, ns::enum } + +static const EnumEntry<uint16_t> OMFSegMapDescFlagNames[] = { + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Read), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Write), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, Execute), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, AddressIs32Bit), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsSelector), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsAbsoluteAddress), + PDB_ENUM_CLASS_ENT(OMFSegDescFlags, IsGroup), +}; + +namespace llvm { +namespace pdb { +ArrayRef<EnumEntry<uint16_t>> getOMFSegMapDescFlagNames() { + return makeArrayRef(OMFSegMapDescFlagNames); +} +} +}
\ No newline at end of file diff --git a/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp new file mode 100644 index 000000000000..432f1e9b24d3 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp @@ -0,0 +1,378 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" + +#include "llvm/ADT/DenseSet.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 "llvm/Support/xxhash.h" +#include <algorithm> +#include <vector> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::codeview; + +struct llvm::pdb::GSIHashStreamBuilder { + struct SymbolDenseMapInfo { + static inline CVSymbol getEmptyKey() { + static CVSymbol Empty; + return Empty; + } + static inline CVSymbol getTombstoneKey() { + static CVSymbol Tombstone( + DenseMapInfo<ArrayRef<uint8_t>>::getTombstoneKey()); + return Tombstone; + } + static unsigned getHashValue(const CVSymbol &Val) { + return xxHash64(Val.RecordData); + } + static bool isEqual(const CVSymbol &LHS, const CVSymbol &RHS) { + return LHS.RecordData == RHS.RecordData; + } + }; + + std::vector<CVSymbol> Records; + uint32_t StreamIndex; + llvm::DenseSet<CVSymbol, SymbolDenseMapInfo> SymbolHashes; + 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); + addSymbol(SymbolSerializer::writeOneSymbol(Copy, Msf.getAllocator(), + CodeViewContainer::Pdb)); + } + void addSymbol(const CVSymbol &Symbol) { + if (Symbol.kind() == S_UDT || Symbol.kind() == S_CONSTANT) { + auto Iter = SymbolHashes.insert(Symbol); + if (!Iter.second) + return; + } + + 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(); +} + +static bool isAsciiString(StringRef S) { + return llvm::all_of(S, [](char C) { return unsigned(C) < 0x80; }); +} + +// See `caseInsensitiveComparePchPchCchCch` in gsi.cpp +static bool gsiRecordLess(StringRef S1, StringRef S2) { + size_t LS = S1.size(); + size_t RS = S2.size(); + // Shorter strings always compare less than longer strings. + if (LS != RS) + return LS < RS; + + // If either string contains non ascii characters, memcmp them. + if (LLVM_UNLIKELY(!isAsciiString(S1) || !isAsciiString(S2))) + return memcmp(S1.data(), S2.data(), LS) < 0; + + // Both strings are ascii, perform a case-insenstive comparison. + return S1.compare_lower(S2.data()) < 0; +} + +void GSIHashStreamBuilder::finalizeBuckets(uint32_t RecordZeroOffset) { + std::array<std::vector<std::pair<StringRef, 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(std::make_pair(Name, HR)); + 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); + + // Sort each bucket by memcmp of the symbol's name. It's important that + // we use the same sorting algorithm as is used by the reference + // implementation to ensure that the search for a record within a bucket + // can properly early-out when it detects the record won't be found. The + // algorithm used here corredsponds to the function + // caseInsensitiveComparePchPchCchCch in the reference implementation. + llvm::sort(Bucket, [](const std::pair<StringRef, PSHashRecord> &Left, + const std::pair<StringRef, PSHashRecord> &Right) { + return gsiRecordLess(Left.first, Right.first); + }); + + for (const auto &Entry : Bucket) + HashRecords.push_back(Entry.second); + } +} + +GSIStreamBuilder::GSIStreamBuilder(msf::MSFBuilder &Msf) + : Msf(Msf), PSH(std::make_unique<GSIHashStreamBuilder>()), + GSH(std::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(calculateGlobalsHashStreamSize()); + if (!Idx) + return Idx.takeError(); + GSH->StreamIndex = *Idx; + Idx = Msf.addStream(calculatePublicsHashStreamSize()); + if (!Idx) + return Idx.takeError(); + PSH->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(); + } + llvm::stable_sort(PublicsByAddr, 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 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.SymHash = PSH->calculateSerializedLength(); + Header.AddrMap = PSH->Records.size() * 4; + Header.NumThunks = 0; + Header.SizeOfThunk = 0; + Header.ISectThunkTable = 0; + memset(Header.Padding, 0, sizeof(Header.Padding)); + Header.OffThunkTable = 0; + Header.NumSections = 0; + 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/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp b/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp new file mode 100644 index 000000000000..f27d60f46815 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp @@ -0,0 +1,181 @@ +//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +GlobalsStream::GlobalsStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +GlobalsStream::~GlobalsStream() = default; + +Error GlobalsStream::reload() { + BinaryStreamReader Reader(*Stream); + if (auto E = GlobalsTable.read(Reader)) + return E; + return Error::success(); +} + +std::vector<std::pair<uint32_t, codeview::CVSymbol>> +GlobalsStream::findRecordsByName(StringRef Name, + const SymbolStream &Symbols) const { + std::vector<std::pair<uint32_t, codeview::CVSymbol>> Result; + + // Hash the name to figure out which bucket this goes into. + size_t ExpandedBucketIndex = hashStringV1(Name) % IPHR_HASH; + int32_t CompressedBucketIndex = GlobalsTable.BucketMap[ExpandedBucketIndex]; + if (CompressedBucketIndex == -1) + return Result; + + uint32_t LastBucketIndex = GlobalsTable.HashBuckets.size() - 1; + uint32_t StartRecordIndex = + GlobalsTable.HashBuckets[CompressedBucketIndex] / 12; + uint32_t EndRecordIndex = 0; + if (LLVM_LIKELY(uint32_t(CompressedBucketIndex) < LastBucketIndex)) { + EndRecordIndex = GlobalsTable.HashBuckets[CompressedBucketIndex + 1]; + } else { + // If this is the last bucket, it consists of all hash records until the end + // of the HashRecords array. + EndRecordIndex = GlobalsTable.HashRecords.size() * 12; + } + + EndRecordIndex /= 12; + + assert(EndRecordIndex <= GlobalsTable.HashRecords.size()); + while (StartRecordIndex < EndRecordIndex) { + PSHashRecord PSH = GlobalsTable.HashRecords[StartRecordIndex]; + uint32_t Off = PSH.Off - 1; + codeview::CVSymbol Record = Symbols.readRecord(Off); + if (codeview::getSymbolName(Record) == Name) + Result.push_back(std::make_pair(Off, std::move(Record))); + ++StartRecordIndex; + } + return Result; +} + +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(); +} + +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; + + // 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, + FixedStreamArray<support::ulittle32_t> &HashBitmap, + const GSIHashHeader *HashHdr, + MutableArrayRef<int32_t> BucketMap, + BinaryStreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) + return EC; + + // 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 / 32; + if (auto EC = Reader.readArray(HashBitmap, NumBitmapEntries)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read a bitmap.")); + uint32_t NumBuckets1 = 0; + uint32_t CompressedBucketIdx = 0; + for (uint32_t I = 0; I <= IPHR_HASH; ++I) { + uint8_t WordIdx = I / 32; + uint8_t BitIdx = I % 32; + bool IsSet = HashBitmap[WordIdx] & (1U << BitIdx); + if (IsSet) { + ++NumBuckets1; + BucketMap[I] = CompressedBucketIdx++; + } else { + BucketMap[I] = -1; + } + } + + uint32_t NumBuckets = 0; + for (uint32_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 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, + BucketMap, Reader)) + return EC; + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/Hash.cpp b/llvm/lib/DebugInfo/PDB/Native/Hash.cpp new file mode 100644 index 000000000000..7fb6b4bd5d31 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/Hash.cpp @@ -0,0 +1,84 @@ +//===- Hash.cpp - PDB Hash Functions --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Endian.h" +#include <cstdint> + +using namespace llvm; +using namespace llvm::support; + +// Corresponds to `Hasher::lhashPbCb` in PDB/include/misc.h. +// Used for name hash table and TPI/IPI hashes. +uint32_t pdb::hashStringV1(StringRef Str) { + uint32_t Result = 0; + uint32_t Size = Str.size(); + + ArrayRef<ulittle32_t> Longs(reinterpret_cast<const ulittle32_t *>(Str.data()), + Size / 4); + + for (auto Value : Longs) + Result ^= Value; + + const uint8_t *Remainder = reinterpret_cast<const uint8_t *>(Longs.end()); + uint32_t RemainderSize = Size % 4; + + // Maximum of 3 bytes left. Hash a 2 byte word if possible, then hash the + // possibly remaining 1 byte. + if (RemainderSize >= 2) { + uint16_t Value = *reinterpret_cast<const ulittle16_t *>(Remainder); + Result ^= static_cast<uint32_t>(Value); + Remainder += 2; + RemainderSize -= 2; + } + + // hash possible odd byte + if (RemainderSize == 1) { + Result ^= *(Remainder++); + } + + const uint32_t toLowerMask = 0x20202020; + Result |= toLowerMask; + Result ^= (Result >> 11); + + return Result ^ (Result >> 16); +} + +// Corresponds to `HasherV2::HashULONG` in PDB/include/misc.h. +// Used for name hash table. +uint32_t pdb::hashStringV2(StringRef Str) { + uint32_t Hash = 0xb170a1bf; + + ArrayRef<char> Buffer(Str.begin(), Str.end()); + + ArrayRef<ulittle32_t> Items( + reinterpret_cast<const ulittle32_t *>(Buffer.data()), + Buffer.size() / sizeof(ulittle32_t)); + for (ulittle32_t Item : Items) { + Hash += Item; + Hash += (Hash << 10); + Hash ^= (Hash >> 6); + } + Buffer = Buffer.slice(Items.size() * sizeof(ulittle32_t)); + for (uint8_t Item : Buffer) { + Hash += Item; + Hash += (Hash << 10); + Hash ^= (Hash >> 6); + } + + return Hash * 1664525U + 1013904223U; +} + +// Corresponds to `SigForPbCb` in langapi/shared/crc32.h. +uint32_t pdb::hashBufferV8(ArrayRef<uint8_t> Buf) { + JamCRC JC(/*Init=*/0U); + JC.update(Buf); + return JC.getCRC(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp b/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp new file mode 100644 index 000000000000..dfdcdf1f4eaf --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp @@ -0,0 +1,71 @@ +//===- HashTable.cpp - PDB Hash Table -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +Error llvm::pdb::readSparseBitVector(BinaryStreamReader &Stream, + SparseBitVector<> &V) { + uint32_t NumWords; + if (auto EC = Stream.readInteger(NumWords)) + return joinErrors( + std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected hash table number of words")); + + for (uint32_t I = 0; I != NumWords; ++I) { + uint32_t Word; + if (auto EC = Stream.readInteger(Word)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected hash table word")); + for (unsigned Idx = 0; Idx < 32; ++Idx) + if (Word & (1U << Idx)) + V.set((I * 32) + Idx); + } + return Error::success(); +} + +Error llvm::pdb::writeSparseBitVector(BinaryStreamWriter &Writer, + SparseBitVector<> &Vec) { + constexpr int BitsPerWord = 8 * sizeof(uint32_t); + + int ReqBits = Vec.find_last() + 1; + uint32_t ReqWords = alignTo(ReqBits, BitsPerWord) / BitsPerWord; + if (auto EC = Writer.writeInteger(ReqWords)) + return joinErrors( + std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not write linear map number of words")); + + uint32_t Idx = 0; + for (uint32_t I = 0; I != ReqWords; ++I) { + uint32_t Word = 0; + for (uint32_t WordIdx = 0; WordIdx < 32; ++WordIdx, ++Idx) { + if (Vec.test(Idx)) + Word |= (1 << WordIdx); + } + if (auto EC = Writer.writeInteger(Word)) + return joinErrors(std::move(EC), make_error<RawError>( + raw_error_code::corrupt_file, + "Could not write linear map word")); + } + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp b/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp new file mode 100644 index 000000000000..f41bb32d69af --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -0,0 +1,131 @@ +//===- InfoStream.cpp - PDB Info Stream (Stream 1) Access -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.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" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStream::InfoStream(std::unique_ptr<BinaryStream> Stream) + : Stream(std::move(Stream)), Header(nullptr) {} + +Error InfoStream::reload() { + BinaryStreamReader Reader(*Stream); + + if (auto EC = Reader.readObject(Header)) + return joinErrors( + std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "PDB Stream does not contain a header.")); + + switch (Header->Version) { + case PdbImplVC70: + case PdbImplVC80: + case PdbImplVC110: + case PdbImplVC140: + break; + default: + return make_error<RawError>(raw_error_code::corrupt_file, + "Unsupported PDB stream version."); + } + + uint32_t Offset = Reader.getOffset(); + if (auto EC = NamedStreams.load(Reader)) + return EC; + uint32_t NewOffset = Reader.getOffset(); + NamedStreamMapByteSize = NewOffset - Offset; + + Reader.setOffset(Offset); + if (auto EC = Reader.readSubstream(SubNamedStreams, NamedStreamMapByteSize)) + return EC; + + bool Stop = false; + while (!Stop && !Reader.empty()) { + PdbRaw_FeatureSig Sig; + if (auto EC = Reader.readEnum(Sig)) + return EC; + // Since this value comes from a file, it's possible we have some strange + // value which doesn't correspond to any value. We don't want to warn on + // -Wcovered-switch-default in this case, so switch on the integral value + // instead of the enumeration value. + switch (uint32_t(Sig)) { + case uint32_t(PdbRaw_FeatureSig::VC110): + // No other flags for VC110 PDB. + Stop = true; + LLVM_FALLTHROUGH; + case uint32_t(PdbRaw_FeatureSig::VC140): + Features |= PdbFeatureContainsIdStream; + break; + case uint32_t(PdbRaw_FeatureSig::NoTypeMerge): + Features |= PdbFeatureNoTypeMerging; + break; + case uint32_t(PdbRaw_FeatureSig::MinimalDebugInfo): + Features |= PdbFeatureMinimalDebugInfo; + break; + default: + continue; + } + FeatureSignatures.push_back(Sig); + } + return Error::success(); +} + +uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); } + +Expected<uint32_t> InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { + uint32_t Result; + if (!NamedStreams.get(Name, Result)) + return make_error<RawError>(raw_error_code::no_stream); + return Result; +} + +StringMap<uint32_t> InfoStream::named_streams() const { + return NamedStreams.entries(); +} + +bool InfoStream::containsIdStream() const { + return !!(Features & PdbFeatureContainsIdStream); +} + +PdbRaw_ImplVer InfoStream::getVersion() const { + return static_cast<PdbRaw_ImplVer>(uint32_t(Header->Version)); +} + +uint32_t InfoStream::getSignature() const { + return uint32_t(Header->Signature); +} + +uint32_t InfoStream::getAge() const { return uint32_t(Header->Age); } + +GUID InfoStream::getGuid() const { return Header->Guid; } + +uint32_t InfoStream::getNamedStreamMapByteSize() const { + return NamedStreamMapByteSize; +} + +PdbRaw_Features InfoStream::getFeatures() const { return Features; } + +ArrayRef<PdbRaw_FeatureSig> InfoStream::getFeatureSignatures() const { + return FeatureSignatures; +} + +const NamedStreamMap &InfoStream::getNamedStreams() const { + return NamedStreams; +} + +BinarySubstreamRef InfoStream::getNamedStreamsBuffer() const { + return SubNamedStreams; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp new file mode 100644 index 000000000000..42daa7cae799 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp @@ -0,0 +1,82 @@ +//===- InfoStreamBuilder.cpp - PDB Info Stream Creation ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStreamBuilder::InfoStreamBuilder(msf::MSFBuilder &Msf, + NamedStreamMap &NamedStreams) + : Msf(Msf), Ver(PdbRaw_ImplVer::PdbImplVC70), Age(0), + NamedStreams(NamedStreams) { + ::memset(&Guid, 0, sizeof(Guid)); +} + +void InfoStreamBuilder::setVersion(PdbRaw_ImplVer V) { Ver = V; } + +void InfoStreamBuilder::addFeature(PdbRaw_FeatureSig Sig) { + Features.push_back(Sig); +} + +void InfoStreamBuilder::setHashPDBContentsToGUID(bool B) { + HashPDBContentsToGUID = B; +} + +void InfoStreamBuilder::setAge(uint32_t A) { Age = A; } + +void InfoStreamBuilder::setSignature(uint32_t S) { Signature = S; } + +void InfoStreamBuilder::setGuid(GUID G) { Guid = G; } + + +Error InfoStreamBuilder::finalizeMsfLayout() { + uint32_t Length = sizeof(InfoStreamHeader) + + NamedStreams.calculateSerializedLength() + + (Features.size() + 1) * sizeof(uint32_t); + if (auto EC = Msf.setStreamSize(StreamPDB, Length)) + return EC; + return Error::success(); +} + +Error InfoStreamBuilder::commit(const msf::MSFLayout &Layout, + WritableBinaryStreamRef Buffer) const { + auto InfoS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, StreamPDB, Msf.getAllocator()); + BinaryStreamWriter Writer(*InfoS); + + InfoStreamHeader H; + // Leave the build id fields 0 so they can be set as the last step before + // committing the file to disk. + ::memset(&H, 0, sizeof(H)); + H.Version = Ver; + if (auto EC = Writer.writeObject(H)) + return EC; + + if (auto EC = NamedStreams.commit(Writer)) + return EC; + if (auto EC = Writer.writeInteger(0)) + return EC; + for (auto E : Features) { + if (auto EC = Writer.writeEnum(E)) + return EC; + } + assert(Writer.bytesRemaining() == 0); + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp b/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp new file mode 100644 index 000000000000..3f4101db7b93 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp @@ -0,0 +1,65 @@ +//===- InjectedSourceStream.cpp - PDB Headerblock Stream Access -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" + +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +InjectedSourceStream::InjectedSourceStream( + std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +Error InjectedSourceStream::reload(const PDBStringTable &Strings) { + BinaryStreamReader Reader(*Stream); + + if (auto EC = Reader.readObject(Header)) + return EC; + + if (Header->Version != + static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid headerblock header version"); + + if (auto EC = InjectedSourceTable.load(Reader)) + return EC; + + for (const auto& Entry : *this) { + if (Entry.second.Size != sizeof(SrcHeaderBlockEntry)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid headerbock entry size"); + if (Entry.second.Version != + static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid headerbock entry version"); + + // Check that all name references are valid. + auto Name = Strings.getStringForID(Entry.second.FileNI); + if (!Name) + return Name.takeError(); + auto ObjName = Strings.getStringForID(Entry.second.ObjNI); + if (!ObjName) + return ObjName.takeError(); + auto VName = Strings.getStringForID(Entry.second.VFileNI); + if (!VName) + return VName.takeError(); + } + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp b/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp new file mode 100644 index 000000000000..1445f0bd9e1b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp @@ -0,0 +1,144 @@ +//===- ModuleDebugStream.cpp - PDB Module Info Stream Access --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +ModuleDebugStreamRef::ModuleDebugStreamRef( + const DbiModuleDescriptor &Module, + std::unique_ptr<MappedBlockStream> Stream) + : Mod(Module), Stream(std::move(Stream)) {} + +ModuleDebugStreamRef::~ModuleDebugStreamRef() = default; + +Error ModuleDebugStreamRef::reload() { + BinaryStreamReader Reader(*Stream); + + if (Mod.getModuleStreamIndex() != llvm::pdb::kInvalidStreamIndex) { + if (Error E = reloadSerialize(Reader)) + return E; + } + if (Reader.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Unexpected bytes in module stream."); + return Error::success(); +} + +Error ModuleDebugStreamRef::reloadSerialize(BinaryStreamReader &Reader) { + uint32_t SymbolSize = Mod.getSymbolDebugInfoByteSize(); + uint32_t C11Size = Mod.getC11LineInfoByteSize(); + uint32_t C13Size = Mod.getC13LineInfoByteSize(); + + if (C11Size > 0 && C13Size > 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Module has both C11 and C13 line info"); + + BinaryStreamRef S; + + if (auto EC = Reader.readInteger(Signature)) + return EC; + Reader.setOffset(0); + if (auto EC = Reader.readSubstream(SymbolsSubstream, SymbolSize)) + return EC; + if (auto EC = Reader.readSubstream(C11LinesSubstream, C11Size)) + return EC; + if (auto EC = Reader.readSubstream(C13LinesSubstream, C13Size)) + return EC; + + BinaryStreamReader SymbolReader(SymbolsSubstream.StreamData); + if (auto EC = SymbolReader.readArray( + SymbolArray, SymbolReader.bytesRemaining(), sizeof(uint32_t))) + return EC; + + BinaryStreamReader SubsectionsReader(C13LinesSubstream.StreamData); + if (auto EC = SubsectionsReader.readArray(Subsections, + SubsectionsReader.bytesRemaining())) + return EC; + + uint32_t GlobalRefsSize; + if (auto EC = Reader.readInteger(GlobalRefsSize)) + return EC; + if (auto EC = Reader.readSubstream(GlobalRefsSubstream, GlobalRefsSize)) + return EC; + return Error::success(); +} + +const codeview::CVSymbolArray +ModuleDebugStreamRef::getSymbolArrayForScope(uint32_t ScopeBegin) const { + return limitSymbolArrayToScope(SymbolArray, ScopeBegin); +} + +BinarySubstreamRef ModuleDebugStreamRef::getSymbolsSubstream() const { + return SymbolsSubstream; +} + +BinarySubstreamRef ModuleDebugStreamRef::getC11LinesSubstream() const { + return C11LinesSubstream; +} + +BinarySubstreamRef ModuleDebugStreamRef::getC13LinesSubstream() const { + return C13LinesSubstream; +} + +BinarySubstreamRef ModuleDebugStreamRef::getGlobalRefsSubstream() const { + return GlobalRefsSubstream; +} + +iterator_range<codeview::CVSymbolArray::Iterator> +ModuleDebugStreamRef::symbols(bool *HadError) const { + return make_range(SymbolArray.begin(HadError), SymbolArray.end()); +} + +CVSymbol ModuleDebugStreamRef::readSymbolAtOffset(uint32_t Offset) const { + auto Iter = SymbolArray.at(Offset); + assert(Iter != SymbolArray.end()); + return *Iter; +} + +iterator_range<ModuleDebugStreamRef::DebugSubsectionIterator> +ModuleDebugStreamRef::subsections() const { + return make_range(Subsections.begin(), Subsections.end()); +} + +bool ModuleDebugStreamRef::hasDebugSubsections() const { + return !C13LinesSubstream.empty(); +} + +Error ModuleDebugStreamRef::commit() { return Error::success(); } + +Expected<codeview::DebugChecksumsSubsectionRef> +ModuleDebugStreamRef::findChecksumsSubsection() const { + codeview::DebugChecksumsSubsectionRef Result; + for (const auto &SS : subsections()) { + if (SS.kind() != DebugSubsectionKind::FileChecksums) + continue; + + if (auto EC = Result.initialize(SS.getRecordData())) + return std::move(EC); + return Result; + } + return Result; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp b/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp new file mode 100644 index 000000000000..4a88391494cd --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp @@ -0,0 +1,126 @@ +//===- NamedStreamMap.cpp - PDB Named Stream Map --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <tuple> + +using namespace llvm; +using namespace llvm::pdb; + +NamedStreamMapTraits::NamedStreamMapTraits(NamedStreamMap &NS) : NS(&NS) {} + +uint16_t NamedStreamMapTraits::hashLookupKey(StringRef S) const { + // In the reference implementation, this uses + // HASH Hasher<ULONG*, USHORT*>::hashPbCb(PB pb, size_t cb, ULONG ulMod). + // Here, the type HASH is a typedef of unsigned short. + // ** It is not a bug that we truncate the result of hashStringV1, in fact + // it is a bug if we do not! ** + // See NMTNI::hash() in the reference implementation. + return static_cast<uint16_t>(hashStringV1(S)); +} + +StringRef NamedStreamMapTraits::storageKeyToLookupKey(uint32_t Offset) const { + return NS->getString(Offset); +} + +uint32_t NamedStreamMapTraits::lookupKeyToStorageKey(StringRef S) { + return NS->appendStringData(S); +} + +NamedStreamMap::NamedStreamMap() : HashTraits(*this), OffsetIndexMap(1) {} + +Error NamedStreamMap::load(BinaryStreamReader &Stream) { + uint32_t StringBufferSize; + if (auto EC = Stream.readInteger(StringBufferSize)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected string buffer size")); + + StringRef Buffer; + if (auto EC = Stream.readFixedString(Buffer, StringBufferSize)) + return EC; + NamesBuffer.assign(Buffer.begin(), Buffer.end()); + + return OffsetIndexMap.load(Stream); +} + +Error NamedStreamMap::commit(BinaryStreamWriter &Writer) const { + // The first field is the number of bytes of string data. + if (auto EC = Writer.writeInteger<uint32_t>(NamesBuffer.size())) + return EC; + + // Then the actual string data. + StringRef Data(NamesBuffer.data(), NamesBuffer.size()); + if (auto EC = Writer.writeFixedString(Data)) + return EC; + + // And finally the Offset Index map. + if (auto EC = OffsetIndexMap.commit(Writer)) + return EC; + + return Error::success(); +} + +uint32_t NamedStreamMap::calculateSerializedLength() const { + return sizeof(uint32_t) // String data size + + NamesBuffer.size() // String data + + OffsetIndexMap.calculateSerializedLength(); // Offset Index Map +} + +uint32_t NamedStreamMap::size() const { return OffsetIndexMap.size(); } + +StringRef NamedStreamMap::getString(uint32_t Offset) const { + assert(NamesBuffer.size() > Offset); + return StringRef(NamesBuffer.data() + Offset); +} + +uint32_t NamedStreamMap::hashString(uint32_t Offset) const { + return hashStringV1(getString(Offset)); +} + +bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { + auto Iter = OffsetIndexMap.find_as(Stream, HashTraits); + if (Iter == OffsetIndexMap.end()) + return false; + StreamNo = (*Iter).second; + return true; +} + +StringMap<uint32_t> NamedStreamMap::entries() const { + StringMap<uint32_t> Result; + for (const auto &Entry : OffsetIndexMap) { + StringRef Stream(NamesBuffer.data() + Entry.first); + Result.try_emplace(Stream, Entry.second); + } + return Result; +} + +uint32_t NamedStreamMap::appendStringData(StringRef S) { + uint32_t Offset = NamesBuffer.size(); + NamesBuffer.insert(NamesBuffer.end(), S.begin(), S.end()); + NamesBuffer.push_back('\0'); + return Offset; +} + +void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) { + OffsetIndexMap.set_as(Stream, support::ulittle32_t(StreamNo), HashTraits); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp new file mode 100644 index 000000000000..39ae84acba20 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp @@ -0,0 +1,60 @@ +//===- NativeCompilandSymbol.cpp - Native impl for compilands ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" + +#include "llvm/ADT/STLExtras.h" + +namespace llvm { +namespace pdb { + +NativeCompilandSymbol::NativeCompilandSymbol(NativeSession &Session, + SymIndexId SymbolId, + DbiModuleDescriptor MI) + : NativeRawSymbol(Session, PDB_SymType::Compiland, SymbolId), Module(MI) {} + +PDB_SymType NativeCompilandSymbol::getSymTag() const { + return PDB_SymType::Compiland; +} + +void NativeCompilandSymbol::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "libraryName", getLibraryName(), Indent); + dumpSymbolField(OS, "name", getName(), Indent); + dumpSymbolField(OS, "editAndContinueEnabled", isEditAndContinueEnabled(), + Indent); +} + +bool NativeCompilandSymbol::isEditAndContinueEnabled() const { + return Module.hasECInfo(); +} + +SymIndexId NativeCompilandSymbol::getLexicalParentId() const { return 0; } + +// The usage of getObjFileName for getLibraryName and getModuleName for getName +// may seem backwards, but it is consistent with DIA, which is what this API +// was modeled after. We may rename these methods later to try to eliminate +// this potential confusion. + +std::string NativeCompilandSymbol::getLibraryName() const { + return Module.getObjFileName(); +} + +std::string NativeCompilandSymbol::getName() const { + return Module.getModuleName(); +} + +} // namespace pdb +} // namespace llvm diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumGlobals.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumGlobals.cpp new file mode 100644 index 000000000000..54646867bc5f --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumGlobals.cpp @@ -0,0 +1,54 @@ +//==- NativeEnumGlobals.cpp - Native Global Enumerator impl ------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumGlobals.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeEnumGlobals::NativeEnumGlobals(NativeSession &PDBSession, + std::vector<codeview::SymbolKind> Kinds) + : Index(0), Session(PDBSession) { + GlobalsStream &GS = cantFail(Session.getPDBFile().getPDBGlobalsStream()); + SymbolStream &SS = cantFail(Session.getPDBFile().getPDBSymbolStream()); + for (uint32_t Off : GS.getGlobalsTable()) { + CVSymbol S = SS.readRecord(Off); + if (!llvm::is_contained(Kinds, S.kind())) + continue; + MatchOffsets.push_back(Off); + } +} + +uint32_t NativeEnumGlobals::getChildCount() const { + return static_cast<uint32_t>(MatchOffsets.size()); +} + +std::unique_ptr<PDBSymbol> +NativeEnumGlobals::getChildAtIndex(uint32_t N) const { + if (N >= MatchOffsets.size()) + return nullptr; + + SymIndexId Id = + Session.getSymbolCache().getOrCreateGlobalSymbolByOffset(MatchOffsets[N]); + return Session.getSymbolCache().getSymbolById(Id); +} + +std::unique_ptr<PDBSymbol> NativeEnumGlobals::getNext() { + return getChildAtIndex(Index++); +} + +void NativeEnumGlobals::reset() { Index = 0; } diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp new file mode 100644 index 000000000000..2f6a5bc3d574 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp @@ -0,0 +1,121 @@ +//==- NativeEnumInjectedSources.cpp - Native Injected Source Enumerator --*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" + +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" + +namespace llvm { +namespace pdb { + +namespace { + +Expected<std::string> readStreamData(BinaryStream &Stream, uint32_t Limit) { + uint32_t Offset = 0, DataLength = std::min(Limit, Stream.getLength()); + std::string Result; + Result.reserve(DataLength); + while (Offset < DataLength) { + ArrayRef<uint8_t> Data; + if (auto E = Stream.readLongestContiguousChunk(Offset, Data)) + return std::move(E); + Data = Data.take_front(DataLength - Offset); + Offset += Data.size(); + Result += toStringRef(Data); + } + return Result; +} + +class NativeInjectedSource final : public IPDBInjectedSource { + const SrcHeaderBlockEntry &Entry; + const PDBStringTable &Strings; + PDBFile &File; + +public: + NativeInjectedSource(const SrcHeaderBlockEntry &Entry, + PDBFile &File, const PDBStringTable &Strings) + : Entry(Entry), Strings(Strings), File(File) {} + + uint32_t getCrc32() const override { return Entry.CRC; } + uint64_t getCodeByteSize() const override { return Entry.FileSize; } + + std::string getFileName() const override { + StringRef Ret = cantFail(Strings.getStringForID(Entry.FileNI), + "InjectedSourceStream should have rejected this"); + return Ret; + } + + std::string getObjectFileName() const override { + StringRef Ret = cantFail(Strings.getStringForID(Entry.ObjNI), + "InjectedSourceStream should have rejected this"); + return Ret; + } + + std::string getVirtualFileName() const override { + StringRef Ret = cantFail(Strings.getStringForID(Entry.VFileNI), + "InjectedSourceStream should have rejected this"); + return Ret; + } + + uint32_t getCompression() const override { return Entry.Compression; } + + std::string getCode() const override { + // Get name of stream storing the data. + StringRef VName = + cantFail(Strings.getStringForID(Entry.VFileNI), + "InjectedSourceStream should have rejected this"); + std::string StreamName = ("/src/files/" + VName).str(); + + // Find stream with that name and read its data. + // FIXME: Consider validating (or even loading) all this in + // InjectedSourceStream so that no error can happen here. + auto ExpectedFileStream = File.safelyCreateNamedStream(StreamName); + if (!ExpectedFileStream) { + consumeError(ExpectedFileStream.takeError()); + return "(failed to open data stream)"; + } + + auto Data = readStreamData(**ExpectedFileStream, Entry.FileSize); + if (!Data) { + consumeError(Data.takeError()); + return "(failed to read data)"; + } + return *Data; + } +}; + +} // namespace + +NativeEnumInjectedSources::NativeEnumInjectedSources( + PDBFile &File, const InjectedSourceStream &IJS, + const PDBStringTable &Strings) + : File(File), Stream(IJS), Strings(Strings), Cur(Stream.begin()) {} + +uint32_t NativeEnumInjectedSources::getChildCount() const { + return static_cast<uint32_t>(Stream.size()); +} + +std::unique_ptr<IPDBInjectedSource> +NativeEnumInjectedSources::getChildAtIndex(uint32_t N) const { + if (N >= getChildCount()) + return nullptr; + return std::make_unique<NativeInjectedSource>(std::next(Stream.begin(), N)->second, + File, Strings); +} + +std::unique_ptr<IPDBInjectedSource> NativeEnumInjectedSources::getNext() { + if (Cur == Stream.end()) + return nullptr; + return std::make_unique<NativeInjectedSource>((Cur++)->second, File, Strings); +} + +void NativeEnumInjectedSources::reset() { Cur = Stream.begin(); } + +} +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp new file mode 100644 index 000000000000..c6621924b516 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp @@ -0,0 +1,43 @@ +//==- NativeEnumModules.cpp - Native Symbol Enumerator impl ------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" + +namespace llvm { +namespace pdb { + +NativeEnumModules::NativeEnumModules(NativeSession &PDBSession, uint32_t Index) + : Session(PDBSession), Index(Index) {} + +uint32_t NativeEnumModules::getChildCount() const { + return Session.getSymbolCache().getNumCompilands(); +} + +std::unique_ptr<PDBSymbol> +NativeEnumModules::getChildAtIndex(uint32_t N) const { + return Session.getSymbolCache().getOrCreateCompiland(N); +} + +std::unique_ptr<PDBSymbol> NativeEnumModules::getNext() { + if (Index >= getChildCount()) + return nullptr; + return getChildAtIndex(Index++); +} + +void NativeEnumModules::reset() { Index = 0; } + +} +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp new file mode 100644 index 000000000000..ac217df1ee48 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeEnumTypes.cpp @@ -0,0 +1,70 @@ +//==- NativeEnumTypes.cpp - Native Type Enumerator impl ----------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession, + LazyRandomTypeCollection &Types, + std::vector<codeview::TypeLeafKind> Kinds) + : Matches(), Index(0), Session(PDBSession) { + Optional<TypeIndex> TI = Types.getFirst(); + while (TI) { + CVType CVT = Types.getType(*TI); + TypeLeafKind K = CVT.kind(); + if (llvm::is_contained(Kinds, K)) { + // Don't add forward refs, we'll find those later while enumerating. + if (!isUdtForwardRef(CVT)) + Matches.push_back(*TI); + } else if (K == TypeLeafKind::LF_MODIFIER) { + TypeIndex ModifiedTI = getModifiedType(CVT); + if (!ModifiedTI.isSimple()) { + CVType UnmodifiedCVT = Types.getType(ModifiedTI); + // LF_MODIFIERs point to forward refs, but don't worry about that + // here. We're pushing the TypeIndex of the LF_MODIFIER itself, + // so we'll worry about resolving forward refs later. + if (llvm::is_contained(Kinds, UnmodifiedCVT.kind())) + Matches.push_back(*TI); + } + } + TI = Types.getNext(*TI); + } +} + +NativeEnumTypes::NativeEnumTypes(NativeSession &PDBSession, + std::vector<codeview::TypeIndex> Indices) + : Matches(std::move(Indices)), Index(0), Session(PDBSession) {} + +uint32_t NativeEnumTypes::getChildCount() const { + return static_cast<uint32_t>(Matches.size()); +} + +std::unique_ptr<PDBSymbol> NativeEnumTypes::getChildAtIndex(uint32_t N) const { + if (N < Matches.size()) { + SymIndexId Id = Session.getSymbolCache().findSymbolByTypeIndex(Matches[N]); + return Session.getSymbolCache().getSymbolById(Id); + } + return nullptr; +} + +std::unique_ptr<PDBSymbol> NativeEnumTypes::getNext() { + return getChildAtIndex(Index++); +} + +void NativeEnumTypes::reset() { Index = 0; } diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp new file mode 100644 index 000000000000..3f393409129b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp @@ -0,0 +1,101 @@ +//===- NativeExeSymbol.cpp - native impl for PDBSymbolExe -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" + +using namespace llvm; +using namespace llvm::pdb; + +static DbiStream *getDbiStreamPtr(NativeSession &Session) { + Expected<DbiStream &> DbiS = Session.getPDBFile().getPDBDbiStream(); + if (DbiS) + return &DbiS.get(); + + consumeError(DbiS.takeError()); + return nullptr; +} + +NativeExeSymbol::NativeExeSymbol(NativeSession &Session, SymIndexId SymbolId) + : NativeRawSymbol(Session, PDB_SymType::Exe, SymbolId), + Dbi(getDbiStreamPtr(Session)) {} + +std::unique_ptr<IPDBEnumSymbols> +NativeExeSymbol::findChildren(PDB_SymType Type) const { + switch (Type) { + case PDB_SymType::Compiland: { + return std::unique_ptr<IPDBEnumSymbols>(new NativeEnumModules(Session)); + break; + } + case PDB_SymType::ArrayType: + return Session.getSymbolCache().createTypeEnumerator(codeview::LF_ARRAY); + case PDB_SymType::Enum: + return Session.getSymbolCache().createTypeEnumerator(codeview::LF_ENUM); + case PDB_SymType::PointerType: + return Session.getSymbolCache().createTypeEnumerator(codeview::LF_POINTER); + case PDB_SymType::UDT: + return Session.getSymbolCache().createTypeEnumerator( + {codeview::LF_STRUCTURE, codeview::LF_CLASS, codeview::LF_UNION, + codeview::LF_INTERFACE}); + case PDB_SymType::VTableShape: + return Session.getSymbolCache().createTypeEnumerator(codeview::LF_VTSHAPE); + case PDB_SymType::FunctionSig: + return Session.getSymbolCache().createTypeEnumerator( + {codeview::LF_PROCEDURE, codeview::LF_MFUNCTION}); + case PDB_SymType::Typedef: + return Session.getSymbolCache().createGlobalsEnumerator(codeview::S_UDT); + + default: + break; + } + return nullptr; +} + +uint32_t NativeExeSymbol::getAge() const { + auto IS = Session.getPDBFile().getPDBInfoStream(); + if (IS) + return IS->getAge(); + consumeError(IS.takeError()); + return 0; +} + +std::string NativeExeSymbol::getSymbolsFileName() const { + return Session.getPDBFile().getFilePath(); +} + +codeview::GUID NativeExeSymbol::getGuid() const { + auto IS = Session.getPDBFile().getPDBInfoStream(); + if (IS) + return IS->getGuid(); + consumeError(IS.takeError()); + return codeview::GUID{{0}}; +} + +bool NativeExeSymbol::hasCTypes() const { + auto Dbi = Session.getPDBFile().getPDBDbiStream(); + if (Dbi) + return Dbi->hasCTypes(); + consumeError(Dbi.takeError()); + return false; +} + +bool NativeExeSymbol::hasPrivateSymbols() const { + auto Dbi = Session.getPDBFile().getPDBDbiStream(); + if (Dbi) + return !Dbi->isStripped(); + consumeError(Dbi.takeError()); + return false; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp new file mode 100644 index 000000000000..2ad552470b61 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp @@ -0,0 +1,734 @@ +//===- NativeRawSymbol.cpp - Native implementation of IPDBRawSymbol -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +NativeRawSymbol::NativeRawSymbol(NativeSession &PDBSession, PDB_SymType Tag, + SymIndexId SymbolId) + : Session(PDBSession), Tag(Tag), SymbolId(SymbolId) {} + +void NativeRawSymbol::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + dumpSymbolIdField(OS, "symIndexId", SymbolId, Indent, Session, + PdbSymbolIdField::SymIndexId, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "symTag", Tag, Indent); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildren(PDB_SymType Type) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildren(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByAddr(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint32_t Section, uint32_t Offset) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByVA(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint64_t VA) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByRVA(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint32_t RVA) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByAddr(uint32_t Section, + uint32_t Offset) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByRVA(uint32_t RVA) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByVA(uint64_t VA) const { + return std::make_unique<NullEnumerator<PDBSymbol>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLines() const { + return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLinesByAddr(uint32_t Section, uint32_t Offset, + uint32_t Length) const { + return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const { + return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeRawSymbol::findInlineeLinesByVA(uint64_t VA, uint32_t Length) const { + return std::make_unique<NullEnumerator<IPDBLineNumber>>(); +} + +void NativeRawSymbol::getDataBytes(SmallVector<uint8_t, 32> &bytes) const { + bytes.clear(); +} + +PDB_MemberAccess NativeRawSymbol::getAccess() const { + return PDB_MemberAccess::Private; +} + +uint32_t NativeRawSymbol::getAddressOffset() const { + return 0; +} + +uint32_t NativeRawSymbol::getAddressSection() const { + return 0; +} + +uint32_t NativeRawSymbol::getAge() const { + return 0; +} + +SymIndexId NativeRawSymbol::getArrayIndexTypeId() const { return 0; } + +void NativeRawSymbol::getBackEndVersion(VersionInfo &Version) const { + Version.Major = 0; + Version.Minor = 0; + Version.Build = 0; + Version.QFE = 0; +} + +uint32_t NativeRawSymbol::getBaseDataOffset() const { + return 0; +} + +uint32_t NativeRawSymbol::getBaseDataSlot() const { + return 0; +} + +SymIndexId NativeRawSymbol::getBaseSymbolId() const { return 0; } + +PDB_BuiltinType NativeRawSymbol::getBuiltinType() const { + return PDB_BuiltinType::None; +} + +uint32_t NativeRawSymbol::getBitPosition() const { + return 0; +} + +PDB_CallingConv NativeRawSymbol::getCallingConvention() const { + return PDB_CallingConv::FarStdCall; +} + +SymIndexId NativeRawSymbol::getClassParentId() const { return 0; } + +std::string NativeRawSymbol::getCompilerName() const { + return {}; +} + +uint32_t NativeRawSymbol::getCount() const { + return 0; +} + +uint32_t NativeRawSymbol::getCountLiveRanges() const { + return 0; +} + +void NativeRawSymbol::getFrontEndVersion(VersionInfo &Version) const { + Version.Major = 0; + Version.Minor = 0; + Version.Build = 0; + Version.QFE = 0; +} + +PDB_Lang NativeRawSymbol::getLanguage() const { + return PDB_Lang::Cobol; +} + +SymIndexId NativeRawSymbol::getLexicalParentId() const { return 0; } + +std::string NativeRawSymbol::getLibraryName() const { + return {}; +} + +uint32_t NativeRawSymbol::getLiveRangeStartAddressOffset() const { + return 0; +} + +uint32_t NativeRawSymbol::getLiveRangeStartAddressSection() const { + return 0; +} + +uint32_t NativeRawSymbol::getLiveRangeStartRelativeVirtualAddress() const { + return 0; +} + +codeview::RegisterId NativeRawSymbol::getLocalBasePointerRegisterId() const { + return codeview::RegisterId::EAX; +} + +SymIndexId NativeRawSymbol::getLowerBoundId() const { return 0; } + +uint32_t NativeRawSymbol::getMemorySpaceKind() const { + return 0; +} + +std::string NativeRawSymbol::getName() const { + return {}; +} + +uint32_t NativeRawSymbol::getNumberOfAcceleratorPointerTags() const { + return 0; +} + +uint32_t NativeRawSymbol::getNumberOfColumns() const { + return 0; +} + +uint32_t NativeRawSymbol::getNumberOfModifiers() const { + return 0; +} + +uint32_t NativeRawSymbol::getNumberOfRegisterIndices() const { + return 0; +} + +uint32_t NativeRawSymbol::getNumberOfRows() const { + return 0; +} + +std::string NativeRawSymbol::getObjectFileName() const { + return {}; +} + +uint32_t NativeRawSymbol::getOemId() const { + return 0; +} + +SymIndexId NativeRawSymbol::getOemSymbolId() const { return 0; } + +uint32_t NativeRawSymbol::getOffsetInUdt() const { + return 0; +} + +PDB_Cpu NativeRawSymbol::getPlatform() const { + return PDB_Cpu::Intel8080; +} + +uint32_t NativeRawSymbol::getRank() const { + return 0; +} + +codeview::RegisterId NativeRawSymbol::getRegisterId() const { + return codeview::RegisterId::EAX; +} + +uint32_t NativeRawSymbol::getRegisterType() const { + return 0; +} + +uint32_t NativeRawSymbol::getRelativeVirtualAddress() const { + return 0; +} + +uint32_t NativeRawSymbol::getSamplerSlot() const { + return 0; +} + +uint32_t NativeRawSymbol::getSignature() const { + return 0; +} + +uint32_t NativeRawSymbol::getSizeInUdt() const { + return 0; +} + +uint32_t NativeRawSymbol::getSlot() const { + return 0; +} + +std::string NativeRawSymbol::getSourceFileName() const { + return {}; +} + +std::unique_ptr<IPDBLineNumber> +NativeRawSymbol::getSrcLineOnTypeDefn() const { + return nullptr; +} + +uint32_t NativeRawSymbol::getStride() const { + return 0; +} + +SymIndexId NativeRawSymbol::getSubTypeId() const { return 0; } + +std::string NativeRawSymbol::getSymbolsFileName() const { return {}; } + +SymIndexId NativeRawSymbol::getSymIndexId() const { return SymbolId; } + +uint32_t NativeRawSymbol::getTargetOffset() const { + return 0; +} + +uint32_t NativeRawSymbol::getTargetRelativeVirtualAddress() const { + return 0; +} + +uint64_t NativeRawSymbol::getTargetVirtualAddress() const { + return 0; +} + +uint32_t NativeRawSymbol::getTargetSection() const { + return 0; +} + +uint32_t NativeRawSymbol::getTextureSlot() const { + return 0; +} + +uint32_t NativeRawSymbol::getTimeStamp() const { + return 0; +} + +uint32_t NativeRawSymbol::getToken() const { + return 0; +} + +SymIndexId NativeRawSymbol::getTypeId() const { return 0; } + +uint32_t NativeRawSymbol::getUavSlot() const { + return 0; +} + +std::string NativeRawSymbol::getUndecoratedName() const { + return {}; +} + +std::string NativeRawSymbol::getUndecoratedNameEx( + PDB_UndnameFlags Flags) const { + return {}; +} + +SymIndexId NativeRawSymbol::getUnmodifiedTypeId() const { return 0; } + +SymIndexId NativeRawSymbol::getUpperBoundId() const { return 0; } + +Variant NativeRawSymbol::getValue() const { + return Variant(); +} + +uint32_t NativeRawSymbol::getVirtualBaseDispIndex() const { + return 0; +} + +uint32_t NativeRawSymbol::getVirtualBaseOffset() const { + return 0; +} + +SymIndexId NativeRawSymbol::getVirtualTableShapeId() const { return 0; } + +std::unique_ptr<PDBSymbolTypeBuiltin> +NativeRawSymbol::getVirtualBaseTableType() const { + return nullptr; +} + +PDB_DataKind NativeRawSymbol::getDataKind() const { + return PDB_DataKind::Unknown; +} + +PDB_SymType NativeRawSymbol::getSymTag() const { return Tag; } + +codeview::GUID NativeRawSymbol::getGuid() const { return codeview::GUID{{0}}; } + +int32_t NativeRawSymbol::getOffset() const { + return 0; +} + +int32_t NativeRawSymbol::getThisAdjust() const { + return 0; +} + +int32_t NativeRawSymbol::getVirtualBasePointerOffset() const { + return 0; +} + +PDB_LocType NativeRawSymbol::getLocationType() const { + return PDB_LocType::Null; +} + +PDB_Machine NativeRawSymbol::getMachineType() const { + return PDB_Machine::Invalid; +} + +codeview::ThunkOrdinal NativeRawSymbol::getThunkOrdinal() const { + return codeview::ThunkOrdinal::Standard; +} + +uint64_t NativeRawSymbol::getLength() const { + return 0; +} + +uint64_t NativeRawSymbol::getLiveRangeLength() const { + return 0; +} + +uint64_t NativeRawSymbol::getVirtualAddress() const { + return 0; +} + +PDB_UdtType NativeRawSymbol::getUdtKind() const { + return PDB_UdtType::Struct; +} + +bool NativeRawSymbol::hasConstructor() const { + return false; +} + +bool NativeRawSymbol::hasCustomCallingConvention() const { + return false; +} + +bool NativeRawSymbol::hasFarReturn() const { + return false; +} + +bool NativeRawSymbol::isCode() const { + return false; +} + +bool NativeRawSymbol::isCompilerGenerated() const { + return false; +} + +bool NativeRawSymbol::isConstType() const { + return false; +} + +bool NativeRawSymbol::isEditAndContinueEnabled() const { + return false; +} + +bool NativeRawSymbol::isFunction() const { + return false; +} + +bool NativeRawSymbol::getAddressTaken() const { + return false; +} + +bool NativeRawSymbol::getNoStackOrdering() const { + return false; +} + +bool NativeRawSymbol::hasAlloca() const { + return false; +} + +bool NativeRawSymbol::hasAssignmentOperator() const { + return false; +} + +bool NativeRawSymbol::hasCTypes() const { + return false; +} + +bool NativeRawSymbol::hasCastOperator() const { + return false; +} + +bool NativeRawSymbol::hasDebugInfo() const { + return false; +} + +bool NativeRawSymbol::hasEH() const { + return false; +} + +bool NativeRawSymbol::hasEHa() const { + return false; +} + +bool NativeRawSymbol::hasInlAsm() const { + return false; +} + +bool NativeRawSymbol::hasInlineAttribute() const { + return false; +} + +bool NativeRawSymbol::hasInterruptReturn() const { + return false; +} + +bool NativeRawSymbol::hasFramePointer() const { + return false; +} + +bool NativeRawSymbol::hasLongJump() const { + return false; +} + +bool NativeRawSymbol::hasManagedCode() const { + return false; +} + +bool NativeRawSymbol::hasNestedTypes() const { + return false; +} + +bool NativeRawSymbol::hasNoInlineAttribute() const { + return false; +} + +bool NativeRawSymbol::hasNoReturnAttribute() const { + return false; +} + +bool NativeRawSymbol::hasOptimizedCodeDebugInfo() const { + return false; +} + +bool NativeRawSymbol::hasOverloadedOperator() const { + return false; +} + +bool NativeRawSymbol::hasSEH() const { + return false; +} + +bool NativeRawSymbol::hasSecurityChecks() const { + return false; +} + +bool NativeRawSymbol::hasSetJump() const { + return false; +} + +bool NativeRawSymbol::hasStrictGSCheck() const { + return false; +} + +bool NativeRawSymbol::isAcceleratorGroupSharedLocal() const { + return false; +} + +bool NativeRawSymbol::isAcceleratorPointerTagLiveRange() const { + return false; +} + +bool NativeRawSymbol::isAcceleratorStubFunction() const { + return false; +} + +bool NativeRawSymbol::isAggregated() const { + return false; +} + +bool NativeRawSymbol::isIntroVirtualFunction() const { + return false; +} + +bool NativeRawSymbol::isCVTCIL() const { + return false; +} + +bool NativeRawSymbol::isConstructorVirtualBase() const { + return false; +} + +bool NativeRawSymbol::isCxxReturnUdt() const { + return false; +} + +bool NativeRawSymbol::isDataAligned() const { + return false; +} + +bool NativeRawSymbol::isHLSLData() const { + return false; +} + +bool NativeRawSymbol::isHotpatchable() const { + return false; +} + +bool NativeRawSymbol::isIndirectVirtualBaseClass() const { + return false; +} + +bool NativeRawSymbol::isInterfaceUdt() const { + return false; +} + +bool NativeRawSymbol::isIntrinsic() const { + return false; +} + +bool NativeRawSymbol::isLTCG() const { + return false; +} + +bool NativeRawSymbol::isLocationControlFlowDependent() const { + return false; +} + +bool NativeRawSymbol::isMSILNetmodule() const { + return false; +} + +bool NativeRawSymbol::isMatrixRowMajor() const { + return false; +} + +bool NativeRawSymbol::isManagedCode() const { + return false; +} + +bool NativeRawSymbol::isMSILCode() const { + return false; +} + +bool NativeRawSymbol::isMultipleInheritance() const { + return false; +} + +bool NativeRawSymbol::isNaked() const { + return false; +} + +bool NativeRawSymbol::isNested() const { + return false; +} + +bool NativeRawSymbol::isOptimizedAway() const { + return false; +} + +bool NativeRawSymbol::isPacked() const { + return false; +} + +bool NativeRawSymbol::isPointerBasedOnSymbolValue() const { + return false; +} + +bool NativeRawSymbol::isPointerToDataMember() const { + return false; +} + +bool NativeRawSymbol::isPointerToMemberFunction() const { + return false; +} + +bool NativeRawSymbol::isPureVirtual() const { + return false; +} + +bool NativeRawSymbol::isRValueReference() const { + return false; +} + +bool NativeRawSymbol::isRefUdt() const { + return false; +} + +bool NativeRawSymbol::isReference() const { + return false; +} + +bool NativeRawSymbol::isRestrictedType() const { + return false; +} + +bool NativeRawSymbol::isReturnValue() const { + return false; +} + +bool NativeRawSymbol::isSafeBuffers() const { + return false; +} + +bool NativeRawSymbol::isScoped() const { + return false; +} + +bool NativeRawSymbol::isSdl() const { + return false; +} + +bool NativeRawSymbol::isSingleInheritance() const { + return false; +} + +bool NativeRawSymbol::isSplitted() const { + return false; +} + +bool NativeRawSymbol::isStatic() const { + return false; +} + +bool NativeRawSymbol::hasPrivateSymbols() const { + return false; +} + +bool NativeRawSymbol::isUnalignedType() const { + return false; +} + +bool NativeRawSymbol::isUnreached() const { + return false; +} + +bool NativeRawSymbol::isValueUdt() const { + return false; +} + +bool NativeRawSymbol::isVirtual() const { + return false; +} + +bool NativeRawSymbol::isVirtualBaseClass() const { + return false; +} + +bool NativeRawSymbol::isVirtualInheritance() const { + return false; +} + +bool NativeRawSymbol::isVolatileType() const { + return false; +} + +bool NativeRawSymbol::wasInlined() const { + return false; +} + +std::string NativeRawSymbol::getUnused() const { + return {}; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp new file mode 100644 index 000000000000..b45a5881dcb5 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -0,0 +1,227 @@ +//===- NativeSession.cpp - Native implementation of IPDBSession -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolCache.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" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +#include <algorithm> +#include <cassert> +#include <memory> +#include <utility> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +static DbiStream *getDbiStreamPtr(PDBFile &File) { + Expected<DbiStream &> DbiS = File.getPDBDbiStream(); + if (DbiS) + return &DbiS.get(); + + consumeError(DbiS.takeError()); + return nullptr; +} + +NativeSession::NativeSession(std::unique_ptr<PDBFile> PdbFile, + std::unique_ptr<BumpPtrAllocator> Allocator) + : Pdb(std::move(PdbFile)), Allocator(std::move(Allocator)), + Cache(*this, getDbiStreamPtr(*Pdb)) {} + +NativeSession::~NativeSession() = default; + +Error NativeSession::createFromPdb(std::unique_ptr<MemoryBuffer> Buffer, + std::unique_ptr<IPDBSession> &Session) { + StringRef Path = Buffer->getBufferIdentifier(); + auto Stream = std::make_unique<MemoryBufferByteStream>( + std::move(Buffer), llvm::support::little); + + auto Allocator = std::make_unique<BumpPtrAllocator>(); + auto File = std::make_unique<PDBFile>(Path, std::move(Stream), *Allocator); + if (auto EC = File->parseFileHeaders()) + return EC; + if (auto EC = File->parseStreamData()) + return EC; + + Session = + std::make_unique<NativeSession>(std::move(File), std::move(Allocator)); + + return Error::success(); +} + +Error NativeSession::createFromExe(StringRef Path, + std::unique_ptr<IPDBSession> &Session) { + return make_error<RawError>(raw_error_code::feature_unsupported); +} + +uint64_t NativeSession::getLoadAddress() const { return 0; } + +bool NativeSession::setLoadAddress(uint64_t Address) { return false; } + +std::unique_ptr<PDBSymbolExe> NativeSession::getGlobalScope() { + return PDBSymbol::createAs<PDBSymbolExe>(*this, getNativeGlobalScope()); +} + +std::unique_ptr<PDBSymbol> +NativeSession::getSymbolById(SymIndexId SymbolId) const { + return Cache.getSymbolById(SymbolId); +} + +bool NativeSession::addressForVA(uint64_t VA, uint32_t &Section, + uint32_t &Offset) const { + return false; +} + +bool NativeSession::addressForRVA(uint32_t VA, uint32_t &Section, + uint32_t &Offset) const { + return false; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const { + return nullptr; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolByRVA(uint32_t RVA, PDB_SymType Type) const { + return nullptr; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolBySectOffset(uint32_t Sect, uint32_t Offset, + PDB_SymType Type) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbers(const PDBSymbolCompiland &Compiland, + const IPDBSourceFile &File) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbersByAddress(uint64_t Address, + uint32_t Length) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +NativeSession::findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset, + uint32_t Length) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> +NativeSession::findSourceFiles(const PDBSymbolCompiland *Compiland, + StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBSourceFile> +NativeSession::findOneSourceFile(const PDBSymbolCompiland *Compiland, + StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> +NativeSession::findCompilandsForSourceFile(StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<PDBSymbolCompiland> +NativeSession::findOneCompilandForSourceFile(StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> NativeSession::getAllSourceFiles() const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> NativeSession::getSourceFilesForCompiland( + const PDBSymbolCompiland &Compiland) const { + return nullptr; +} + +std::unique_ptr<IPDBSourceFile> +NativeSession::getSourceFileById(uint32_t FileId) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumDataStreams> NativeSession::getDebugStreams() const { + return nullptr; +} + +std::unique_ptr<IPDBEnumTables> NativeSession::getEnumTables() const { + return nullptr; +} + +std::unique_ptr<IPDBEnumInjectedSources> +NativeSession::getInjectedSources() const { + auto ISS = Pdb->getInjectedSourceStream(); + if (!ISS) { + consumeError(ISS.takeError()); + return nullptr; + } + auto Strings = Pdb->getStringTable(); + if (!Strings) { + consumeError(Strings.takeError()); + return nullptr; + } + return std::make_unique<NativeEnumInjectedSources>(*Pdb, *ISS, *Strings); +} + +std::unique_ptr<IPDBEnumSectionContribs> +NativeSession::getSectionContribs() const { + return nullptr; +} + +std::unique_ptr<IPDBEnumFrameData> +NativeSession::getFrameData() const { + return nullptr; +} + +void NativeSession::initializeExeSymbol() { + if (ExeSymbol == 0) + ExeSymbol = Cache.createSymbol<NativeExeSymbol>(); +} + +NativeExeSymbol &NativeSession::getNativeGlobalScope() const { + const_cast<NativeSession &>(*this).initializeExeSymbol(); + + return Cache.getNativeSymbolById<NativeExeSymbol>(ExeSymbol); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSymbolEnumerator.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSymbolEnumerator.cpp new file mode 100644 index 000000000000..704c1254afbf --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeSymbolEnumerator.cpp @@ -0,0 +1,122 @@ +//===- NativeSymbolEnumerator.cpp - info about enumerators ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h" + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeSymbolEnumerator::NativeSymbolEnumerator( + NativeSession &Session, SymIndexId Id, const NativeTypeEnum &Parent, + codeview::EnumeratorRecord Record) + : NativeRawSymbol(Session, PDB_SymType::Data, Id), Parent(Parent), + Record(std::move(Record)) {} + +NativeSymbolEnumerator::~NativeSymbolEnumerator() {} + +void NativeSymbolEnumerator::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + dumpSymbolIdField(OS, "classParentId", getClassParentId(), Indent, Session, + PdbSymbolIdField::ClassParent, ShowIdFields, + RecurseIdFields); + dumpSymbolIdField(OS, "lexicalParentId", getLexicalParentId(), Indent, + Session, PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "name", getName(), Indent); + dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + dumpSymbolField(OS, "dataKind", getDataKind(), Indent); + dumpSymbolField(OS, "locationType", getLocationType(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); + dumpSymbolField(OS, "value", getValue(), Indent); +} + +SymIndexId NativeSymbolEnumerator::getClassParentId() const { + return Parent.getSymIndexId(); +} + +SymIndexId NativeSymbolEnumerator::getLexicalParentId() const { return 0; } + +std::string NativeSymbolEnumerator::getName() const { return Record.Name; } + +SymIndexId NativeSymbolEnumerator::getTypeId() const { + return Parent.getTypeId(); +} + +PDB_DataKind NativeSymbolEnumerator::getDataKind() const { + return PDB_DataKind::Constant; +} + +PDB_LocType NativeSymbolEnumerator::getLocationType() const { + return PDB_LocType::Constant; +} + +bool NativeSymbolEnumerator::isConstType() const { return false; } + +bool NativeSymbolEnumerator::isVolatileType() const { return false; } + +bool NativeSymbolEnumerator::isUnalignedType() const { return false; } + +Variant NativeSymbolEnumerator::getValue() const { + const NativeTypeBuiltin &BT = Parent.getUnderlyingBuiltinType(); + + switch (BT.getBuiltinType()) { + case PDB_BuiltinType::Int: + case PDB_BuiltinType::Long: + case PDB_BuiltinType::Char: { + assert(Record.Value.isSignedIntN(BT.getLength() * 8)); + int64_t N = Record.Value.getSExtValue(); + switch (BT.getLength()) { + case 1: + return Variant{static_cast<int8_t>(N)}; + case 2: + return Variant{static_cast<int16_t>(N)}; + case 4: + return Variant{static_cast<int32_t>(N)}; + case 8: + return Variant{static_cast<int64_t>(N)}; + } + break; + } + case PDB_BuiltinType::UInt: + case PDB_BuiltinType::ULong: { + assert(Record.Value.isIntN(BT.getLength() * 8)); + uint64_t U = Record.Value.getZExtValue(); + switch (BT.getLength()) { + case 1: + return Variant{static_cast<uint8_t>(U)}; + case 2: + return Variant{static_cast<uint16_t>(U)}; + case 4: + return Variant{static_cast<uint32_t>(U)}; + case 8: + return Variant{static_cast<uint64_t>(U)}; + } + break; + } + case PDB_BuiltinType::Bool: { + assert(Record.Value.isIntN(BT.getLength() * 8)); + uint64_t U = Record.Value.getZExtValue(); + return Variant{static_cast<bool>(U)}; + } + default: + assert(false && "Invalid enumeration type"); + break; + } + + return Variant{Record.Value.getSExtValue()}; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeArray.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeArray.cpp new file mode 100644 index 000000000000..80d455ad66e9 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeArray.cpp @@ -0,0 +1,66 @@ +//===- NativeTypeArray.cpp - info about arrays ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeArray.h" + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeArray::NativeTypeArray(NativeSession &Session, SymIndexId Id, + codeview::TypeIndex TI, + codeview::ArrayRecord Record) + : NativeRawSymbol(Session, PDB_SymType::ArrayType, Id), Record(Record), + Index(TI) {} +NativeTypeArray::~NativeTypeArray() {} + +void NativeTypeArray::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolField(OS, "arrayIndexTypeId", getArrayIndexTypeId(), Indent); + dumpSymbolIdField(OS, "elementTypeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "length", getLength(), Indent); + dumpSymbolField(OS, "count", getCount(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +SymIndexId NativeTypeArray::getArrayIndexTypeId() const { + return Session.getSymbolCache().findSymbolByTypeIndex(Record.getIndexType()); +} + +bool NativeTypeArray::isConstType() const { return false; } + +bool NativeTypeArray::isUnalignedType() const { return false; } + +bool NativeTypeArray::isVolatileType() const { return false; } + +uint32_t NativeTypeArray::getCount() const { + NativeRawSymbol &Element = + Session.getSymbolCache().getNativeSymbolById(getTypeId()); + return getLength() / Element.getLength(); +} + +SymIndexId NativeTypeArray::getTypeId() const { + return Session.getSymbolCache().findSymbolByTypeIndex( + Record.getElementType()); +} + +uint64_t NativeTypeArray::getLength() const { return Record.Size; }
\ No newline at end of file diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeBuiltin.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeBuiltin.cpp new file mode 100644 index 000000000000..a08663aa91ba --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeBuiltin.cpp @@ -0,0 +1,46 @@ +//===- NativeTypeBuiltin.cpp -------------------------------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeBuiltin::NativeTypeBuiltin(NativeSession &PDBSession, SymIndexId Id, + ModifierOptions Mods, PDB_BuiltinType T, + uint64_t L) + : NativeRawSymbol(PDBSession, PDB_SymType::BuiltinType, Id), + Session(PDBSession), Mods(Mods), Type(T), Length(L) {} + +NativeTypeBuiltin::~NativeTypeBuiltin() {} + +void NativeTypeBuiltin::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const {} + +PDB_SymType NativeTypeBuiltin::getSymTag() const { + return PDB_SymType::BuiltinType; +} + +PDB_BuiltinType NativeTypeBuiltin::getBuiltinType() const { return Type; } + +bool NativeTypeBuiltin::isConstType() const { + return (Mods & ModifierOptions::Const) != ModifierOptions::None; +} + +uint64_t NativeTypeBuiltin::getLength() const { return Length; } + +bool NativeTypeBuiltin::isUnalignedType() const { + return (Mods & ModifierOptions::Unaligned) != ModifierOptions::None; +} + +bool NativeTypeBuiltin::isVolatileType() const { + return (Mods & ModifierOptions::Volatile) != ModifierOptions::None; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp new file mode 100644 index 000000000000..26ccb7daece0 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeEnum.cpp @@ -0,0 +1,381 @@ +//===- NativeTypeEnum.cpp - info about enum type ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/Native/NativeSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +#include "llvm/Support/FormatVariadic.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +// Yea, this is a pretty terrible class name. But if we have an enum: +// +// enum Foo { +// A, +// B +// }; +// +// then A and B are the "enumerators" of the "enum" Foo. And we need +// to enumerate them. +class NativeEnumEnumEnumerators : public IPDBEnumSymbols, TypeVisitorCallbacks { +public: + NativeEnumEnumEnumerators(NativeSession &Session, + const NativeTypeEnum &ClassParent); + + uint32_t getChildCount() const override; + std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override; + std::unique_ptr<PDBSymbol> getNext() override; + void reset() override; + +private: + Error visitKnownMember(CVMemberRecord &CVM, + EnumeratorRecord &Record) override; + Error visitKnownMember(CVMemberRecord &CVM, + ListContinuationRecord &Record) override; + + NativeSession &Session; + const NativeTypeEnum &ClassParent; + std::vector<EnumeratorRecord> Enumerators; + Optional<TypeIndex> ContinuationIndex; + uint32_t Index = 0; +}; +} // namespace + +NativeEnumEnumEnumerators::NativeEnumEnumEnumerators( + NativeSession &Session, const NativeTypeEnum &ClassParent) + : Session(Session), ClassParent(ClassParent) { + TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream()); + LazyRandomTypeCollection &Types = Tpi.typeCollection(); + + ContinuationIndex = ClassParent.getEnumRecord().FieldList; + while (ContinuationIndex) { + CVType FieldList = Types.getType(*ContinuationIndex); + assert(FieldList.kind() == LF_FIELDLIST); + ContinuationIndex.reset(); + cantFail(visitMemberRecordStream(FieldList.data(), *this)); + } +} + +Error NativeEnumEnumEnumerators::visitKnownMember(CVMemberRecord &CVM, + EnumeratorRecord &Record) { + Enumerators.push_back(Record); + return Error::success(); +} + +Error NativeEnumEnumEnumerators::visitKnownMember( + CVMemberRecord &CVM, ListContinuationRecord &Record) { + ContinuationIndex = Record.ContinuationIndex; + return Error::success(); +} + +uint32_t NativeEnumEnumEnumerators::getChildCount() const { + return Enumerators.size(); +} + +std::unique_ptr<PDBSymbol> +NativeEnumEnumEnumerators::getChildAtIndex(uint32_t Index) const { + if (Index >= getChildCount()) + return nullptr; + + SymIndexId Id = Session.getSymbolCache() + .getOrCreateFieldListMember<NativeSymbolEnumerator>( + ClassParent.getEnumRecord().FieldList, Index, + ClassParent, Enumerators[Index]); + return Session.getSymbolCache().getSymbolById(Id); +} + +std::unique_ptr<PDBSymbol> NativeEnumEnumEnumerators::getNext() { + if (Index >= getChildCount()) + return nullptr; + + return getChildAtIndex(Index++); +} + +void NativeEnumEnumEnumerators::reset() { Index = 0; } + +NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id, + TypeIndex Index, EnumRecord Record) + : NativeRawSymbol(Session, PDB_SymType::Enum, Id), Index(Index), + Record(std::move(Record)) {} + +NativeTypeEnum::NativeTypeEnum(NativeSession &Session, SymIndexId Id, + NativeTypeEnum &UnmodifiedType, + codeview::ModifierRecord Modifier) + : NativeRawSymbol(Session, PDB_SymType::Enum, Id), + UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {} + +NativeTypeEnum::~NativeTypeEnum() {} + +void NativeTypeEnum::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolField(OS, "baseType", static_cast<uint32_t>(getBuiltinType()), + Indent); + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "name", getName(), Indent); + dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + if (Modifiers.hasValue()) + dumpSymbolIdField(OS, "unmodifiedTypeId", getUnmodifiedTypeId(), Indent, + Session, PdbSymbolIdField::UnmodifiedType, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "length", getLength(), Indent); + dumpSymbolField(OS, "constructor", hasConstructor(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "hasAssignmentOperator", hasAssignmentOperator(), Indent); + dumpSymbolField(OS, "hasCastOperator", hasCastOperator(), Indent); + dumpSymbolField(OS, "hasNestedTypes", hasNestedTypes(), Indent); + dumpSymbolField(OS, "overloadedOperator", hasOverloadedOperator(), Indent); + dumpSymbolField(OS, "isInterfaceUdt", isInterfaceUdt(), Indent); + dumpSymbolField(OS, "intrinsic", isIntrinsic(), Indent); + dumpSymbolField(OS, "nested", isNested(), Indent); + dumpSymbolField(OS, "packed", isPacked(), Indent); + dumpSymbolField(OS, "isRefUdt", isRefUdt(), Indent); + dumpSymbolField(OS, "scoped", isScoped(), Indent); + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "isValueUdt", isValueUdt(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeTypeEnum::findChildren(PDB_SymType Type) const { + if (Type != PDB_SymType::Data) + return std::make_unique<NullEnumerator<PDBSymbol>>(); + + const NativeTypeEnum *ClassParent = nullptr; + if (!Modifiers) + ClassParent = this; + else + ClassParent = UnmodifiedType; + return std::make_unique<NativeEnumEnumEnumerators>(Session, *ClassParent); +} + +PDB_SymType NativeTypeEnum::getSymTag() const { return PDB_SymType::Enum; } + +PDB_BuiltinType NativeTypeEnum::getBuiltinType() const { + if (UnmodifiedType) + return UnmodifiedType->getBuiltinType(); + + Session.getSymbolCache().findSymbolByTypeIndex(Record->getUnderlyingType()); + + codeview::TypeIndex Underlying = Record->getUnderlyingType(); + + // This indicates a corrupt record. + if (!Underlying.isSimple() || + Underlying.getSimpleMode() != SimpleTypeMode::Direct) { + return PDB_BuiltinType::None; + } + + switch (Underlying.getSimpleKind()) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean8: + return PDB_BuiltinType::Bool; + case SimpleTypeKind::NarrowCharacter: + case SimpleTypeKind::UnsignedCharacter: + case SimpleTypeKind::SignedCharacter: + return PDB_BuiltinType::Char; + case SimpleTypeKind::WideCharacter: + return PDB_BuiltinType::WCharT; + case SimpleTypeKind::Character16: + return PDB_BuiltinType::Char16; + case SimpleTypeKind::Character32: + return PDB_BuiltinType::Char32; + case SimpleTypeKind::Int128: + case SimpleTypeKind::Int128Oct: + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::Int32: + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return PDB_BuiltinType::Int; + case SimpleTypeKind::UInt128: + case SimpleTypeKind::UInt128Oct: + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + case SimpleTypeKind::UInt32: + case SimpleTypeKind::UInt32Long: + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return PDB_BuiltinType::UInt; + case SimpleTypeKind::HResult: + return PDB_BuiltinType::HResult; + case SimpleTypeKind::Complex16: + case SimpleTypeKind::Complex32: + case SimpleTypeKind::Complex32PartialPrecision: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Complex128: + return PDB_BuiltinType::Complex; + case SimpleTypeKind::Float16: + case SimpleTypeKind::Float32: + case SimpleTypeKind::Float32PartialPrecision: + case SimpleTypeKind::Float48: + case SimpleTypeKind::Float64: + case SimpleTypeKind::Float80: + case SimpleTypeKind::Float128: + return PDB_BuiltinType::Float; + default: + return PDB_BuiltinType::None; + } + llvm_unreachable("Unreachable"); +} + +SymIndexId NativeTypeEnum::getUnmodifiedTypeId() const { + return UnmodifiedType ? UnmodifiedType->getSymIndexId() : 0; +} + +bool NativeTypeEnum::hasConstructor() const { + if (UnmodifiedType) + return UnmodifiedType->hasConstructor(); + + return bool(Record->getOptions() & + codeview::ClassOptions::HasConstructorOrDestructor); +} + +bool NativeTypeEnum::hasAssignmentOperator() const { + if (UnmodifiedType) + return UnmodifiedType->hasAssignmentOperator(); + + return bool(Record->getOptions() & + codeview::ClassOptions::HasOverloadedAssignmentOperator); +} + +bool NativeTypeEnum::hasNestedTypes() const { + if (UnmodifiedType) + return UnmodifiedType->hasNestedTypes(); + + return bool(Record->getOptions() & + codeview::ClassOptions::ContainsNestedClass); +} + +bool NativeTypeEnum::isIntrinsic() const { + if (UnmodifiedType) + return UnmodifiedType->isIntrinsic(); + + return bool(Record->getOptions() & codeview::ClassOptions::Intrinsic); +} + +bool NativeTypeEnum::hasCastOperator() const { + if (UnmodifiedType) + return UnmodifiedType->hasCastOperator(); + + return bool(Record->getOptions() & + codeview::ClassOptions::HasConversionOperator); +} + +uint64_t NativeTypeEnum::getLength() const { + if (UnmodifiedType) + return UnmodifiedType->getLength(); + + const auto Id = Session.getSymbolCache().findSymbolByTypeIndex( + Record->getUnderlyingType()); + const auto UnderlyingType = + Session.getConcreteSymbolById<PDBSymbolTypeBuiltin>(Id); + return UnderlyingType ? UnderlyingType->getLength() : 0; +} + +std::string NativeTypeEnum::getName() const { + if (UnmodifiedType) + return UnmodifiedType->getName(); + + return Record->getName(); +} + +bool NativeTypeEnum::isNested() const { + if (UnmodifiedType) + return UnmodifiedType->isNested(); + + return bool(Record->getOptions() & codeview::ClassOptions::Nested); +} + +bool NativeTypeEnum::hasOverloadedOperator() const { + if (UnmodifiedType) + return UnmodifiedType->hasOverloadedOperator(); + + return bool(Record->getOptions() & + codeview::ClassOptions::HasOverloadedOperator); +} + +bool NativeTypeEnum::isPacked() const { + if (UnmodifiedType) + return UnmodifiedType->isPacked(); + + return bool(Record->getOptions() & codeview::ClassOptions::Packed); +} + +bool NativeTypeEnum::isScoped() const { + if (UnmodifiedType) + return UnmodifiedType->isScoped(); + + return bool(Record->getOptions() & codeview::ClassOptions::Scoped); +} + +SymIndexId NativeTypeEnum::getTypeId() const { + if (UnmodifiedType) + return UnmodifiedType->getTypeId(); + + return Session.getSymbolCache().findSymbolByTypeIndex( + Record->getUnderlyingType()); +} + +bool NativeTypeEnum::isRefUdt() const { return false; } + +bool NativeTypeEnum::isValueUdt() const { return false; } + +bool NativeTypeEnum::isInterfaceUdt() const { return false; } + +bool NativeTypeEnum::isConstType() const { + if (!Modifiers) + return false; + return ((Modifiers->getModifiers() & ModifierOptions::Const) != + ModifierOptions::None); +} + +bool NativeTypeEnum::isVolatileType() const { + if (!Modifiers) + return false; + return ((Modifiers->getModifiers() & ModifierOptions::Volatile) != + ModifierOptions::None); +} + +bool NativeTypeEnum::isUnalignedType() const { + if (!Modifiers) + return false; + return ((Modifiers->getModifiers() & ModifierOptions::Unaligned) != + ModifierOptions::None); +} + +const NativeTypeBuiltin &NativeTypeEnum::getUnderlyingBuiltinType() const { + if (UnmodifiedType) + return UnmodifiedType->getUnderlyingBuiltinType(); + + return Session.getSymbolCache().getNativeSymbolById<NativeTypeBuiltin>( + getTypeId()); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeFunctionSig.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeFunctionSig.cpp new file mode 100644 index 000000000000..f98a4c3043eb --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeFunctionSig.cpp @@ -0,0 +1,199 @@ +//===- NativeTypeFunctionSig.cpp - info about function signature -*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h" + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +// This is kind of a silly class, hence why we keep it private to the file. +// It's only purpose is to wrap the real type record. I guess this is so that +// we can have the lexical parent point to the function instead of the global +// scope. +class NativeTypeFunctionArg : public NativeRawSymbol { +public: + NativeTypeFunctionArg(NativeSession &Session, + std::unique_ptr<PDBSymbol> RealType) + : NativeRawSymbol(Session, PDB_SymType::FunctionArg, 0), + RealType(std::move(RealType)) {} + + void dump(raw_ostream &OS, int Indent, PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const override { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + } + + SymIndexId getTypeId() const override { return RealType->getSymIndexId(); } + + std::unique_ptr<PDBSymbol> RealType; +}; + +class NativeEnumFunctionArgs : public IPDBEnumChildren<PDBSymbol> { +public: + NativeEnumFunctionArgs(NativeSession &Session, + std::unique_ptr<NativeEnumTypes> TypeEnumerator) + : Session(Session), TypeEnumerator(std::move(TypeEnumerator)) {} + + uint32_t getChildCount() const override { + return TypeEnumerator->getChildCount(); + } + std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override { + return wrap(TypeEnumerator->getChildAtIndex(Index)); + } + std::unique_ptr<PDBSymbol> getNext() override { + return wrap(TypeEnumerator->getNext()); + } + + void reset() override { TypeEnumerator->reset(); } + +private: + std::unique_ptr<PDBSymbol> wrap(std::unique_ptr<PDBSymbol> S) const { + if (!S) + return nullptr; + auto NTFA = std::make_unique<NativeTypeFunctionArg>(Session, std::move(S)); + return PDBSymbol::create(Session, std::move(NTFA)); + } + NativeSession &Session; + std::unique_ptr<NativeEnumTypes> TypeEnumerator; +}; +} // namespace + +NativeTypeFunctionSig::NativeTypeFunctionSig(NativeSession &Session, + SymIndexId Id, + codeview::TypeIndex Index, + codeview::ProcedureRecord Proc) + : NativeRawSymbol(Session, PDB_SymType::FunctionSig, Id), + Proc(std::move(Proc)), Index(Index), IsMemberFunction(false) {} + +NativeTypeFunctionSig::NativeTypeFunctionSig( + NativeSession &Session, SymIndexId Id, codeview::TypeIndex Index, + codeview::MemberFunctionRecord MemberFunc) + : NativeRawSymbol(Session, PDB_SymType::FunctionSig, Id), + MemberFunc(std::move(MemberFunc)), Index(Index), IsMemberFunction(true) {} + +void NativeTypeFunctionSig::initialize() { + if (IsMemberFunction) { + ClassParentId = + Session.getSymbolCache().findSymbolByTypeIndex(MemberFunc.ClassType); + initializeArgList(MemberFunc.ArgumentList); + } else { + initializeArgList(Proc.ArgumentList); + } +} + +NativeTypeFunctionSig::~NativeTypeFunctionSig() {} + +void NativeTypeFunctionSig::initializeArgList(codeview::TypeIndex ArgListTI) { + TpiStream &Tpi = cantFail(Session.getPDBFile().getPDBTpiStream()); + CVType CVT = Tpi.typeCollection().getType(ArgListTI); + + cantFail(TypeDeserializer::deserializeAs<ArgListRecord>(CVT, ArgList)); +} + +void NativeTypeFunctionSig::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + + dumpSymbolField(OS, "callingConvention", getCallingConvention(), Indent); + dumpSymbolField(OS, "count", getCount(), Indent); + dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + if (IsMemberFunction) + dumpSymbolField(OS, "thisAdjust", getThisAdjust(), Indent); + dumpSymbolField(OS, "constructor", hasConstructor(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "isConstructorVirtualBase", isConstructorVirtualBase(), + Indent); + dumpSymbolField(OS, "isCxxReturnUdt", isCxxReturnUdt(), Indent); + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +std::unique_ptr<IPDBEnumSymbols> +NativeTypeFunctionSig::findChildren(PDB_SymType Type) const { + if (Type != PDB_SymType::FunctionArg) + return std::make_unique<NullEnumerator<PDBSymbol>>(); + + auto NET = std::make_unique<NativeEnumTypes>(Session, + /* copy */ ArgList.ArgIndices); + return std::unique_ptr<IPDBEnumSymbols>( + new NativeEnumFunctionArgs(Session, std::move(NET))); +} + +SymIndexId NativeTypeFunctionSig::getClassParentId() const { + if (!IsMemberFunction) + return 0; + + return ClassParentId; +} + +PDB_CallingConv NativeTypeFunctionSig::getCallingConvention() const { + return IsMemberFunction ? MemberFunc.CallConv : Proc.CallConv; +} + +uint32_t NativeTypeFunctionSig::getCount() const { + return IsMemberFunction ? (1 + MemberFunc.getParameterCount()) + : Proc.getParameterCount(); +} + +SymIndexId NativeTypeFunctionSig::getTypeId() const { + TypeIndex ReturnTI = + IsMemberFunction ? MemberFunc.getReturnType() : Proc.getReturnType(); + + SymIndexId Result = Session.getSymbolCache().findSymbolByTypeIndex(ReturnTI); + return Result; +} + +int32_t NativeTypeFunctionSig::getThisAdjust() const { + return IsMemberFunction ? MemberFunc.getThisPointerAdjustment() : 0; +} + +bool NativeTypeFunctionSig::hasConstructor() const { + if (!IsMemberFunction) + return false; + + return (MemberFunc.getOptions() & FunctionOptions::Constructor) != + FunctionOptions::None; +} + +bool NativeTypeFunctionSig::isConstType() const { return false; } + +bool NativeTypeFunctionSig::isConstructorVirtualBase() const { + if (!IsMemberFunction) + return false; + + return (MemberFunc.getOptions() & + FunctionOptions::ConstructorWithVirtualBases) != + FunctionOptions::None; +} + +bool NativeTypeFunctionSig::isCxxReturnUdt() const { + FunctionOptions Options = + IsMemberFunction ? MemberFunc.getOptions() : Proc.getOptions(); + return (Options & FunctionOptions::CxxReturnUdt) != FunctionOptions::None; +} + +bool NativeTypeFunctionSig::isUnalignedType() const { return false; } + +bool NativeTypeFunctionSig::isVolatileType() const { return false; } diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypePointer.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypePointer.cpp new file mode 100644 index 000000000000..32dcfc235954 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypePointer.cpp @@ -0,0 +1,193 @@ +//===- NativeTypePointer.cpp - info about pointer type ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypePointer.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypePointer::NativeTypePointer(NativeSession &Session, SymIndexId Id, + codeview::TypeIndex TI) + : NativeRawSymbol(Session, PDB_SymType::PointerType, Id), TI(TI) { + assert(TI.isSimple()); + assert(TI.getSimpleMode() != SimpleTypeMode::Direct); +} + +NativeTypePointer::NativeTypePointer(NativeSession &Session, SymIndexId Id, + codeview::TypeIndex TI, + codeview::PointerRecord Record) + : NativeRawSymbol(Session, PDB_SymType::PointerType, Id), TI(TI), + Record(std::move(Record)) {} + +NativeTypePointer::~NativeTypePointer() {} + +void NativeTypePointer::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + if (isMemberPointer()) { + dumpSymbolIdField(OS, "classParentId", getClassParentId(), Indent, Session, + PdbSymbolIdField::ClassParent, ShowIdFields, + RecurseIdFields); + } + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); + dumpSymbolField(OS, "length", getLength(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "isPointerToDataMember", isPointerToDataMember(), Indent); + dumpSymbolField(OS, "isPointerToMemberFunction", isPointerToMemberFunction(), + Indent); + dumpSymbolField(OS, "RValueReference", isRValueReference(), Indent); + dumpSymbolField(OS, "reference", isReference(), Indent); + dumpSymbolField(OS, "restrictedType", isRestrictedType(), Indent); + if (isMemberPointer()) { + if (isSingleInheritance()) + dumpSymbolField(OS, "isSingleInheritance", 1, Indent); + else if (isMultipleInheritance()) + dumpSymbolField(OS, "isMultipleInheritance", 1, Indent); + else if (isVirtualInheritance()) + dumpSymbolField(OS, "isVirtualInheritance", 1, Indent); + } + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +SymIndexId NativeTypePointer::getClassParentId() const { + if (!isMemberPointer()) + return 0; + + assert(Record); + const MemberPointerInfo &MPI = Record->getMemberInfo(); + return Session.getSymbolCache().findSymbolByTypeIndex(MPI.ContainingType); +} + +uint64_t NativeTypePointer::getLength() const { + if (Record) + return Record->getSize(); + + switch (TI.getSimpleMode()) { + case SimpleTypeMode::NearPointer: + case SimpleTypeMode::FarPointer: + case SimpleTypeMode::HugePointer: + return 2; + case SimpleTypeMode::NearPointer32: + case SimpleTypeMode::FarPointer32: + return 4; + case SimpleTypeMode::NearPointer64: + return 8; + case SimpleTypeMode::NearPointer128: + return 16; + default: + assert(false && "invalid simple type mode!"); + } + return 0; +} + +SymIndexId NativeTypePointer::getTypeId() const { + // This is the pointee SymIndexId. + TypeIndex Referent = Record ? Record->ReferentType : TI.makeDirect(); + + return Session.getSymbolCache().findSymbolByTypeIndex(Referent); +} + +bool NativeTypePointer::isReference() const { + if (!Record) + return false; + return Record->getMode() == PointerMode::LValueReference; +} + +bool NativeTypePointer::isRValueReference() const { + if (!Record) + return false; + return Record->getMode() == PointerMode::RValueReference; +} + +bool NativeTypePointer::isPointerToDataMember() const { + if (!Record) + return false; + return Record->getMode() == PointerMode::PointerToDataMember; +} + +bool NativeTypePointer::isPointerToMemberFunction() const { + if (!Record) + return false; + return Record->getMode() == PointerMode::PointerToMemberFunction; +} + +bool NativeTypePointer::isConstType() const { + if (!Record) + return false; + return (Record->getOptions() & PointerOptions::Const) != PointerOptions::None; +} + +bool NativeTypePointer::isRestrictedType() const { + if (!Record) + return false; + return (Record->getOptions() & PointerOptions::Restrict) != + PointerOptions::None; +} + +bool NativeTypePointer::isVolatileType() const { + if (!Record) + return false; + return (Record->getOptions() & PointerOptions::Volatile) != + PointerOptions::None; +} + +bool NativeTypePointer::isUnalignedType() const { + if (!Record) + return false; + return (Record->getOptions() & PointerOptions::Unaligned) != + PointerOptions::None; +} + +static inline bool isInheritanceKind(const MemberPointerInfo &MPI, + PointerToMemberRepresentation P1, + PointerToMemberRepresentation P2) { + return (MPI.getRepresentation() == P1 || MPI.getRepresentation() == P2); +} + +bool NativeTypePointer::isSingleInheritance() const { + if (!isMemberPointer()) + return false; + return isInheritanceKind( + Record->getMemberInfo(), + PointerToMemberRepresentation::SingleInheritanceData, + PointerToMemberRepresentation::SingleInheritanceFunction); +} + +bool NativeTypePointer::isMultipleInheritance() const { + if (!isMemberPointer()) + return false; + return isInheritanceKind( + Record->getMemberInfo(), + PointerToMemberRepresentation::MultipleInheritanceData, + PointerToMemberRepresentation::MultipleInheritanceFunction); +} + +bool NativeTypePointer::isVirtualInheritance() const { + if (!isMemberPointer()) + return false; + return isInheritanceKind( + Record->getMemberInfo(), + PointerToMemberRepresentation::VirtualInheritanceData, + PointerToMemberRepresentation::VirtualInheritanceFunction); +} + +bool NativeTypePointer::isMemberPointer() const { + return isPointerToDataMember() || isPointerToMemberFunction(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeTypedef.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeTypedef.cpp new file mode 100644 index 000000000000..60b373282267 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeTypedef.cpp @@ -0,0 +1,27 @@ +#include "llvm/DebugInfo/PDB/Native/NativeTypeTypedef.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeTypedef::NativeTypeTypedef(NativeSession &Session, SymIndexId Id, + codeview::UDTSym Typedef) + : NativeRawSymbol(Session, PDB_SymType::Typedef, Id), + Record(std::move(Typedef)) {} + +NativeTypeTypedef::~NativeTypeTypedef() {} + +void NativeTypeTypedef::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + dumpSymbolField(OS, "name", getName(), Indent); + dumpSymbolIdField(OS, "typeId", getTypeId(), Indent, Session, + PdbSymbolIdField::Type, ShowIdFields, RecurseIdFields); +} + +std::string NativeTypeTypedef::getName() const { return Record.Name; } + +SymIndexId NativeTypeTypedef::getTypeId() const { + return Session.getSymbolCache().findSymbolByTypeIndex(Record.Type); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp new file mode 100644 index 000000000000..be67846c0b24 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp @@ -0,0 +1,220 @@ +//===- NativeTypeUDT.cpp - info about class/struct type ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeTypeUDT.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" + +#include <cassert> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id, + codeview::TypeIndex TI, codeview::ClassRecord CR) + : NativeRawSymbol(Session, PDB_SymType::UDT, Id), Index(TI), + Class(std::move(CR)), Tag(Class.getPointer()) {} + +NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id, + codeview::TypeIndex TI, codeview::UnionRecord UR) + : NativeRawSymbol(Session, PDB_SymType::UDT, Id), Index(TI), + Union(std::move(UR)), Tag(Union.getPointer()) {} + +NativeTypeUDT::NativeTypeUDT(NativeSession &Session, SymIndexId Id, + NativeTypeUDT &UnmodifiedType, + codeview::ModifierRecord Modifier) + : NativeRawSymbol(Session, PDB_SymType::UDT, Id), + UnmodifiedType(&UnmodifiedType), Modifiers(std::move(Modifier)) {} + +NativeTypeUDT::~NativeTypeUDT() {} + +void NativeTypeUDT::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolField(OS, "name", getName(), Indent); + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + if (Modifiers.hasValue()) + dumpSymbolIdField(OS, "unmodifiedTypeId", getUnmodifiedTypeId(), Indent, + Session, PdbSymbolIdField::UnmodifiedType, ShowIdFields, + RecurseIdFields); + if (getUdtKind() != PDB_UdtType::Union) + dumpSymbolField(OS, "virtualTableShapeId", getVirtualTableShapeId(), + Indent); + dumpSymbolField(OS, "length", getLength(), Indent); + dumpSymbolField(OS, "udtKind", getUdtKind(), Indent); + dumpSymbolField(OS, "constructor", hasConstructor(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "hasAssignmentOperator", hasAssignmentOperator(), Indent); + dumpSymbolField(OS, "hasCastOperator", hasCastOperator(), Indent); + dumpSymbolField(OS, "hasNestedTypes", hasNestedTypes(), Indent); + dumpSymbolField(OS, "overloadedOperator", hasOverloadedOperator(), Indent); + dumpSymbolField(OS, "isInterfaceUdt", isInterfaceUdt(), Indent); + dumpSymbolField(OS, "intrinsic", isIntrinsic(), Indent); + dumpSymbolField(OS, "nested", isNested(), Indent); + dumpSymbolField(OS, "packed", isPacked(), Indent); + dumpSymbolField(OS, "isRefUdt", isRefUdt(), Indent); + dumpSymbolField(OS, "scoped", isScoped(), Indent); + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "isValueUdt", isValueUdt(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +std::string NativeTypeUDT::getName() const { + if (UnmodifiedType) + return UnmodifiedType->getName(); + + return Tag->getName(); +} + +SymIndexId NativeTypeUDT::getLexicalParentId() const { return 0; } + +SymIndexId NativeTypeUDT::getUnmodifiedTypeId() const { + if (UnmodifiedType) + return UnmodifiedType->getSymIndexId(); + + return 0; +} + +SymIndexId NativeTypeUDT::getVirtualTableShapeId() const { + if (UnmodifiedType) + return UnmodifiedType->getVirtualTableShapeId(); + + if (Class) + return Session.getSymbolCache().findSymbolByTypeIndex(Class->VTableShape); + + return 0; +} + +uint64_t NativeTypeUDT::getLength() const { + if (UnmodifiedType) + return UnmodifiedType->getLength(); + + if (Class) + return Class->getSize(); + + return Union->getSize(); +} + +PDB_UdtType NativeTypeUDT::getUdtKind() const { + if (UnmodifiedType) + return UnmodifiedType->getUdtKind(); + + switch (Tag->Kind) { + case TypeRecordKind::Class: + return PDB_UdtType::Class; + case TypeRecordKind::Union: + return PDB_UdtType::Union; + case TypeRecordKind::Struct: + return PDB_UdtType::Struct; + case TypeRecordKind::Interface: + return PDB_UdtType::Interface; + default: + llvm_unreachable("Unexected udt kind"); + } +} + +bool NativeTypeUDT::hasConstructor() const { + if (UnmodifiedType) + return UnmodifiedType->hasConstructor(); + + return (Tag->Options & ClassOptions::HasConstructorOrDestructor) != + ClassOptions::None; +} + +bool NativeTypeUDT::isConstType() const { + if (!Modifiers) + return false; + return (Modifiers->Modifiers & ModifierOptions::Const) != + ModifierOptions::None; +} + +bool NativeTypeUDT::hasAssignmentOperator() const { + if (UnmodifiedType) + return UnmodifiedType->hasAssignmentOperator(); + + return (Tag->Options & ClassOptions::HasOverloadedAssignmentOperator) != + ClassOptions::None; +} + +bool NativeTypeUDT::hasCastOperator() const { + if (UnmodifiedType) + return UnmodifiedType->hasCastOperator(); + + return (Tag->Options & ClassOptions::HasConversionOperator) != + ClassOptions::None; +} + +bool NativeTypeUDT::hasNestedTypes() const { + if (UnmodifiedType) + return UnmodifiedType->hasNestedTypes(); + + return (Tag->Options & ClassOptions::ContainsNestedClass) != + ClassOptions::None; +} + +bool NativeTypeUDT::hasOverloadedOperator() const { + if (UnmodifiedType) + return UnmodifiedType->hasOverloadedOperator(); + + return (Tag->Options & ClassOptions::HasOverloadedOperator) != + ClassOptions::None; +} + +bool NativeTypeUDT::isInterfaceUdt() const { return false; } + +bool NativeTypeUDT::isIntrinsic() const { + if (UnmodifiedType) + return UnmodifiedType->isIntrinsic(); + + return (Tag->Options & ClassOptions::Intrinsic) != ClassOptions::None; +} + +bool NativeTypeUDT::isNested() const { + if (UnmodifiedType) + return UnmodifiedType->isNested(); + + return (Tag->Options & ClassOptions::Nested) != ClassOptions::None; +} + +bool NativeTypeUDT::isPacked() const { + if (UnmodifiedType) + return UnmodifiedType->isPacked(); + + return (Tag->Options & ClassOptions::Packed) != ClassOptions::None; +} + +bool NativeTypeUDT::isRefUdt() const { return false; } + +bool NativeTypeUDT::isScoped() const { + if (UnmodifiedType) + return UnmodifiedType->isScoped(); + + return (Tag->Options & ClassOptions::Scoped) != ClassOptions::None; +} + +bool NativeTypeUDT::isValueUdt() const { return false; } + +bool NativeTypeUDT::isUnalignedType() const { + if (!Modifiers) + return false; + return (Modifiers->Modifiers & ModifierOptions::Unaligned) != + ModifierOptions::None; +} + +bool NativeTypeUDT::isVolatileType() const { + if (!Modifiers) + return false; + return (Modifiers->Modifiers & ModifierOptions::Volatile) != + ModifierOptions::None; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeVTShape.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeVTShape.cpp new file mode 100644 index 000000000000..837fe19ec88c --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeVTShape.cpp @@ -0,0 +1,35 @@ +#include "llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h" + +using namespace llvm; +using namespace llvm::pdb; + +// Create a pointer record for a non-simple type. +NativeTypeVTShape::NativeTypeVTShape(NativeSession &Session, SymIndexId Id, + codeview::TypeIndex TI, + codeview::VFTableShapeRecord SR) + : NativeRawSymbol(Session, PDB_SymType::VTableShape, Id), TI(TI), + Record(std::move(SR)) {} + +NativeTypeVTShape::~NativeTypeVTShape() {} + +void NativeTypeVTShape::dump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowIdFields, + PdbSymbolIdField RecurseIdFields) const { + NativeRawSymbol::dump(OS, Indent, ShowIdFields, RecurseIdFields); + + dumpSymbolIdField(OS, "lexicalParentId", 0, Indent, Session, + PdbSymbolIdField::LexicalParent, ShowIdFields, + RecurseIdFields); + dumpSymbolField(OS, "count", getCount(), Indent); + dumpSymbolField(OS, "constType", isConstType(), Indent); + dumpSymbolField(OS, "unalignedType", isUnalignedType(), Indent); + dumpSymbolField(OS, "volatileType", isVolatileType(), Indent); +} + +bool NativeTypeVTShape::isConstType() const { return false; } + +bool NativeTypeVTShape::isVolatileType() const { return false; } + +bool NativeTypeVTShape::isUnalignedType() const { return false; } + +uint32_t NativeTypeVTShape::getCount() const { return Record.Slots.size(); } diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp new file mode 100644 index 000000000000..9ac226b89139 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -0,0 +1,507 @@ +//===- PDBFile.cpp - Low level interface to a PDB file ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <cassert> +#include <cstdint> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +typedef FixedStreamArray<support::ulittle32_t> ulittle_array; +} // end anonymous namespace + +PDBFile::PDBFile(StringRef Path, std::unique_ptr<BinaryStream> PdbFileBuffer, + BumpPtrAllocator &Allocator) + : FilePath(Path), Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {} + +PDBFile::~PDBFile() = default; + +StringRef PDBFile::getFilePath() const { return FilePath; } + +StringRef PDBFile::getFileDirectory() const { + return sys::path::parent_path(FilePath); +} + +uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; } + +uint32_t PDBFile::getFreeBlockMapBlock() const { + return ContainerLayout.SB->FreeBlockMapBlock; +} + +uint32_t PDBFile::getBlockCount() const { + return ContainerLayout.SB->NumBlocks; +} + +uint32_t PDBFile::getNumDirectoryBytes() const { + return ContainerLayout.SB->NumDirectoryBytes; +} + +uint32_t PDBFile::getBlockMapIndex() const { + return ContainerLayout.SB->BlockMapAddr; +} + +uint32_t PDBFile::getUnknown1() const { return ContainerLayout.SB->Unknown1; } + +uint32_t PDBFile::getNumDirectoryBlocks() const { + return msf::bytesToBlocks(ContainerLayout.SB->NumDirectoryBytes, + ContainerLayout.SB->BlockSize); +} + +uint64_t PDBFile::getBlockMapOffset() const { + return (uint64_t)ContainerLayout.SB->BlockMapAddr * + ContainerLayout.SB->BlockSize; +} + +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]; +} + +ArrayRef<support::ulittle32_t> +PDBFile::getStreamBlockList(uint32_t StreamIndex) const { + return ContainerLayout.StreamMap[StreamIndex]; +} + +uint32_t PDBFile::getFileSize() const { return Buffer->getLength(); } + +Expected<ArrayRef<uint8_t>> PDBFile::getBlockData(uint32_t BlockIndex, + uint32_t NumBytes) const { + uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize()); + + ArrayRef<uint8_t> Result; + if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result)) + return std::move(EC); + return Result; +} + +Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset, + ArrayRef<uint8_t> Data) const { + return make_error<RawError>(raw_error_code::not_writable, + "PDBFile is immutable"); +} + +Error PDBFile::parseFileHeaders() { + BinaryStreamReader Reader(*Buffer); + + // Initialize SB. + const msf::SuperBlock *SB = nullptr; + if (auto EC = Reader.readObject(SB)) { + consumeError(std::move(EC)); + return make_error<RawError>(raw_error_code::corrupt_file, + "MSF superblock is missing"); + } + + if (auto EC = msf::validateSuperBlock(*SB)) + return EC; + + if (Buffer->getLength() % SB->BlockSize != 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "File size is not a multiple of block size"); + ContainerLayout.SB = SB; + + // Initialize Free Page Map. + ContainerLayout.FreePageMap.resize(SB->NumBlocks); + // The Fpm exists either at block 1 or block 2 of the MSF. However, this + // allows for a maximum of getBlockSize() * 8 blocks bits in the Fpm, and + // thusly an equal number of total blocks in the file. For a block size + // of 4KiB (very common), this would yield 32KiB total blocks in file, for a + // maximum file size of 32KiB * 4KiB = 128MiB. Obviously this won't do, so + // the Fpm is split across the file at `getBlockSize()` intervals. As a + // result, every block whose index is of the form |{1,2} + getBlockSize() * k| + // for any non-negative integer k is an Fpm block. In theory, we only really + // need to reserve blocks of the form |{1,2} + getBlockSize() * 8 * k|, but + // current versions of the MSF format already expect the Fpm to be arranged + // at getBlockSize() intervals, so we have to be compatible. + // See the function fpmPn() for more information: + // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L489 + auto FpmStream = + MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator); + BinaryStreamReader FpmReader(*FpmStream); + ArrayRef<uint8_t> FpmBytes; + if (auto EC = FpmReader.readBytes(FpmBytes, FpmReader.bytesRemaining())) + return EC; + uint32_t BlocksRemaining = getBlockCount(); + uint32_t BI = 0; + for (auto Byte : FpmBytes) { + uint32_t BlocksThisByte = std::min(BlocksRemaining, 8U); + for (uint32_t I = 0; I < BlocksThisByte; ++I) { + if (Byte & (1 << I)) + ContainerLayout.FreePageMap[BI] = true; + --BlocksRemaining; + ++BI; + } + } + + Reader.setOffset(getBlockMapOffset()); + if (auto EC = Reader.readArray(ContainerLayout.DirectoryBlocks, + getNumDirectoryBlocks())) + return EC; + + return Error::success(); +} + +Error PDBFile::parseStreamData() { + assert(ContainerLayout.SB); + if (DirectoryStream) + return Error::success(); + + uint32_t NumStreams = 0; + + // Normally you can't use a MappedBlockStream without having fully parsed the + // PDB file, because it accesses the directory and various other things, which + // is exactly what we are attempting to parse. By specifying a custom + // subclass of IPDBStreamData which only accesses the fields that have already + // been parsed, we can avoid this and reuse MappedBlockStream. + auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer, + Allocator); + BinaryStreamReader Reader(*DS); + if (auto EC = Reader.readInteger(NumStreams)) + return EC; + + if (auto EC = Reader.readArray(ContainerLayout.StreamSizes, NumStreams)) + return EC; + for (uint32_t I = 0; I < NumStreams; ++I) { + uint32_t StreamSize = getStreamByteSize(I); + // FIXME: What does StreamSize ~0U mean? + uint64_t NumExpectedStreamBlocks = + StreamSize == UINT32_MAX + ? 0 + : msf::bytesToBlocks(StreamSize, ContainerLayout.SB->BlockSize); + + // For convenience, we store the block array contiguously. This is because + // if someone calls setStreamMap(), it is more convenient to be able to call + // it with an ArrayRef instead of setting up a StreamRef. Since the + // DirectoryStream is cached in the class and thus lives for the life of the + // class, we can be guaranteed that readArray() will return a stable + // reference, even if it has to allocate from its internal pool. + ArrayRef<support::ulittle32_t> Blocks; + if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks)) + return EC; + for (uint32_t Block : Blocks) { + uint64_t BlockEndOffset = + (uint64_t)(Block + 1) * ContainerLayout.SB->BlockSize; + if (BlockEndOffset > getFileSize()) + return make_error<RawError>(raw_error_code::corrupt_file, + "Stream block map is corrupt."); + } + ContainerLayout.StreamMap.push_back(Blocks); + } + + // We should have read exactly SB->NumDirectoryBytes bytes. + assert(Reader.bytesRemaining() == 0); + DirectoryStream = std::move(DS); + return Error::success(); +} + +ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const { + return ContainerLayout.DirectoryBlocks; +} + +std::unique_ptr<MappedBlockStream> +PDBFile::createIndexedStream(uint16_t SN) const { + 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); + Result.Blocks.assign(Blocks.begin(), Blocks.end()); + Result.Length = getStreamByteSize(StreamIdx); + return Result; +} + +msf::MSFStreamLayout PDBFile::getFpmStreamLayout() const { + return msf::getFpmStreamLayout(ContainerLayout); +} + +Expected<GlobalsStream &> PDBFile::getPDBGlobalsStream() { + if (!Globals) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto GlobalS = + safelyCreateIndexedStream(DbiS->getGlobalSymbolStreamIndex()); + if (!GlobalS) + return GlobalS.takeError(); + auto TempGlobals = std::make_unique<GlobalsStream>(std::move(*GlobalS)); + if (auto EC = TempGlobals->reload()) + return std::move(EC); + Globals = std::move(TempGlobals); + } + return *Globals; +} + +Expected<InfoStream &> PDBFile::getPDBInfoStream() { + if (!Info) { + auto InfoS = safelyCreateIndexedStream(StreamPDB); + if (!InfoS) + return InfoS.takeError(); + auto TempInfo = std::make_unique<InfoStream>(std::move(*InfoS)); + if (auto EC = TempInfo->reload()) + return std::move(EC); + Info = std::move(TempInfo); + } + return *Info; +} + +Expected<DbiStream &> PDBFile::getPDBDbiStream() { + if (!Dbi) { + auto DbiS = safelyCreateIndexedStream(StreamDBI); + if (!DbiS) + return DbiS.takeError(); + auto TempDbi = std::make_unique<DbiStream>(std::move(*DbiS)); + if (auto EC = TempDbi->reload(this)) + return std::move(EC); + Dbi = std::move(TempDbi); + } + return *Dbi; +} + +Expected<TpiStream &> PDBFile::getPDBTpiStream() { + if (!Tpi) { + auto TpiS = safelyCreateIndexedStream(StreamTPI); + if (!TpiS) + return TpiS.takeError(); + auto TempTpi = std::make_unique<TpiStream>(*this, std::move(*TpiS)); + if (auto EC = TempTpi->reload()) + return std::move(EC); + Tpi = std::move(TempTpi); + } + return *Tpi; +} + +Expected<TpiStream &> PDBFile::getPDBIpiStream() { + if (!Ipi) { + if (!hasPDBIpiStream()) + return make_error<RawError>(raw_error_code::no_stream); + + auto IpiS = safelyCreateIndexedStream(StreamIPI); + if (!IpiS) + return IpiS.takeError(); + auto TempIpi = std::make_unique<TpiStream>(*this, std::move(*IpiS)); + if (auto EC = TempIpi->reload()) + return std::move(EC); + Ipi = std::move(TempIpi); + } + return *Ipi; +} + +Expected<PublicsStream &> PDBFile::getPDBPublicsStream() { + if (!Publics) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto PublicS = + safelyCreateIndexedStream(DbiS->getPublicSymbolStreamIndex()); + if (!PublicS) + return PublicS.takeError(); + auto TempPublics = std::make_unique<PublicsStream>(std::move(*PublicS)); + if (auto EC = TempPublics->reload()) + return std::move(EC); + Publics = std::move(TempPublics); + } + return *Publics; +} + +Expected<SymbolStream &> PDBFile::getPDBSymbolStream() { + if (!Symbols) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex(); + auto SymbolS = safelyCreateIndexedStream(SymbolStreamNum); + if (!SymbolS) + return SymbolS.takeError(); + + auto TempSymbols = std::make_unique<SymbolStream>(std::move(*SymbolS)); + if (auto EC = TempSymbols->reload()) + return std::move(EC); + Symbols = std::move(TempSymbols); + } + return *Symbols; +} + +Expected<PDBStringTable &> PDBFile::getStringTable() { + if (!Strings) { + auto NS = safelyCreateNamedStream("/names"); + if (!NS) + return NS.takeError(); + + auto N = std::make_unique<PDBStringTable>(); + BinaryStreamReader Reader(**NS); + if (auto EC = N->reload(Reader)) + return std::move(EC); + assert(Reader.bytesRemaining() == 0); + StringTableStream = std::move(*NS); + Strings = std::move(N); + } + return *Strings; +} + +Expected<InjectedSourceStream &> PDBFile::getInjectedSourceStream() { + if (!InjectedSources) { + auto IJS = safelyCreateNamedStream("/src/headerblock"); + if (!IJS) + return IJS.takeError(); + + auto Strings = getStringTable(); + if (!Strings) + return Strings.takeError(); + + auto IJ = std::make_unique<InjectedSourceStream>(std::move(*IJS)); + if (auto EC = IJ->reload(*Strings)) + return std::move(EC); + InjectedSources = std::move(IJ); + } + return *InjectedSources; +} + +uint32_t PDBFile::getPointerSize() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return 0; + PDB_Machine Machine = DbiS->getMachineType(); + if (Machine == PDB_Machine::Amd64) + return 8; + return 4; +} + +bool PDBFile::hasPDBDbiStream() const { + return StreamDBI < getNumStreams() && getStreamByteSize(StreamDBI) > 0; +} + +bool PDBFile::hasPDBGlobalsStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) { + consumeError(DbiS.takeError()); + return false; + } + + return DbiS->getGlobalSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBInfoStream() const { return StreamPDB < getNumStreams(); } + +bool PDBFile::hasPDBIpiStream() const { + if (!hasPDBInfoStream()) + return false; + + if (StreamIPI >= getNumStreams()) + return false; + + auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream()); + return InfoStream.containsIdStream(); +} + +bool PDBFile::hasPDBPublicsStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) { + consumeError(DbiS.takeError()); + return false; + } + return DbiS->getPublicSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBSymbolStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return false; + return DbiS->getSymRecordStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBTpiStream() const { return StreamTPI < getNumStreams(); } + +bool PDBFile::hasPDBStringTable() { + auto IS = getPDBInfoStream(); + if (!IS) + return false; + Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/names"); + if (!ExpectedNSI) { + consumeError(ExpectedNSI.takeError()); + return false; + } + assert(*ExpectedNSI < getNumStreams()); + return true; +} + +bool PDBFile::hasPDBInjectedSourceStream() { + auto IS = getPDBInfoStream(); + if (!IS) + return false; + Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/src/headerblock"); + if (!ExpectedNSI) { + consumeError(ExpectedNSI.takeError()); + return false; + } + assert(*ExpectedNSI < getNumStreams()); + return true; +} + +/// Wrapper around MappedBlockStream::createIndexedStream() that checks if a +/// stream with that index actually exists. If it does not, the return value +/// will have an MSFError with code msf_error_code::no_stream. Else, the return +/// value will contain the stream returned by createIndexedStream(). +Expected<std::unique_ptr<MappedBlockStream>> +PDBFile::safelyCreateIndexedStream(uint32_t StreamIndex) const { + if (StreamIndex >= getNumStreams()) + // This rejects kInvalidStreamIndex with an error as well. + return make_error<RawError>(raw_error_code::no_stream); + return createIndexedStream(StreamIndex); +} + +Expected<std::unique_ptr<MappedBlockStream>> +PDBFile::safelyCreateNamedStream(StringRef Name) { + auto IS = getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex(Name); + if (!ExpectedNSI) + return ExpectedNSI.takeError(); + uint32_t NameStreamIndex = *ExpectedNSI; + + return safelyCreateIndexedStream(NameStreamIndex); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp new file mode 100644 index 000000000000..aa3288724390 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -0,0 +1,357 @@ +//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.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/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/xxhash.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator) + : Allocator(Allocator), InjectedSourceHashTraits(Strings), + InjectedSourceTable(2) {} + +PDBFileBuilder::~PDBFileBuilder() {} + +Error PDBFileBuilder::initialize(uint32_t BlockSize) { + auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize); + if (!ExpectedMsf) + return ExpectedMsf.takeError(); + Msf = std::make_unique<MSFBuilder>(std::move(*ExpectedMsf)); + return Error::success(); +} + +MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } + +InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { + if (!Info) + Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams); + return *Info; +} + +DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { + if (!Dbi) + Dbi = std::make_unique<DbiStreamBuilder>(*Msf); + return *Dbi; +} + +TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() { + if (!Tpi) + Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI); + return *Tpi; +} + +TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() { + if (!Ipi) + Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI); + return *Ipi; +} + +PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() { + return Strings; +} + +GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() { + if (!Gsi) + Gsi = std::make_unique<GSIStreamBuilder>(*Msf); + return *Gsi; +} + +Expected<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name, + uint32_t Size) { + auto ExpectedStream = Msf->addStream(Size); + if (ExpectedStream) + NamedStreams.set(Name, *ExpectedStream); + return ExpectedStream; +} + +Error PDBFileBuilder::addNamedStream(StringRef Name, StringRef Data) { + Expected<uint32_t> ExpectedIndex = allocateNamedStream(Name, Data.size()); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + assert(NamedStreamData.count(*ExpectedIndex) == 0); + NamedStreamData[*ExpectedIndex] = Data; + return Error::success(); +} + +void PDBFileBuilder::addInjectedSource(StringRef Name, + std::unique_ptr<MemoryBuffer> Buffer) { + // Stream names must be exact matches, since they get looked up in a hash + // table and the hash value is dependent on the exact contents of the string. + // link.exe lowercases a path and converts / to \, so we must do the same. + SmallString<64> VName; + sys::path::native(Name.lower(), VName); + + uint32_t NI = getStringTableBuilder().insert(Name); + uint32_t VNI = getStringTableBuilder().insert(VName); + + InjectedSourceDescriptor Desc; + Desc.Content = std::move(Buffer); + Desc.NameIndex = NI; + Desc.VNameIndex = VNI; + Desc.StreamName = "/src/files/"; + + Desc.StreamName += VName; + + InjectedSources.push_back(std::move(Desc)); +} + +Error PDBFileBuilder::finalizeMsfLayout() { + + if (Ipi && Ipi->getRecordCount() > 0) { + // In theory newer PDBs always have an ID stream, but by saying that we're + // only going to *really* have an ID stream if there is at least one ID + // record, we leave open the opportunity to test older PDBs such as those + // that don't have an ID stream. + auto &Info = getInfoBuilder(); + Info.addFeature(PdbRaw_FeatureSig::VC140); + } + + uint32_t StringsLen = Strings.calculateSerializedSize(); + + Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0); + if (!SN) + return SN.takeError(); + + if (Gsi) { + if (auto EC = Gsi->finalizeMsfLayout()) + return EC; + if (Dbi) { + Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex()); + Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex()); + Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIdx()); + } + } + if (Tpi) { + if (auto EC = Tpi->finalizeMsfLayout()) + return EC; + } + if (Dbi) { + if (auto EC = Dbi->finalizeMsfLayout()) + return EC; + } + SN = allocateNamedStream("/names", StringsLen); + if (!SN) + return SN.takeError(); + + if (Ipi) { + if (auto EC = Ipi->finalizeMsfLayout()) + return EC; + } + + // Do this last, since it relies on the named stream map being complete, and + // that can be updated by previous steps in the finalization. + if (Info) { + if (auto EC = Info->finalizeMsfLayout()) + return EC; + } + + if (!InjectedSources.empty()) { + for (const auto &IS : InjectedSources) { + JamCRC CRC(0); + CRC.update(arrayRefFromStringRef(IS.Content->getBuffer())); + + SrcHeaderBlockEntry Entry; + ::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry)); + Entry.Size = sizeof(SrcHeaderBlockEntry); + Entry.FileSize = IS.Content->getBufferSize(); + Entry.FileNI = IS.NameIndex; + Entry.VFileNI = IS.VNameIndex; + Entry.ObjNI = 1; + Entry.IsVirtual = 0; + Entry.Version = + static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne); + Entry.CRC = CRC.getCRC(); + StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex); + InjectedSourceTable.set_as(VName, std::move(Entry), + InjectedSourceHashTraits); + } + + uint32_t SrcHeaderBlockSize = + sizeof(SrcHeaderBlockHeader) + + InjectedSourceTable.calculateSerializedLength(); + SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize); + if (!SN) + return SN.takeError(); + for (const auto &IS : InjectedSources) { + SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize()); + if (!SN) + return SN.takeError(); + } + } + + // Do this last, since it relies on the named stream map being complete, and + // that can be updated by previous steps in the finalization. + if (Info) { + if (auto EC = Info->finalizeMsfLayout()) + return EC; + } + + return Error::success(); +} + +Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const { + uint32_t SN = 0; + if (!NamedStreams.get(Name, SN)) + return llvm::make_error<pdb::RawError>(raw_error_code::no_stream); + return SN; +} + +void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer, + const msf::MSFLayout &Layout) { + assert(!InjectedSourceTable.empty()); + + uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock")); + auto Stream = WritableMappedBlockStream::createIndexedStream( + Layout, MsfBuffer, SN, Allocator); + BinaryStreamWriter Writer(*Stream); + + SrcHeaderBlockHeader Header; + ::memset(&Header, 0, sizeof(Header)); + Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne); + Header.Size = Writer.bytesRemaining(); + + cantFail(Writer.writeObject(Header)); + cantFail(InjectedSourceTable.commit(Writer)); + + assert(Writer.bytesRemaining() == 0); +} + +void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer, + const msf::MSFLayout &Layout) { + if (InjectedSourceTable.empty()) + return; + + commitSrcHeaderBlock(MsfBuffer, Layout); + + for (const auto &IS : InjectedSources) { + uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName)); + + auto SourceStream = WritableMappedBlockStream::createIndexedStream( + Layout, MsfBuffer, SN, Allocator); + BinaryStreamWriter SourceWriter(*SourceStream); + assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize()); + cantFail(SourceWriter.writeBytes( + arrayRefFromStringRef(IS.Content->getBuffer()))); + } +} + +Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) { + assert(!Filename.empty()); + if (auto EC = finalizeMsfLayout()) + return EC; + + MSFLayout Layout; + Expected<FileBufferByteStream> ExpectedMsfBuffer = + Msf->commit(Filename, Layout); + if (!ExpectedMsfBuffer) + return ExpectedMsfBuffer.takeError(); + FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer); + + auto ExpectedSN = getNamedStreamIndex("/names"); + if (!ExpectedSN) + return ExpectedSN.takeError(); + + auto NS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, *ExpectedSN, Allocator); + BinaryStreamWriter NSWriter(*NS); + if (auto EC = Strings.commit(NSWriter)) + return EC; + + for (const auto &NSE : NamedStreamData) { + if (NSE.second.empty()) + continue; + + auto NS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, NSE.first, Allocator); + BinaryStreamWriter NSW(*NS); + if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second))) + return EC; + } + + if (Info) { + if (auto EC = Info->commit(Layout, Buffer)) + return EC; + } + + if (Dbi) { + if (auto EC = Dbi->commit(Layout, Buffer)) + return EC; + } + + if (Tpi) { + if (auto EC = Tpi->commit(Layout, Buffer)) + return EC; + } + + if (Ipi) { + if (auto EC = Ipi->commit(Layout, Buffer)) + return EC; + } + + if (Gsi) { + if (auto EC = Gsi->commit(Layout, Buffer)) + return EC; + } + + auto InfoStreamBlocks = Layout.StreamMap[StreamPDB]; + assert(!InfoStreamBlocks.empty()); + uint64_t InfoStreamFileOffset = + blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize); + InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>( + Buffer.getBufferStart() + InfoStreamFileOffset); + + commitInjectedSources(Buffer, Layout); + + // Set the build id at the very end, after every other byte of the PDB + // has been written. + if (Info->hashPDBContentsToGUID()) { + // Compute a hash of all sections of the output file. + uint64_t Digest = + xxHash64({Buffer.getBufferStart(), Buffer.getBufferEnd()}); + + H->Age = 1; + + memcpy(H->Guid.Guid, &Digest, 8); + // xxhash only gives us 8 bytes, so put some fixed data in the other half. + memcpy(H->Guid.Guid + 8, "LLD PDB.", 8); + + // Put the hash in the Signature field too. + H->Signature = static_cast<uint32_t>(Digest); + + // Return GUID to caller. + memcpy(Guid, H->Guid.Guid, 16); + } else { + H->Age = Info->getAge(); + H->Guid = Info->getGuid(); + Optional<uint32_t> Sig = Info->getSignature(); + H->Signature = Sig.hasValue() ? *Sig : time(nullptr); + } + + return Buffer.commit(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp new file mode 100644 index 000000000000..2be1656e06bb --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp @@ -0,0 +1,140 @@ +//===- PDBStringTable.cpp - PDB String Table ---------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace llvm::pdb; + +uint32_t PDBStringTable::getByteSize() const { return Header->ByteSize; } +uint32_t PDBStringTable::getNameCount() const { return NameCount; } +uint32_t PDBStringTable::getHashVersion() const { return Header->HashVersion; } +uint32_t PDBStringTable::getSignature() const { return Header->Signature; } + +Error PDBStringTable::readHeader(BinaryStreamReader &Reader) { + if (auto EC = Reader.readObject(Header)) + return EC; + + if (Header->Signature != PDBStringTableSignature) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid hash table signature"); + if (Header->HashVersion != 1 && Header->HashVersion != 2) + return make_error<RawError>(raw_error_code::corrupt_file, + "Unsupported hash version"); + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} + +Error PDBStringTable::readStrings(BinaryStreamReader &Reader) { + BinaryStreamRef Stream; + if (auto EC = Reader.readStreamRef(Stream)) + return EC; + + if (auto EC = Strings.initialize(Stream)) { + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Invalid hash table byte length")); + } + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} + +const codeview::DebugStringTableSubsectionRef & +PDBStringTable::getStringTable() const { + return Strings; +} + +Error PDBStringTable::readHashTable(BinaryStreamReader &Reader) { + const support::ulittle32_t *HashCount; + if (auto EC = Reader.readObject(HashCount)) + return EC; + + if (auto EC = Reader.readArray(IDs, *HashCount)) { + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read bucket array")); + } + + return Error::success(); +} + +Error PDBStringTable::readEpilogue(BinaryStreamReader &Reader) { + if (auto EC = Reader.readInteger(NameCount)) + return EC; + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} + +Error PDBStringTable::reload(BinaryStreamReader &Reader) { + + BinaryStreamReader SectionReader; + + std::tie(SectionReader, Reader) = Reader.split(sizeof(PDBStringTableHeader)); + if (auto EC = readHeader(SectionReader)) + return EC; + + std::tie(SectionReader, Reader) = Reader.split(Header->ByteSize); + if (auto EC = readStrings(SectionReader)) + return EC; + + // We don't know how long the hash table is until we parse it, so let the + // function responsible for doing that figure it out. + if (auto EC = readHashTable(Reader)) + return EC; + + std::tie(SectionReader, Reader) = Reader.split(sizeof(uint32_t)); + if (auto EC = readEpilogue(SectionReader)) + return EC; + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} + +Expected<StringRef> PDBStringTable::getStringForID(uint32_t ID) const { + return Strings.getString(ID); +} + +Expected<uint32_t> PDBStringTable::getIDForString(StringRef Str) const { + uint32_t Hash = + (Header->HashVersion == 1) ? hashStringV1(Str) : hashStringV2(Str); + size_t Count = IDs.size(); + uint32_t Start = Hash % Count; + for (size_t I = 0; I < Count; ++I) { + // The hash is just a starting point for the search, but if it + // doesn't work we should find the string no matter what, because + // we iterate the entire array. + uint32_t Index = (Start + I) % Count; + + // If we find 0, it means the item isn't in the hash table. + uint32_t ID = IDs[Index]; + if (ID == 0) + return make_error<RawError>(raw_error_code::no_entry); + auto ExpectedStr = getStringForID(ID); + if (!ExpectedStr) + return ExpectedStr.takeError(); + + if (*ExpectedStr == Str) + return ID; + } + return make_error<RawError>(raw_error_code::no_entry); +} + +FixedStreamArray<support::ulittle32_t> PDBStringTable::name_ids() const { + return IDs; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp new file mode 100644 index 000000000000..f7f36901e4d4 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp @@ -0,0 +1,229 @@ +//===- PDBStringTableBuilder.cpp - PDB String Table -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" + +#include <map> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::support::endian; +using namespace llvm::pdb; + +StringTableHashTraits::StringTableHashTraits(PDBStringTableBuilder &Table) + : Table(&Table) {} + +uint32_t StringTableHashTraits::hashLookupKey(StringRef S) const { + // The reference implementation doesn't include code for /src/headerblock + // handling, but it can only read natvis entries lld's PDB files if + // this hash function truncates the hash to 16 bit. + // PDB/include/misc.h in the reference implementation has a hashSz() function + // that returns an unsigned short, that seems what's being used for + // /src/headerblock. + return static_cast<uint16_t>(Table->getIdForString(S)); +} + +StringRef StringTableHashTraits::storageKeyToLookupKey(uint32_t Offset) const { + return Table->getStringForId(Offset); +} + +uint32_t StringTableHashTraits::lookupKeyToStorageKey(StringRef S) { + return Table->insert(S); +} + +uint32_t PDBStringTableBuilder::insert(StringRef S) { + return Strings.insert(S); +} + +uint32_t PDBStringTableBuilder::getIdForString(StringRef S) const { + return Strings.getIdForString(S); +} + +StringRef PDBStringTableBuilder::getStringForId(uint32_t Id) const { + return Strings.getStringForId(Id); +} + +static uint32_t computeBucketCount(uint32_t NumStrings) { + // This is a precomputed list of Buckets given the specified number of + // strings. Matching the reference algorithm exactly is not strictly + // necessary for correctness, but it helps when comparing LLD's PDBs with + // Microsoft's PDBs so as to eliminate superfluous differences. + // The reference implementation does (in nmt.h, NMT::grow()): + // unsigned StringCount = 0; + // unsigned BucketCount = 1; + // fn insert() { + // ++StringCount; + // if (BucketCount * 3 / 4 < StringCount) + // BucketCount = BucketCount * 3 / 2 + 1; + // } + // This list contains all StringCount, BucketCount pairs where BucketCount was + // just incremented. It ends before the first BucketCount entry where + // BucketCount * 3 would overflow a 32-bit unsigned int. + static std::map<uint32_t, uint32_t> StringsToBuckets = { + {0, 1}, + {1, 2}, + {2, 4}, + {4, 7}, + {6, 11}, + {9, 17}, + {13, 26}, + {20, 40}, + {31, 61}, + {46, 92}, + {70, 139}, + {105, 209}, + {157, 314}, + {236, 472}, + {355, 709}, + {532, 1064}, + {799, 1597}, + {1198, 2396}, + {1798, 3595}, + {2697, 5393}, + {4045, 8090}, + {6068, 12136}, + {9103, 18205}, + {13654, 27308}, + {20482, 40963}, + {30723, 61445}, + {46084, 92168}, + {69127, 138253}, + {103690, 207380}, + {155536, 311071}, + {233304, 466607}, + {349956, 699911}, + {524934, 1049867}, + {787401, 1574801}, + {1181101, 2362202}, + {1771652, 3543304}, + {2657479, 5314957}, + {3986218, 7972436}, + {5979328, 11958655}, + {8968992, 17937983}, + {13453488, 26906975}, + {20180232, 40360463}, + {30270348, 60540695}, + {45405522, 90811043}, + {68108283, 136216565}, + {102162424, 204324848}, + {153243637, 306487273}, + {229865455, 459730910}, + {344798183, 689596366}, + {517197275, 1034394550}, + {775795913, 1551591826}, + {1163693870, 2327387740}}; + auto Entry = StringsToBuckets.lower_bound(NumStrings); + assert(Entry != StringsToBuckets.end()); + return Entry->second; +} + +uint32_t PDBStringTableBuilder::calculateHashTableSize() const { + uint32_t Size = sizeof(uint32_t); // Hash table begins with 4-byte size field. + Size += sizeof(uint32_t) * computeBucketCount(Strings.size()); + + return Size; +} + +uint32_t PDBStringTableBuilder::calculateSerializedSize() const { + uint32_t Size = 0; + Size += sizeof(PDBStringTableHeader); + Size += Strings.calculateSerializedSize(); + Size += calculateHashTableSize(); + Size += sizeof(uint32_t); // The /names stream ends with the string count. + return Size; +} + +void PDBStringTableBuilder::setStrings( + const codeview::DebugStringTableSubsection &Strings) { + this->Strings = Strings; +} + +Error PDBStringTableBuilder::writeHeader(BinaryStreamWriter &Writer) const { + // Write a header + PDBStringTableHeader H; + H.Signature = PDBStringTableSignature; + H.HashVersion = 1; + H.ByteSize = Strings.calculateSerializedSize(); + if (auto EC = Writer.writeObject(H)) + return EC; + assert(Writer.bytesRemaining() == 0); + return Error::success(); +} + +Error PDBStringTableBuilder::writeStrings(BinaryStreamWriter &Writer) const { + if (auto EC = Strings.commit(Writer)) + return EC; + + assert(Writer.bytesRemaining() == 0); + return Error::success(); +} + +Error PDBStringTableBuilder::writeHashTable(BinaryStreamWriter &Writer) const { + // Write a hash table. + uint32_t BucketCount = computeBucketCount(Strings.size()); + if (auto EC = Writer.writeInteger(BucketCount)) + return EC; + std::vector<ulittle32_t> Buckets(BucketCount); + + for (auto &Pair : Strings) { + StringRef S = Pair.getKey(); + uint32_t Offset = Pair.getValue(); + uint32_t Hash = hashStringV1(S); + + for (uint32_t I = 0; I != BucketCount; ++I) { + uint32_t Slot = (Hash + I) % BucketCount; + if (Buckets[Slot] != 0) + continue; + Buckets[Slot] = Offset; + break; + } + } + + if (auto EC = Writer.writeArray(ArrayRef<ulittle32_t>(Buckets))) + return EC; + + assert(Writer.bytesRemaining() == 0); + return Error::success(); +} + +Error PDBStringTableBuilder::writeEpilogue(BinaryStreamWriter &Writer) const { + if (auto EC = Writer.writeInteger<uint32_t>(Strings.size())) + return EC; + assert(Writer.bytesRemaining() == 0); + return Error::success(); +} + +Error PDBStringTableBuilder::commit(BinaryStreamWriter &Writer) const { + BinaryStreamWriter SectionWriter; + + std::tie(SectionWriter, Writer) = Writer.split(sizeof(PDBStringTableHeader)); + if (auto EC = writeHeader(SectionWriter)) + return EC; + + std::tie(SectionWriter, Writer) = + Writer.split(Strings.calculateSerializedSize()); + if (auto EC = writeStrings(SectionWriter)) + return EC; + + std::tie(SectionWriter, Writer) = Writer.split(calculateHashTableSize()); + if (auto EC = writeHashTable(SectionWriter)) + return EC; + + std::tie(SectionWriter, Writer) = Writer.split(sizeof(uint32_t)); + if (auto EC = writeEpilogue(SectionWriter)) + return EC; + + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp b/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp new file mode 100644 index 000000000000..a33bf03bf8fb --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp @@ -0,0 +1,101 @@ +//===- PublicsStream.cpp - PDB Public Symbol Stream -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +PublicsStream::PublicsStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +PublicsStream::~PublicsStream() = default; + +uint32_t PublicsStream::getSymHash() const { return Header->SymHash; } +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 +// stream so that we verify the stream is at least not corrupted. However, +// we skip over the hash table which we believe contains information about +// public symbols. +Error PublicsStream::reload() { + BinaryStreamReader Reader(*Stream); + + // Check stream size. + if (Reader.bytesRemaining() < + sizeof(PublicsStreamHeader) + sizeof(GSIHashHeader)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + // Read PSGSIHDR struct. + if (Reader.readObject(Header)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + // 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); + if (auto EC = Reader.readArray(AddressMap, NumAddressMapEntries)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read an address map.")); + + // Something called "thunk map" follows. + if (auto EC = Reader.readArray(ThunkMap, Header->NumThunks)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read a thunk map.")); + + // Something called "section map" follows. + if (Reader.bytesRemaining() > 0) { + if (auto EC = Reader.readArray(SectionOffsets, Header->NumSections)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read a section map.")); + } + + if (Reader.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupted publics stream."); + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/RawError.cpp b/llvm/lib/DebugInfo/PDB/Native/RawError.cpp new file mode 100644 index 000000000000..ed6cf0839675 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/RawError.cpp @@ -0,0 +1,53 @@ +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class RawErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.pdb.raw"; } + std::string message(int Condition) const override { + switch (static_cast<raw_error_code>(Condition)) { + case raw_error_code::unspecified: + return "An unknown error has occurred."; + case raw_error_code::feature_unsupported: + return "The feature is unsupported by the implementation."; + case raw_error_code::invalid_format: + return "The record is in an unexpected format."; + case raw_error_code::corrupt_file: + return "The PDB file is corrupt."; + case raw_error_code::insufficient_buffer: + return "The buffer is not large enough to read the requested number of " + "bytes."; + case raw_error_code::no_stream: + return "The specified stream could not be loaded."; + case raw_error_code::index_out_of_bounds: + return "The specified item does not exist in the array."; + case raw_error_code::invalid_block_address: + return "The specified block address is not valid."; + case raw_error_code::duplicate_entry: + return "The entry already exists."; + case raw_error_code::no_entry: + return "The entry does not exist."; + case raw_error_code::not_writable: + return "The PDB does not support writing."; + case raw_error_code::stream_too_long: + return "The stream was longer than expected."; + case raw_error_code::invalid_tpi_hash: + return "The Type record has an invalid hash value."; + } + llvm_unreachable("Unrecognized raw_error_code"); + } +}; +} // namespace + +static llvm::ManagedStatic<RawErrorCategory> RawCategory; +const std::error_category &llvm::pdb::RawErrCategory() { return *RawCategory; } + +char RawError::ID; diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp new file mode 100644 index 000000000000..5cdd628312fe --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp @@ -0,0 +1,299 @@ +#include "llvm/DebugInfo/PDB/Native/SymbolCache.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumGlobals.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" +#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeArray.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeEnum.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypePointer.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeTypedef.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeUDT.h" +#include "llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// Maps codeview::SimpleTypeKind of a built-in type to the parameters necessary +// to instantiate a NativeBuiltinSymbol for that type. +static const struct BuiltinTypeEntry { + codeview::SimpleTypeKind Kind; + PDB_BuiltinType Type; + uint32_t Size; +} BuiltinTypes[] = { + {codeview::SimpleTypeKind::None, PDB_BuiltinType::None, 0}, + {codeview::SimpleTypeKind::Void, PDB_BuiltinType::Void, 0}, + {codeview::SimpleTypeKind::HResult, PDB_BuiltinType::HResult, 4}, + {codeview::SimpleTypeKind::Int16Short, PDB_BuiltinType::Int, 2}, + {codeview::SimpleTypeKind::UInt16Short, PDB_BuiltinType::UInt, 2}, + {codeview::SimpleTypeKind::Int32, PDB_BuiltinType::Int, 4}, + {codeview::SimpleTypeKind::UInt32, PDB_BuiltinType::UInt, 4}, + {codeview::SimpleTypeKind::Int32Long, PDB_BuiltinType::Int, 4}, + {codeview::SimpleTypeKind::UInt32Long, PDB_BuiltinType::UInt, 4}, + {codeview::SimpleTypeKind::Int64Quad, PDB_BuiltinType::Int, 8}, + {codeview::SimpleTypeKind::UInt64Quad, PDB_BuiltinType::UInt, 8}, + {codeview::SimpleTypeKind::NarrowCharacter, PDB_BuiltinType::Char, 1}, + {codeview::SimpleTypeKind::WideCharacter, PDB_BuiltinType::WCharT, 2}, + {codeview::SimpleTypeKind::Character16, PDB_BuiltinType::Char16, 2}, + {codeview::SimpleTypeKind::Character32, PDB_BuiltinType::Char32, 4}, + {codeview::SimpleTypeKind::SignedCharacter, PDB_BuiltinType::Char, 1}, + {codeview::SimpleTypeKind::UnsignedCharacter, PDB_BuiltinType::UInt, 1}, + {codeview::SimpleTypeKind::Float32, PDB_BuiltinType::Float, 4}, + {codeview::SimpleTypeKind::Float64, PDB_BuiltinType::Float, 8}, + {codeview::SimpleTypeKind::Float80, PDB_BuiltinType::Float, 10}, + {codeview::SimpleTypeKind::Boolean8, PDB_BuiltinType::Bool, 1}, + // This table can be grown as necessary, but these are the only types we've + // needed so far. +}; + +SymbolCache::SymbolCache(NativeSession &Session, DbiStream *Dbi) + : Session(Session), Dbi(Dbi) { + // Id 0 is reserved for the invalid symbol. + Cache.push_back(nullptr); + + if (Dbi) + Compilands.resize(Dbi->modules().getModuleCount()); +} + +std::unique_ptr<IPDBEnumSymbols> +SymbolCache::createTypeEnumerator(TypeLeafKind Kind) { + return createTypeEnumerator(std::vector<TypeLeafKind>{Kind}); +} + +std::unique_ptr<IPDBEnumSymbols> +SymbolCache::createTypeEnumerator(std::vector<TypeLeafKind> Kinds) { + auto Tpi = Session.getPDBFile().getPDBTpiStream(); + if (!Tpi) { + consumeError(Tpi.takeError()); + return nullptr; + } + auto &Types = Tpi->typeCollection(); + return std::unique_ptr<IPDBEnumSymbols>( + new NativeEnumTypes(Session, Types, std::move(Kinds))); +} + +std::unique_ptr<IPDBEnumSymbols> +SymbolCache::createGlobalsEnumerator(codeview::SymbolKind Kind) { + return std::unique_ptr<IPDBEnumSymbols>( + new NativeEnumGlobals(Session, {Kind})); +} + +SymIndexId SymbolCache::createSimpleType(TypeIndex Index, + ModifierOptions Mods) { + if (Index.getSimpleMode() != codeview::SimpleTypeMode::Direct) + return createSymbol<NativeTypePointer>(Index); + + const auto Kind = Index.getSimpleKind(); + const auto It = std::find_if( + std::begin(BuiltinTypes), std::end(BuiltinTypes), + [Kind](const BuiltinTypeEntry &Builtin) { return Builtin.Kind == Kind; }); + if (It == std::end(BuiltinTypes)) + return 0; + return createSymbol<NativeTypeBuiltin>(Mods, It->Type, It->Size); +} + +SymIndexId +SymbolCache::createSymbolForModifiedType(codeview::TypeIndex ModifierTI, + codeview::CVType CVT) { + ModifierRecord Record; + if (auto EC = TypeDeserializer::deserializeAs<ModifierRecord>(CVT, Record)) { + consumeError(std::move(EC)); + return 0; + } + + if (Record.ModifiedType.isSimple()) + return createSimpleType(Record.ModifiedType, Record.Modifiers); + + // Make sure we create and cache a record for the unmodified type. + SymIndexId UnmodifiedId = findSymbolByTypeIndex(Record.ModifiedType); + NativeRawSymbol &UnmodifiedNRS = *Cache[UnmodifiedId]; + + switch (UnmodifiedNRS.getSymTag()) { + case PDB_SymType::Enum: + return createSymbol<NativeTypeEnum>( + static_cast<NativeTypeEnum &>(UnmodifiedNRS), std::move(Record)); + case PDB_SymType::UDT: + return createSymbol<NativeTypeUDT>( + static_cast<NativeTypeUDT &>(UnmodifiedNRS), std::move(Record)); + default: + // No other types can be modified. (LF_POINTER, for example, records + // its modifiers a different way. + assert(false && "Invalid LF_MODIFIER record"); + break; + } + return 0; +} + +SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) { + // First see if it's already in our cache. + const auto Entry = TypeIndexToSymbolId.find(Index); + if (Entry != TypeIndexToSymbolId.end()) + return Entry->second; + + // Symbols for built-in types are created on the fly. + if (Index.isSimple()) { + SymIndexId Result = createSimpleType(Index, ModifierOptions::None); + assert(TypeIndexToSymbolId.count(Index) == 0); + TypeIndexToSymbolId[Index] = Result; + return Result; + } + + // We need to instantiate and cache the desired type symbol. + auto Tpi = Session.getPDBFile().getPDBTpiStream(); + if (!Tpi) { + consumeError(Tpi.takeError()); + return 0; + } + codeview::LazyRandomTypeCollection &Types = Tpi->typeCollection(); + codeview::CVType CVT = Types.getType(Index); + + if (isUdtForwardRef(CVT)) { + Expected<TypeIndex> EFD = Tpi->findFullDeclForForwardRef(Index); + + if (!EFD) + consumeError(EFD.takeError()); + else if (*EFD != Index) { + assert(!isUdtForwardRef(Types.getType(*EFD))); + SymIndexId Result = findSymbolByTypeIndex(*EFD); + // Record a mapping from ForwardRef -> SymIndex of complete type so that + // we'll take the fast path next time. + assert(TypeIndexToSymbolId.count(Index) == 0); + TypeIndexToSymbolId[Index] = Result; + return Result; + } + } + + // At this point if we still have a forward ref udt it means the full decl was + // not in the PDB. We just have to deal with it and use the forward ref. + SymIndexId Id = 0; + switch (CVT.kind()) { + case codeview::LF_ENUM: + Id = createSymbolForType<NativeTypeEnum, EnumRecord>(Index, std::move(CVT)); + break; + case codeview::LF_ARRAY: + Id = createSymbolForType<NativeTypeArray, ArrayRecord>(Index, + std::move(CVT)); + break; + case codeview::LF_CLASS: + case codeview::LF_STRUCTURE: + case codeview::LF_INTERFACE: + Id = createSymbolForType<NativeTypeUDT, ClassRecord>(Index, std::move(CVT)); + break; + case codeview::LF_UNION: + Id = createSymbolForType<NativeTypeUDT, UnionRecord>(Index, std::move(CVT)); + break; + case codeview::LF_POINTER: + Id = createSymbolForType<NativeTypePointer, PointerRecord>(Index, + std::move(CVT)); + break; + case codeview::LF_MODIFIER: + Id = createSymbolForModifiedType(Index, std::move(CVT)); + break; + case codeview::LF_PROCEDURE: + Id = createSymbolForType<NativeTypeFunctionSig, ProcedureRecord>( + Index, std::move(CVT)); + break; + case codeview::LF_MFUNCTION: + Id = createSymbolForType<NativeTypeFunctionSig, MemberFunctionRecord>( + Index, std::move(CVT)); + break; + case codeview::LF_VTSHAPE: + Id = createSymbolForType<NativeTypeVTShape, VFTableShapeRecord>( + Index, std::move(CVT)); + break; + default: + Id = createSymbolPlaceholder(); + break; + } + if (Id != 0) { + assert(TypeIndexToSymbolId.count(Index) == 0); + TypeIndexToSymbolId[Index] = Id; + } + return Id; +} + +std::unique_ptr<PDBSymbol> +SymbolCache::getSymbolById(SymIndexId SymbolId) const { + assert(SymbolId < Cache.size()); + + // Id 0 is reserved. + if (SymbolId == 0 || SymbolId >= Cache.size()) + return nullptr; + + // Make sure to handle the case where we've inserted a placeholder symbol + // for types we don't yet suppport. + NativeRawSymbol *NRS = Cache[SymbolId].get(); + if (!NRS) + return nullptr; + + return PDBSymbol::create(Session, *NRS); +} + +NativeRawSymbol &SymbolCache::getNativeSymbolById(SymIndexId SymbolId) const { + return *Cache[SymbolId]; +} + +uint32_t SymbolCache::getNumCompilands() const { + if (!Dbi) + return 0; + + return Dbi->modules().getModuleCount(); +} + +SymIndexId SymbolCache::getOrCreateGlobalSymbolByOffset(uint32_t Offset) { + auto Iter = GlobalOffsetToSymbolId.find(Offset); + if (Iter != GlobalOffsetToSymbolId.end()) + return Iter->second; + + SymbolStream &SS = cantFail(Session.getPDBFile().getPDBSymbolStream()); + CVSymbol CVS = SS.readRecord(Offset); + SymIndexId Id = 0; + switch (CVS.kind()) { + case SymbolKind::S_UDT: { + UDTSym US = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(CVS)); + Id = createSymbol<NativeTypeTypedef>(std::move(US)); + break; + } + default: + Id = createSymbolPlaceholder(); + break; + } + if (Id != 0) { + assert(GlobalOffsetToSymbolId.count(Offset) == 0); + GlobalOffsetToSymbolId[Offset] = Id; + } + + return Id; +} + +std::unique_ptr<PDBSymbolCompiland> +SymbolCache::getOrCreateCompiland(uint32_t Index) { + if (!Dbi) + return nullptr; + + if (Index >= Compilands.size()) + return nullptr; + + if (Compilands[Index] == 0) { + const DbiModuleList &Modules = Dbi->modules(); + Compilands[Index] = + createSymbol<NativeCompilandSymbol>(Modules.getModuleDescriptor(Index)); + } + + return Session.getConcreteSymbolById<PDBSymbolCompiland>(Compilands[Index]); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp new file mode 100644 index 000000000000..003840b6e67e --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp @@ -0,0 +1,45 @@ +//===- SymbolStream.cpp - PDB Symbol Stream Access ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +SymbolStream::SymbolStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +SymbolStream::~SymbolStream() {} + +Error SymbolStream::reload() { + BinaryStreamReader Reader(*Stream); + + if (auto EC = Reader.readArray(SymbolRecords, Stream->getLength())) + return EC; + + return Error::success(); +} + +iterator_range<codeview::CVSymbolArray::Iterator> +SymbolStream::getSymbols(bool *HadError) const { + return llvm::make_range(SymbolRecords.begin(HadError), SymbolRecords.end()); +} + +Error SymbolStream::commit() { return Error::success(); } + +codeview::CVSymbol SymbolStream::readRecord(uint32_t Offset) const { + return *SymbolRecords.at(Offset); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp new file mode 100644 index 000000000000..b71b2b158144 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -0,0 +1,129 @@ +//===- TpiHashing.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/Support/CRC.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// Corresponds to `fUDTAnon`. +static bool isAnonymous(StringRef Name) { + return Name == "<unnamed-tag>" || Name == "__unnamed" || + Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed"); +} + +// Computes the hash for a user-defined type record. This could be a struct, +// class, union, or enum. +static uint32_t getHashForUdt(const TagRecord &Rec, + ArrayRef<uint8_t> FullRecord) { + ClassOptions Opts = Rec.getOptions(); + bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); + bool Scoped = bool(Opts & ClassOptions::Scoped); + bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName); + bool IsAnon = HasUniqueName && isAnonymous(Rec.getName()); + + if (!ForwardRef && !Scoped && !IsAnon) + return hashStringV1(Rec.getName()); + if (!ForwardRef && HasUniqueName && !IsAnon) + return hashStringV1(Rec.getUniqueName()); + return hashBufferV8(FullRecord); +} + +template <typename T> +static Expected<uint32_t> getHashForUdt(const CVType &Rec) { + T Deserialized; + if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), + Deserialized)) + return std::move(E); + return getHashForUdt(Deserialized, Rec.data()); +} + +template <typename T> +static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) { + T Deserialized; + if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), + Deserialized)) + return std::move(E); + + ClassOptions Opts = Deserialized.getOptions(); + + bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); + + uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data()); + + // If we don't have a forward ref we can't compute the hash of it from the + // full record because it requires hashing the entire buffer. + if (!ForwardRef) + return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0}; + + bool Scoped = bool(Opts & ClassOptions::Scoped); + + StringRef NameToHash = + Scoped ? Deserialized.getUniqueName() : Deserialized.getName(); + uint32_t FullHash = hashStringV1(NameToHash); + return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash}; +} + +template <typename T> +static Expected<uint32_t> getSourceLineHash(const CVType &Rec) { + T Deserialized; + if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), + Deserialized)) + return std::move(E); + char Buf[4]; + support::endian::write32le(Buf, Deserialized.getUDT().getIndex()); + return hashStringV1(StringRef(Buf, 4)); +} + +Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) { + switch (Type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return getTagRecordHashForUdt<ClassRecord>(Type); + case LF_UNION: + return getTagRecordHashForUdt<UnionRecord>(Type); + case LF_ENUM: + return getTagRecordHashForUdt<EnumRecord>(Type); + default: + assert(false && "Type is not a tag record!"); + } + return make_error<StringError>("Invalid record type", + inconvertibleErrorCode()); +} + +Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) { + switch (Rec.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return getHashForUdt<ClassRecord>(Rec); + case LF_UNION: + return getHashForUdt<UnionRecord>(Rec); + case LF_ENUM: + return getHashForUdt<EnumRecord>(Rec); + + case LF_UDT_SRC_LINE: + return getSourceLineHash<UdtSourceLineRecord>(Rec); + case LF_UDT_MOD_SRC_LINE: + return getSourceLineHash<UdtModSourceLineRecord>(Rec); + + default: + break; + } + + // Run CRC32 over the bytes. This corresponds to `hashBufv8`. + JamCRC JC(/*Init=*/0U); + JC.update(Rec.data()); + return JC.getCRC(); +} diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp new file mode 100644 index 000000000000..ac19db03fab2 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -0,0 +1,246 @@ +//===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.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/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> +#include <vector> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::support; +using namespace llvm::msf; +using namespace llvm::pdb; + +TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream) + : Pdb(File), Stream(std::move(Stream)) {} + +TpiStream::~TpiStream() = default; + +Error TpiStream::reload() { + BinaryStreamReader Reader(*Stream); + + if (Reader.bytesRemaining() < sizeof(TpiStreamHeader)) + return make_error<RawError>(raw_error_code::corrupt_file, + "TPI Stream does not contain a header."); + + if (Reader.readObject(Header)) + return make_error<RawError>(raw_error_code::corrupt_file, + "TPI Stream does not contain a header."); + + if (Header->Version != PdbTpiV80) + return make_error<RawError>(raw_error_code::corrupt_file, + "Unsupported TPI Version."); + + if (Header->HeaderSize != sizeof(TpiStreamHeader)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupt TPI Header size."); + + if (Header->HashKeySize != sizeof(ulittle32_t)) + return make_error<RawError>(raw_error_code::corrupt_file, + "TPI Stream expected 4 byte hash key size."); + + if (Header->NumHashBuckets < MinTpiHashBuckets || + Header->NumHashBuckets > MaxTpiHashBuckets) + return make_error<RawError>(raw_error_code::corrupt_file, + "TPI Stream Invalid number of hash buckets."); + + // The actual type records themselves come from this stream + if (auto EC = + Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes)) + return EC; + + BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData); + if (auto EC = + RecordReader.readArray(TypeRecords, TypeRecordsSubstream.size())) + return EC; + + // Hash indices, hash values, etc come from the hash stream. + if (Header->HashStreamIndex != kInvalidStreamIndex) { + auto HS = Pdb.safelyCreateIndexedStream(Header->HashStreamIndex); + if (!HS) { + consumeError(HS.takeError()); + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid TPI hash stream index."); + } + BinaryStreamReader HSR(**HS); + + // There should be a hash value for every type record, or no hashes at all. + uint32_t NumHashValues = + Header->HashValueBuffer.Length / sizeof(ulittle32_t); + if (NumHashValues != getNumTypeRecords() && NumHashValues != 0) + return make_error<RawError>( + raw_error_code::corrupt_file, + "TPI hash count does not match with the number of type records."); + HSR.setOffset(Header->HashValueBuffer.Off); + if (auto EC = HSR.readArray(HashValues, NumHashValues)) + return EC; + + HSR.setOffset(Header->IndexOffsetBuffer.Off); + uint32_t NumTypeIndexOffsets = + Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset); + if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets)) + return EC; + + if (Header->HashAdjBuffer.Length > 0) { + HSR.setOffset(Header->HashAdjBuffer.Off); + if (auto EC = HashAdjusters.load(HSR)) + return EC; + } + + HashStream = std::move(*HS); + } + + Types = std::make_unique<LazyRandomTypeCollection>( + TypeRecords, getNumTypeRecords(), getTypeIndexOffsets()); + return Error::success(); +} + +PdbRaw_TpiVer TpiStream::getTpiVersion() const { + uint32_t Value = Header->Version; + return static_cast<PdbRaw_TpiVer>(Value); +} + +uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; } + +uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; } + +uint32_t TpiStream::getNumTypeRecords() const { + return TypeIndexEnd() - TypeIndexBegin(); +} + +uint16_t TpiStream::getTypeHashStreamIndex() const { + return Header->HashStreamIndex; +} + +uint16_t TpiStream::getTypeHashStreamAuxIndex() const { + return Header->HashAuxStreamIndex; +} + +uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; } +uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } + +void TpiStream::buildHashMap() { + if (!HashMap.empty()) + return; + if (HashValues.empty()) + return; + + HashMap.resize(Header->NumHashBuckets); + + TypeIndex TIB{Header->TypeIndexBegin}; + TypeIndex TIE{Header->TypeIndexEnd}; + while (TIB < TIE) { + uint32_t HV = HashValues[TIB.toArrayIndex()]; + HashMap[HV].push_back(TIB++); + } +} + +std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const { + if (!supportsTypeLookup()) + const_cast<TpiStream*>(this)->buildHashMap(); + + uint32_t Bucket = hashStringV1(Name) % Header->NumHashBuckets; + if (Bucket > HashMap.size()) + return {}; + + std::vector<TypeIndex> Result; + for (TypeIndex TI : HashMap[Bucket]) { + std::string ThisName = computeTypeName(*Types, TI); + if (ThisName == Name) + Result.push_back(TI); + } + return Result; +} + +bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); } + +Expected<TypeIndex> +TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { + if (!supportsTypeLookup()) + const_cast<TpiStream*>(this)->buildHashMap(); + + CVType F = Types->getType(ForwardRefTI); + if (!isUdtForwardRef(F)) + return ForwardRefTI; + + Expected<TagRecordHash> ForwardTRH = hashTagRecord(F); + if (!ForwardTRH) + return ForwardTRH.takeError(); + + uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets; + + for (TypeIndex TI : HashMap[BucketIdx]) { + CVType CVT = Types->getType(TI); + if (CVT.kind() != F.kind()) + continue; + + Expected<TagRecordHash> FullTRH = hashTagRecord(CVT); + if (!FullTRH) + return FullTRH.takeError(); + if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash) + continue; + TagRecord &ForwardTR = ForwardTRH->getRecord(); + TagRecord &FullTR = FullTRH->getRecord(); + + if (!ForwardTR.hasUniqueName()) { + if (ForwardTR.getName() == FullTR.getName()) + return TI; + continue; + } + + if (!FullTR.hasUniqueName()) + continue; + if (ForwardTR.getUniqueName() == FullTR.getUniqueName()) + return TI; + } + return ForwardRefTI; +} + +codeview::CVType TpiStream::getType(codeview::TypeIndex Index) { + assert(!Index.isSimple()); + return Types->getType(Index); +} + +BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const { + return TypeRecordsSubstream; +} + +FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const { + return HashValues; +} + +FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const { + return TypeIndexOffsets; +} + +HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() { + return HashAdjusters; +} + +CVTypeRange TpiStream::types(bool *HadError) const { + return make_range(TypeRecords.begin(HadError), TypeRecords.end()); +} + +Error TpiStream::commit() { return Error::success(); } diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp new file mode 100644 index 000000000000..4f10f8524a9b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -0,0 +1,178 @@ +//===- TpiStreamBuilder.cpp - -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.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/RawTypes.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; +using namespace llvm::support; + +TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx) + : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) { +} + +TpiStreamBuilder::~TpiStreamBuilder() = default; + +void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) { + VerHeader = Version; +} + +void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record, + Optional<uint32_t> Hash) { + // If we just crossed an 8KB threshold, add a type index offset. + size_t NewSize = TypeRecordBytes + Record.size(); + constexpr size_t EightKB = 8 * 1024; + if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) { + TypeIndexOffsets.push_back( + {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex + + TypeRecords.size()), + ulittle32_t(TypeRecordBytes)}); + } + TypeRecordBytes = NewSize; + + TypeRecords.push_back(Record); + if (Hash) + TypeHashes.push_back(*Hash); +} + +Error TpiStreamBuilder::finalize() { + if (Header) + return Error::success(); + + TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>(); + + uint32_t Count = TypeRecords.size(); + + H->Version = VerHeader; + H->HeaderSize = sizeof(TpiStreamHeader); + H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; + H->TypeIndexEnd = H->TypeIndexBegin + Count; + H->TypeRecordBytes = TypeRecordBytes; + + H->HashStreamIndex = HashStreamIndex; + H->HashAuxStreamIndex = kInvalidStreamIndex; + H->HashKeySize = sizeof(ulittle32_t); + H->NumHashBuckets = MaxTpiHashBuckets - 1; + + // Recall that hash values go into a completely different stream identified by + // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data + // begins at offset 0 of this independent stream. + H->HashValueBuffer.Off = 0; + H->HashValueBuffer.Length = calculateHashBufferSize(); + + // We never write any adjustments into our PDBs, so this is usually some + // offset with zero length. + H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length; + H->HashAdjBuffer.Length = 0; + + H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length; + H->IndexOffsetBuffer.Length = calculateIndexOffsetSize(); + + Header = H; + return Error::success(); +} + +uint32_t TpiStreamBuilder::calculateSerializedLength() { + return sizeof(TpiStreamHeader) + TypeRecordBytes; +} + +uint32_t TpiStreamBuilder::calculateHashBufferSize() const { + assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) && + "either all or no type records should have hashes"); + return TypeHashes.size() * sizeof(ulittle32_t); +} + +uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const { + return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset); +} + +Error TpiStreamBuilder::finalizeMsfLayout() { + uint32_t Length = calculateSerializedLength(); + if (auto EC = Msf.setStreamSize(Idx, Length)) + return EC; + + uint32_t HashStreamSize = + calculateHashBufferSize() + calculateIndexOffsetSize(); + + if (HashStreamSize == 0) + return Error::success(); + + auto ExpectedIndex = Msf.addStream(HashStreamSize); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + HashStreamIndex = *ExpectedIndex; + if (!TypeHashes.empty()) { + ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size()); + MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size()); + for (uint32_t I = 0; I < TypeHashes.size(); ++I) { + HashBuffer[I] = TypeHashes[I] % (MaxTpiHashBuckets - 1); + } + ArrayRef<uint8_t> Bytes( + reinterpret_cast<const uint8_t *>(HashBuffer.data()), + calculateHashBufferSize()); + HashValueStream = + std::make_unique<BinaryByteStream>(Bytes, llvm::support::little); + } + return Error::success(); +} + +Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, + WritableBinaryStreamRef Buffer) { + if (auto EC = finalize()) + return EC; + + auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, + Idx, Allocator); + + BinaryStreamWriter Writer(*InfoS); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + for (auto Rec : TypeRecords) { + assert(!Rec.empty()); // An empty record will not write anything, but it + // would shift all offsets from here on. + if (auto EC = Writer.writeBytes(Rec)) + return EC; + } + + if (HashStreamIndex != kInvalidStreamIndex) { + auto HVS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, HashStreamIndex, Allocator); + BinaryStreamWriter HW(*HVS); + if (HashValueStream) { + if (auto EC = HW.writeStreamRef(*HashValueStream)) + return EC; + } + + for (auto &IndexOffset : TypeIndexOffsets) { + if (auto EC = HW.writeObject(IndexOffset)) + return EC; + } + } + + return Error::success(); +} diff --git a/llvm/lib/DebugInfo/PDB/PDB.cpp b/llvm/lib/DebugInfo/PDB/PDB.cpp new file mode 100644 index 000000000000..e7b968cb7bea --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDB.cpp @@ -0,0 +1,54 @@ +//===- PDB.cpp - base header file for creating a PDB reader ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#if LLVM_ENABLE_DIA_SDK +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#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; + +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) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return errorCodeToError(ErrorOrBuffer.getError()); + + return NativeSession::createFromPdb(std::move(*ErrorOrBuffer), Session); + } + +#if LLVM_ENABLE_DIA_SDK + return DIASession::createFromPdb(Path, Session); +#else + return make_error<PDBError>(pdb_error_code::dia_sdk_not_present); +#endif +} + +Error llvm::pdb::loadDataForEXE(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::createFromExe(Path, Session); + +#if LLVM_ENABLE_DIA_SDK + return DIASession::createFromExe(Path, Session); +#else + return make_error<PDBError>(pdb_error_code::dia_sdk_not_present); +#endif +} diff --git a/llvm/lib/DebugInfo/PDB/PDBContext.cpp b/llvm/lib/DebugInfo/PDB/PDBContext.cpp new file mode 100644 index 000000000000..e452f1d4ced7 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBContext.cpp @@ -0,0 +1,124 @@ +//===-- PDBContext.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===/ + +#include "llvm/DebugInfo/PDB/PDBContext.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" +#include "llvm/Object/COFF.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::pdb; + +PDBContext::PDBContext(const COFFObjectFile &Object, + std::unique_ptr<IPDBSession> PDBSession) + : DIContext(CK_PDB), Session(std::move(PDBSession)) { + ErrorOr<uint64_t> ImageBase = Object.getImageBase(); + if (ImageBase) + Session->setLoadAddress(ImageBase.get()); +} + +void PDBContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts){} + +DILineInfo PDBContext::getLineInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Specifier) { + DILineInfo Result; + Result.FunctionName = getFunctionName(Address.Address, Specifier.FNKind); + + uint32_t Length = 1; + std::unique_ptr<PDBSymbol> Symbol = + Session->findSymbolByAddress(Address.Address, PDB_SymType::None); + if (auto Func = dyn_cast_or_null<PDBSymbolFunc>(Symbol.get())) { + Length = Func->getLength(); + } else if (auto Data = dyn_cast_or_null<PDBSymbolData>(Symbol.get())) { + Length = Data->getLength(); + } + + // If we couldn't find a symbol, then just assume 1 byte, so that we get + // only the line number of the first instruction. + auto LineNumbers = Session->findLineNumbersByAddress(Address.Address, Length); + if (!LineNumbers || LineNumbers->getChildCount() == 0) + return Result; + + auto LineInfo = LineNumbers->getNext(); + assert(LineInfo); + auto SourceFile = Session->getSourceFileById(LineInfo->getSourceFileId()); + + if (SourceFile && + Specifier.FLIKind != DILineInfoSpecifier::FileLineInfoKind::None) + Result.FileName = SourceFile->getFileName(); + Result.Column = LineInfo->getColumnNumber(); + Result.Line = LineInfo->getLineNumber(); + return Result; +} + +DILineInfoTable +PDBContext::getLineInfoForAddressRange(object::SectionedAddress Address, + uint64_t Size, + DILineInfoSpecifier Specifier) { + if (Size == 0) + return DILineInfoTable(); + + DILineInfoTable Table; + auto LineNumbers = Session->findLineNumbersByAddress(Address.Address, Size); + if (!LineNumbers || LineNumbers->getChildCount() == 0) + return Table; + + while (auto LineInfo = LineNumbers->getNext()) { + DILineInfo LineEntry = getLineInfoForAddress( + {LineInfo->getVirtualAddress(), Address.SectionIndex}, Specifier); + Table.push_back(std::make_pair(LineInfo->getVirtualAddress(), LineEntry)); + } + return Table; +} + +DIInliningInfo +PDBContext::getInliningInfoForAddress(object::SectionedAddress Address, + DILineInfoSpecifier Specifier) { + DIInliningInfo InlineInfo; + DILineInfo Frame = getLineInfoForAddress(Address, Specifier); + InlineInfo.addFrame(Frame); + return InlineInfo; +} + +std::vector<DILocal> +PDBContext::getLocalsForAddress(object::SectionedAddress Address) { + return std::vector<DILocal>(); +} + +std::string PDBContext::getFunctionName(uint64_t Address, + DINameKind NameKind) const { + if (NameKind == DINameKind::None) + return std::string(); + + std::unique_ptr<PDBSymbol> FuncSymbol = + Session->findSymbolByAddress(Address, PDB_SymType::Function); + auto *Func = dyn_cast_or_null<PDBSymbolFunc>(FuncSymbol.get()); + + if (NameKind == DINameKind::LinkageName) { + // It is not possible to get the mangled linkage name through a + // PDBSymbolFunc. For that we have to specifically request a + // PDBSymbolPublicSymbol. + auto PublicSym = + Session->findSymbolByAddress(Address, PDB_SymType::PublicSymbol); + if (auto *PS = dyn_cast_or_null<PDBSymbolPublicSymbol>(PublicSym.get())) { + // If we also have a function symbol, prefer the use of public symbol name + // only if it refers to the same address. The public symbol uses the + // linkage name while the function does not. + if (!Func || Func->getVirtualAddress() == PS->getVirtualAddress()) + return PS->getName(); + } + } + + return Func ? Func->getName() : std::string(); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBExtras.cpp b/llvm/lib/DebugInfo/PDB/PDBExtras.cpp new file mode 100644 index 000000000000..354a99476c4b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBExtras.cpp @@ -0,0 +1,393 @@ +//===- PDBExtras.cpp - helper functions and classes for PDBs --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::pdb; + +#define CASE_OUTPUT_ENUM_CLASS_STR(Class, Value, Str, Stream) \ + case Class::Value: \ + Stream << Str; \ + break; + +#define CASE_OUTPUT_ENUM_CLASS_NAME(Class, Value, Stream) \ + CASE_OUTPUT_ENUM_CLASS_STR(Class, Value, #Value, Stream) + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_VariantType &Type) { + switch (Type) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Bool, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Single, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Double, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int8, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int32, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int64, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt8, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt32, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt64, OS) + default: + OS << "Unknown"; + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_BuiltinType &Type) { + switch (Type) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, None, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Void, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Char, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, WCharT, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Int, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, UInt, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Float, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, BCD, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Bool, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Long, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, ULong, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Currency, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Date, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Variant, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Complex, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Bitfield, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, BSTR, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, HResult, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Char16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_BuiltinType, Char32, OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_CallingConv &Conv) { + OS << "__"; + switch (Conv) { + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearC , "cdecl", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarC , "cdecl", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearPascal , "pascal", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarPascal , "pascal", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearFast , "fastcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarFast , "fastcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearStdCall, "stdcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarStdCall , "stdcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearSysCall, "syscall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarSysCall , "syscall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, ThisCall , "thiscall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, MipsCall , "mipscall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, Generic , "genericcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, AlphaCall , "alphacall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, PpcCall , "ppccall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, SHCall , "superhcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, ArmCall , "armcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, AM33Call , "am33call", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, TriCall , "tricall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, SH5Call , "sh5call", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, M32RCall , "m32rcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, ClrCall , "clrcall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, Inline , "inlinecall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearVector , "vectorcall", OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_DataKind &Data) { + switch (Data) { + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Unknown, "unknown", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Local, "local", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, StaticLocal, "static local", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Param, "param", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, ObjectPtr, "this ptr", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, FileStatic, "static global", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Global, "global", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Member, "member", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, StaticMember, "static member", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Constant, "const", OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const llvm::codeview::CPURegister &CpuReg) { + if (CpuReg.Cpu == llvm::codeview::CPUType::ARM64) { + switch (CpuReg.Reg) { +#define CV_REGISTERS_ARM64 +#define CV_REGISTER(name, val) \ + case codeview::RegisterId::name: \ + OS << #name; \ + return OS; +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM64 + + default: + break; + } + } else { + switch (CpuReg.Reg) { +#define CV_REGISTERS_X86 +#define CV_REGISTER(name, val) \ + case codeview::RegisterId::name: \ + OS << #name; \ + return OS; +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_X86 + + default: + break; + } + } + OS << static_cast<int>(CpuReg.Reg); + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_LocType &Loc) { + switch (Loc) { + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Static, "static", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, TLS, "tls", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, RegRel, "regrel", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, ThisRel, "thisrel", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Enregistered, "register", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, BitField, "bitfield", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Slot, "slot", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, IlRel, "IL rel", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, MetaData, "metadata", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Constant, "constant", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, RegRelAliasIndir, + "regrelaliasindir", OS) + default: + OS << "Unknown"; + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const codeview::ThunkOrdinal &Thunk) { + switch (Thunk) { + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, BranchIsland, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, Pcode, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, Standard, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, ThisAdjustor, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, TrampIncremental, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, UnknownLoad, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::ThunkOrdinal, Vcall, OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_Checksum &Checksum) { + switch (Checksum) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Checksum, None, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Checksum, MD5, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Checksum, SHA1, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Checksum, SHA256, OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_Lang &Lang) { + switch (Lang) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, C, OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_Lang, Cpp, "C++", OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Fortran, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Masm, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Pascal, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Basic, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Cobol, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Link, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Cvtres, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Cvtpgd, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, CSharp, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, VB, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, ILAsm, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Java, OS) + 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) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Lang, Swift, OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_SymType &Tag) { + switch (Tag) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Exe, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Compiland, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, CompilandDetails, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, CompilandEnv, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Function, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Block, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Data, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Annotation, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Label, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, PublicSymbol, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, UDT, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Enum, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, FunctionSig, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, PointerType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, ArrayType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, BuiltinType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Typedef, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, BaseClass, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Friend, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, FunctionArg, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, FuncDebugStart, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, FuncDebugEnd, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, UsingNamespace, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, VTableShape, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, VTable, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Custom, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Thunk, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, CustomType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, ManagedType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Dimension, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, CallSite, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, InlineSite, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, BaseInterface, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, VectorType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, MatrixType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, HLSLType, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Caller, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Callee, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Export, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, HeapAllocationSite, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, CoffGroup, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SymType, Inlinee, OS) + default: + OS << "Unknown SymTag " << uint32_t(Tag); + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_MemberAccess &Access) { + switch (Access) { + CASE_OUTPUT_ENUM_CLASS_STR(PDB_MemberAccess, Public, "public", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_MemberAccess, Protected, "protected", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_MemberAccess, Private, "private", OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_UdtType &Type) { + switch (Type) { + CASE_OUTPUT_ENUM_CLASS_STR(PDB_UdtType, Class, "class", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_UdtType, Struct, "struct", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_UdtType, Interface, "interface", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_UdtType, Union, "union", OS) + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_Machine &Machine) { + switch (Machine) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Am33, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Amd64, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Arm, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, ArmNT, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Ebc, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, x86, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Ia64, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, M32R, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Mips16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, MipsFpu, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, MipsFpu16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, PowerPC, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, PowerPCFP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, R4000, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, SH3, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, SH3DSP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, SH4, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, SH5, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Thumb, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, WceMipsV2, OS) + default: + OS << "Unknown"; + } + return OS; +} + +raw_ostream &llvm::pdb::dumpPDBSourceCompression(raw_ostream &OS, + uint32_t Compression) { + switch (Compression) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, None, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, Huffman, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, LZ, OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_SourceCompression, RunLengthEncoded, "RLE", + OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, DotNet, OS) + default: + OS << "Unknown (" << Compression << ")"; + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const Variant &Value) { + switch (Value.Type) { + case PDB_VariantType::Bool: + OS << (Value.Value.Bool ? "true" : "false"); + break; + case PDB_VariantType::Double: + OS << Value.Value.Double; + break; + case PDB_VariantType::Int16: + OS << Value.Value.Int16; + break; + case PDB_VariantType::Int32: + OS << Value.Value.Int32; + break; + case PDB_VariantType::Int64: + OS << Value.Value.Int64; + break; + case PDB_VariantType::Int8: + OS << static_cast<int>(Value.Value.Int8); + break; + case PDB_VariantType::Single: + OS << Value.Value.Single; + break; + case PDB_VariantType::UInt16: + OS << Value.Value.UInt16; + break; + case PDB_VariantType::UInt32: + OS << Value.Value.UInt32; + break; + case PDB_VariantType::UInt64: + OS << Value.Value.UInt64; + break; + case PDB_VariantType::UInt8: + OS << static_cast<unsigned>(Value.Value.UInt8); + break; + case PDB_VariantType::String: + OS << Value.Value.String; + break; + default: + OS << Value.Type; + } + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const VersionInfo &Version) { + OS << Version.Major << "." << Version.Minor << "." << Version.Build; + return OS; +} + +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const TagStats &Stats) { + for (auto Tag : Stats) { + OS << Tag.first << ":" << Tag.second << " "; + } + return OS; +} diff --git a/llvm/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp b/llvm/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp new file mode 100644 index 000000000000..8eb3311b09e3 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp @@ -0,0 +1,39 @@ +//===- PDBInterfaceAnchors.h - defines class anchor funcions ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Class anchors are necessary per the LLVM Coding style guide, to ensure that +// the vtable is only generated in this object file, and not in every object +// file that incldues the corresponding header. +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/IPDBDataStream.h" +#include "llvm/DebugInfo/PDB/IPDBFrameData.h" +#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/IPDBTable.h" + +using namespace llvm; +using namespace llvm::pdb; + +IPDBSession::~IPDBSession() = default; + +IPDBDataStream::~IPDBDataStream() = default; + +IPDBRawSymbol::~IPDBRawSymbol() = default; + +IPDBLineNumber::~IPDBLineNumber() = default; + +IPDBTable::~IPDBTable() = default; + +IPDBInjectedSource::~IPDBInjectedSource() = default; + +IPDBSectionContrib::~IPDBSectionContrib() = default; + +IPDBFrameData::~IPDBFrameData() = default; diff --git a/llvm/lib/DebugInfo/PDB/PDBSymDumper.cpp b/llvm/lib/DebugInfo/PDB/PDBSymDumper.cpp new file mode 100644 index 000000000000..0956a32f4a49 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymDumper.cpp @@ -0,0 +1,146 @@ +//===- PDBSymDumper.cpp - ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; +using namespace llvm::pdb; + +#define PDB_SYMDUMP_UNREACHABLE(Type) \ + if (RequireImpl) \ + llvm_unreachable("Attempt to dump " #Type " with no dump implementation"); + +PDBSymDumper::PDBSymDumper(bool ShouldRequireImpl) + : RequireImpl(ShouldRequireImpl) {} + +PDBSymDumper::~PDBSymDumper() = default; + +void PDBSymDumper::dump(const PDBSymbolAnnotation &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolAnnotation) +} + +void PDBSymDumper::dump(const PDBSymbolBlock &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolBlock) +} + +void PDBSymDumper::dump(const PDBSymbolCompiland &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolCompiland) +} + +void PDBSymDumper::dump(const PDBSymbolCompilandDetails &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolCompilandDetails) +} + +void PDBSymDumper::dump(const PDBSymbolCompilandEnv &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolCompilandEnv) +} + +void PDBSymDumper::dump(const PDBSymbolCustom &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolCustom) +} + +void PDBSymDumper::dump(const PDBSymbolData &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolData) +} + +void PDBSymDumper::dump(const PDBSymbolExe &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolExe) +} + +void PDBSymDumper::dump(const PDBSymbolFunc &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolFunc) +} + +void PDBSymDumper::dump(const PDBSymbolFuncDebugEnd &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolFuncDebugEnd) +} + +void PDBSymDumper::dump(const PDBSymbolFuncDebugStart &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolFuncDebugStart) +} + +void PDBSymDumper::dump(const PDBSymbolLabel &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolLabel) +} + +void PDBSymDumper::dump(const PDBSymbolPublicSymbol &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolPublicSymbol) +} + +void PDBSymDumper::dump(const PDBSymbolThunk &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolThunk) +} + +void PDBSymDumper::dump(const PDBSymbolTypeArray &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeArray) +} + +void PDBSymDumper::dump(const PDBSymbolTypeBaseClass &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeBaseClass) +} + +void PDBSymDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeBuiltin) +} + +void PDBSymDumper::dump(const PDBSymbolTypeCustom &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeCustom) +} + +void PDBSymDumper::dump(const PDBSymbolTypeDimension &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeDimension) +} + +void PDBSymDumper::dump(const PDBSymbolTypeEnum &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeEnum) +} + +void PDBSymDumper::dump(const PDBSymbolTypeFriend &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeFriend) +} + +void PDBSymDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeFunctionArg) +} + +void PDBSymDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeFunctionSig) +} + +void PDBSymDumper::dump(const PDBSymbolTypeManaged &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeManaged) +} + +void PDBSymDumper::dump(const PDBSymbolTypePointer &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypePointer) +} + +void PDBSymDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeTypedef) +} + +void PDBSymDumper::dump(const PDBSymbolTypeUDT &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeUDT) +} + +void PDBSymDumper::dump(const PDBSymbolTypeVTable &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeVTable) +} + +void PDBSymDumper::dump(const PDBSymbolTypeVTableShape &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolTypeVTableShape) +} + +void PDBSymDumper::dump(const PDBSymbolUnknown &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolUnknown) +} + +void PDBSymDumper::dump(const PDBSymbolUsingNamespace &Symbol) { + PDB_SYMDUMP_UNREACHABLE(PDBSymbolUsingNamespace) +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbol.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbol.cpp new file mode 100644 index 000000000000..34c8ac41d45b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbol.cpp @@ -0,0 +1,214 @@ +//===- PDBSymbol.cpp - base class for user-facing symbol types --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolAnnotation.h" +#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCustom.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h" +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeCustom.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeDimension.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFriend.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeManaged.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h" +#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h" +#include "llvm/DebugInfo/PDB/PDBSymbolUsingNamespace.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include <algorithm> +#include <memory> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbol::PDBSymbol(const IPDBSession &PDBSession) : Session(PDBSession) {} + +PDBSymbol::PDBSymbol(PDBSymbol &&Other) + : Session(Other.Session), RawSymbol(std::move(Other.RawSymbol)) {} + +PDBSymbol::~PDBSymbol() = default; + +#define FACTORY_SYMTAG_CASE(Tag, Type) \ + case PDB_SymType::Tag: \ + return std::unique_ptr<PDBSymbol>(new Type(PDBSession)); + +std::unique_ptr<PDBSymbol> +PDBSymbol::createSymbol(const IPDBSession &PDBSession, PDB_SymType Tag) { + switch (Tag) { + FACTORY_SYMTAG_CASE(Exe, PDBSymbolExe) + FACTORY_SYMTAG_CASE(Compiland, PDBSymbolCompiland) + FACTORY_SYMTAG_CASE(CompilandDetails, PDBSymbolCompilandDetails) + FACTORY_SYMTAG_CASE(CompilandEnv, PDBSymbolCompilandEnv) + FACTORY_SYMTAG_CASE(Function, PDBSymbolFunc) + FACTORY_SYMTAG_CASE(Block, PDBSymbolBlock) + FACTORY_SYMTAG_CASE(Data, PDBSymbolData) + FACTORY_SYMTAG_CASE(Annotation, PDBSymbolAnnotation) + FACTORY_SYMTAG_CASE(Label, PDBSymbolLabel) + FACTORY_SYMTAG_CASE(PublicSymbol, PDBSymbolPublicSymbol) + FACTORY_SYMTAG_CASE(UDT, PDBSymbolTypeUDT) + FACTORY_SYMTAG_CASE(Enum, PDBSymbolTypeEnum) + FACTORY_SYMTAG_CASE(FunctionSig, PDBSymbolTypeFunctionSig) + FACTORY_SYMTAG_CASE(PointerType, PDBSymbolTypePointer) + FACTORY_SYMTAG_CASE(ArrayType, PDBSymbolTypeArray) + FACTORY_SYMTAG_CASE(BuiltinType, PDBSymbolTypeBuiltin) + FACTORY_SYMTAG_CASE(Typedef, PDBSymbolTypeTypedef) + FACTORY_SYMTAG_CASE(BaseClass, PDBSymbolTypeBaseClass) + FACTORY_SYMTAG_CASE(Friend, PDBSymbolTypeFriend) + FACTORY_SYMTAG_CASE(FunctionArg, PDBSymbolTypeFunctionArg) + FACTORY_SYMTAG_CASE(FuncDebugStart, PDBSymbolFuncDebugStart) + FACTORY_SYMTAG_CASE(FuncDebugEnd, PDBSymbolFuncDebugEnd) + FACTORY_SYMTAG_CASE(UsingNamespace, PDBSymbolUsingNamespace) + FACTORY_SYMTAG_CASE(VTableShape, PDBSymbolTypeVTableShape) + FACTORY_SYMTAG_CASE(VTable, PDBSymbolTypeVTable) + FACTORY_SYMTAG_CASE(Custom, PDBSymbolCustom) + FACTORY_SYMTAG_CASE(Thunk, PDBSymbolThunk) + FACTORY_SYMTAG_CASE(CustomType, PDBSymbolTypeCustom) + FACTORY_SYMTAG_CASE(ManagedType, PDBSymbolTypeManaged) + FACTORY_SYMTAG_CASE(Dimension, PDBSymbolTypeDimension) + default: + return std::unique_ptr<PDBSymbol>(new PDBSymbolUnknown(PDBSession)); + } +} + +std::unique_ptr<PDBSymbol> +PDBSymbol::create(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> RawSymbol) { + auto SymbolPtr = createSymbol(PDBSession, RawSymbol->getSymTag()); + SymbolPtr->RawSymbol = RawSymbol.get(); + SymbolPtr->OwnedRawSymbol = std::move(RawSymbol); + return SymbolPtr; +} + +std::unique_ptr<PDBSymbol> PDBSymbol::create(const IPDBSession &PDBSession, + IPDBRawSymbol &RawSymbol) { + auto SymbolPtr = createSymbol(PDBSession, RawSymbol.getSymTag()); + SymbolPtr->RawSymbol = &RawSymbol; + return SymbolPtr; +} + +void PDBSymbol::defaultDump(raw_ostream &OS, int Indent, + PdbSymbolIdField ShowFlags, + PdbSymbolIdField RecurseFlags) const { + RawSymbol->dump(OS, Indent, ShowFlags, RecurseFlags); +} + +void PDBSymbol::dumpProperties() const { + outs() << "\n"; + defaultDump(outs(), 0, PdbSymbolIdField::All, PdbSymbolIdField::None); + outs().flush(); +} + +void PDBSymbol::dumpChildStats() const { + TagStats Stats; + getChildStats(Stats); + outs() << "\n"; + for (auto &Stat : Stats) { + outs() << Stat.first << ": " << Stat.second << "\n"; + } + outs().flush(); +} + +PDB_SymType PDBSymbol::getSymTag() const { return RawSymbol->getSymTag(); } +uint32_t PDBSymbol::getSymIndexId() const { return RawSymbol->getSymIndexId(); } + +std::unique_ptr<IPDBEnumSymbols> PDBSymbol::findAllChildren() const { + return findAllChildren(PDB_SymType::None); +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbol::findAllChildren(PDB_SymType Type) const { + return RawSymbol->findChildren(Type); +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbol::findChildren(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags) const { + return RawSymbol->findChildren(Type, Name, Flags); +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbol::findChildrenByRVA(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint32_t RVA) const { + return RawSymbol->findChildrenByRVA(Type, Name, Flags, RVA); +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbol::findInlineFramesByRVA(uint32_t RVA) const { + return RawSymbol->findInlineFramesByRVA(RVA); +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbol::getChildStats(TagStats &Stats) const { + std::unique_ptr<IPDBEnumSymbols> Result(findAllChildren()); + if (!Result) + return nullptr; + Stats.clear(); + while (auto Child = Result->getNext()) { + ++Stats[Child->getSymTag()]; + } + Result->reset(); + return Result; +} + +std::unique_ptr<PDBSymbol> PDBSymbol::getSymbolByIdHelper(uint32_t Id) const { + return Session.getSymbolById(Id); +} + +void llvm::pdb::dumpSymbolIdField(raw_ostream &OS, StringRef Name, + SymIndexId Value, int Indent, + const IPDBSession &Session, + PdbSymbolIdField FieldId, + PdbSymbolIdField ShowFlags, + PdbSymbolIdField RecurseFlags) { + if ((FieldId & ShowFlags) == PdbSymbolIdField::None) + return; + + OS << "\n"; + OS.indent(Indent); + OS << Name << ": " << Value; + // Don't recurse unless the user requested it. + if ((FieldId & RecurseFlags) == PdbSymbolIdField::None) + return; + // And obviously don't recurse on the symbol itself. + if (FieldId == PdbSymbolIdField::SymIndexId) + return; + + auto Child = Session.getSymbolById(Value); + + // It could have been a placeholder symbol for a type we don't yet support, + // so just exit in that case. + if (!Child) + return; + + // Don't recurse more than once, so pass PdbSymbolIdField::None) for the + // recurse flags. + Child->defaultDump(OS, Indent + 2, ShowFlags, PdbSymbolIdField::None); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp new file mode 100644 index 000000000000..0fa83efb7ae0 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp @@ -0,0 +1,20 @@ +//===- PDBSymbolAnnotation.cpp - --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolAnnotation.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolAnnotation::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolBlock.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolBlock.cpp new file mode 100644 index 000000000000..9452282a8817 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolBlock.cpp @@ -0,0 +1,19 @@ +//===- PDBSymbolBlock.cpp - -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolBlock::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp new file mode 100644 index 000000000000..9b2883546305 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp @@ -0,0 +1,109 @@ +//===- PDBSymbolCompiland.cpp - compiland details ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Path.h" +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolCompiland::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +std::string PDBSymbolCompiland::getSourceFileName() const { + return sys::path::filename(getSourceFileFullPath()).str(); +} + +std::string PDBSymbolCompiland::getSourceFileFullPath() const { + std::string SourceFileFullPath; + + // RecordedResult could be the basename, relative path or full path of the + // source file. Usually it is retrieved and recorded from the command that + // compiles this compiland. + // + // cmd FileName -> RecordedResult = .\\FileName + // cmd (Path)\\FileName -> RecordedResult = (Path)\\FileName + // + std::string RecordedResult = RawSymbol->getSourceFileName(); + + if (RecordedResult.empty()) { + if (auto Envs = findAllChildren<PDBSymbolCompilandEnv>()) { + std::string EnvWorkingDir, EnvSrc; + + while (auto Env = Envs->getNext()) { + std::string Var = Env->getName(); + if (Var == "cwd") { + EnvWorkingDir = Env->getValue(); + continue; + } + if (Var == "src") { + EnvSrc = Env->getValue(); + if (sys::path::is_absolute(EnvSrc)) + return EnvSrc; + RecordedResult = EnvSrc; + continue; + } + } + if (!EnvWorkingDir.empty() && !EnvSrc.empty()) { + auto Len = EnvWorkingDir.length(); + if (EnvWorkingDir[Len - 1] != '/' && EnvWorkingDir[Len - 1] != '\\') { + std::string Path = EnvWorkingDir + "\\" + EnvSrc; + std::replace(Path.begin(), Path.end(), '/', '\\'); + // We will return it as full path if we can't find a better one. + if (sys::path::is_absolute(Path)) + SourceFileFullPath = Path; + } + } + } + } + + if (!RecordedResult.empty()) { + if (sys::path::is_absolute(RecordedResult)) + return RecordedResult; + + // This searches name that has same basename as the one in RecordedResult. + auto OneSrcFile = Session.findOneSourceFile( + this, RecordedResult, PDB_NameSearchFlags::NS_CaseInsensitive); + if (OneSrcFile) + return OneSrcFile->getFileName(); + } + + // At this point, we have to walk through all source files of this compiland, + // and determine the right source file if any that is used to generate this + // compiland based on language indicated in compilanddetails language field. + auto Details = findOneChild<PDBSymbolCompilandDetails>(); + PDB_Lang Lang = Details ? Details->getLanguage() : PDB_Lang::Cpp; + auto SrcFiles = Session.getSourceFilesForCompiland(*this); + if (SrcFiles) { + while (auto File = SrcFiles->getNext()) { + std::string FileName = File->getFileName(); + auto file_extension = sys::path::extension(FileName); + if (StringSwitch<bool>(file_extension.lower()) + .Case(".cpp", Lang == PDB_Lang::Cpp) + .Case(".cc", Lang == PDB_Lang::Cpp) + .Case(".cxx", Lang == PDB_Lang::Cpp) + .Case(".c", Lang == PDB_Lang::C) + .Case(".asm", Lang == PDB_Lang::Masm) + .Case(".swift", Lang == PDB_Lang::Swift) + .Default(false)) + return File->getFileName(); + } + } + + return SourceFileFullPath; +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp new file mode 100644 index 000000000000..0d86dfe1e632 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolCompilandDetails.cpp - compiland details --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolCompilandDetails::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp new file mode 100644 index 000000000000..61f119405fd9 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp @@ -0,0 +1,29 @@ +//===- PDBSymbolCompilandEnv.cpp - compiland env variables ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h" + +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +std::string PDBSymbolCompilandEnv::getValue() const { + Variant Value = RawSymbol->getValue(); + if (Value.Type != PDB_VariantType::String) + return std::string(); + return std::string(Value.Value.String); +} + +void PDBSymbolCompilandEnv::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolCustom.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolCustom.cpp new file mode 100644 index 000000000000..6c9a4aa76c3d --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolCustom.cpp @@ -0,0 +1,24 @@ +//===- PDBSymbolCustom.cpp - compiler-specific types ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCustom.h" + +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolCustom::getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) { + RawSymbol->getDataBytes(bytes); +} + +void PDBSymbolCustom::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolData.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolData.cpp new file mode 100644 index 000000000000..d2b82111ccd5 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolData.cpp @@ -0,0 +1,68 @@ +//===- PDBSymbolData.cpp - PDB data (e.g. variable) accessors ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolData::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } + +std::unique_ptr<IPDBEnumLineNumbers> PDBSymbolData::getLineNumbers() const { + auto Len = RawSymbol->getLength(); + Len = Len ? Len : 1; + if (auto RVA = RawSymbol->getRelativeVirtualAddress()) + return Session.findLineNumbersByRVA(RVA, Len); + + if (auto Section = RawSymbol->getAddressSection()) + return Session.findLineNumbersBySectOffset( + Section, RawSymbol->getAddressOffset(), Len); + + return nullptr; +} + +uint32_t PDBSymbolData::getCompilandId() const { + if (auto Lines = getLineNumbers()) { + if (auto FirstLine = Lines->getNext()) + return FirstLine->getCompilandId(); + } + + uint32_t DataSection = RawSymbol->getAddressSection(); + uint32_t DataOffset = RawSymbol->getAddressOffset(); + if (DataSection == 0) { + if (auto RVA = RawSymbol->getRelativeVirtualAddress()) + Session.addressForRVA(RVA, DataSection, DataOffset); + } + + if (DataSection) { + if (auto SecContribs = Session.getSectionContribs()) { + while (auto Section = SecContribs->getNext()) { + if (Section->getAddressSection() == DataSection && + Section->getAddressOffset() <= DataOffset && + (Section->getAddressOffset() + Section->getLength()) > DataOffset) + return Section->getCompilandId(); + } + } + } else { + auto LexParentId = RawSymbol->getLexicalParentId(); + while (auto LexParent = Session.getSymbolById(LexParentId)) { + if (LexParent->getSymTag() == PDB_SymType::Exe) + break; + if (LexParent->getSymTag() == PDB_SymType::Compiland) + return LexParentId; + LexParentId = LexParent->getRawSymbol().getLexicalParentId(); + } + } + + return 0; +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp new file mode 100644 index 000000000000..c85756c43e47 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp @@ -0,0 +1,29 @@ +//===- PDBSymbolExe.cpp - ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolExe::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } + +uint32_t PDBSymbolExe::getPointerByteSize() const { + auto Pointer = findOneChild<PDBSymbolTypePointer>(); + if (Pointer) + return Pointer->getLength(); + + if (getMachineType() == PDB_Machine::x86) + return 4; + return 8; +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolFunc.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolFunc.cpp new file mode 100644 index 000000000000..cb0329bc0ed7 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolFunc.cpp @@ -0,0 +1,111 @@ +//===- PDBSymbolFunc.cpp - --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" + +#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include <unordered_set> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +class FunctionArgEnumerator : public IPDBEnumChildren<PDBSymbolData> { +public: + typedef ConcreteSymbolEnumerator<PDBSymbolData> ArgEnumeratorType; + + FunctionArgEnumerator(const IPDBSession &PDBSession, + const PDBSymbolFunc &PDBFunc) + : Session(PDBSession), Func(PDBFunc) { + // Arguments can appear multiple times if they have live range + // information, so we only take the first occurrence. + std::unordered_set<std::string> SeenNames; + auto DataChildren = Func.findAllChildren<PDBSymbolData>(); + while (auto Child = DataChildren->getNext()) { + if (Child->getDataKind() == PDB_DataKind::Param) { + std::string Name = Child->getName(); + if (SeenNames.find(Name) != SeenNames.end()) + continue; + Args.push_back(std::move(Child)); + SeenNames.insert(Name); + } + } + reset(); + } + + uint32_t getChildCount() const override { return Args.size(); } + + std::unique_ptr<PDBSymbolData> + getChildAtIndex(uint32_t Index) const override { + if (Index >= Args.size()) + return nullptr; + + return Session.getConcreteSymbolById<PDBSymbolData>( + Args[Index]->getSymIndexId()); + } + + std::unique_ptr<PDBSymbolData> getNext() override { + if (CurIter == Args.end()) + return nullptr; + const auto &Result = **CurIter; + ++CurIter; + return Session.getConcreteSymbolById<PDBSymbolData>(Result.getSymIndexId()); + } + + void reset() override { CurIter = Args.empty() ? Args.end() : Args.begin(); } + +private: + typedef std::vector<std::unique_ptr<PDBSymbolData>> ArgListType; + const IPDBSession &Session; + const PDBSymbolFunc &Func; + ArgListType Args; + ArgListType::const_iterator CurIter; +}; +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolData>> +PDBSymbolFunc::getArguments() const { + return std::make_unique<FunctionArgEnumerator>(Session, *this); +} + +void PDBSymbolFunc::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } + +bool PDBSymbolFunc::isDestructor() const { + std::string Name = getName(); + if (Name.empty()) + return false; + if (Name[0] == '~') + return true; + if (Name == "__vecDelDtor") + return true; + return false; +} + +std::unique_ptr<IPDBEnumLineNumbers> PDBSymbolFunc::getLineNumbers() const { + auto Len = RawSymbol->getLength(); + return Session.findLineNumbersByAddress(RawSymbol->getVirtualAddress(), + Len ? Len : 1); +} + +uint32_t PDBSymbolFunc::getCompilandId() const { + if (auto Lines = getLineNumbers()) { + if (auto FirstLine = Lines->getNext()) { + return FirstLine->getCompilandId(); + } + } + return 0; +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp new file mode 100644 index 000000000000..66433dc17b49 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolFuncDebugEnd.cpp - ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolFuncDebugEnd::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp new file mode 100644 index 000000000000..fe32c93c0121 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolFuncDebugStart.cpp - ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolFuncDebugStart::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolLabel.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolLabel.cpp new file mode 100644 index 000000000000..1fffe69a0c83 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolLabel.cpp @@ -0,0 +1,18 @@ +//===- PDBSymbolLabel.cpp - -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolLabel::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp new file mode 100644 index 000000000000..08697683f641 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolPublicSymbol.cpp - ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolPublicSymbol::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolThunk.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolThunk.cpp new file mode 100644 index 000000000000..6483858183e5 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolThunk.cpp @@ -0,0 +1,18 @@ +//===- PDBSymbolThunk.cpp - -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolThunk::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp new file mode 100644 index 000000000000..a0d521abe43f --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp @@ -0,0 +1,24 @@ +//===- PDBSymbolTypeArray.cpp - ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeArray::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +void PDBSymbolTypeArray::dumpRight(PDBSymDumper &Dumper) const { + Dumper.dumpRight(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp new file mode 100644 index 000000000000..08467059b5e1 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolTypeBaseClass.cpp - -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeBaseClass::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp new file mode 100644 index 000000000000..a0dd9ef601c0 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp @@ -0,0 +1,20 @@ +//===- PDBSymbolTypeBuiltin.cpp - ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeBuiltin::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp new file mode 100644 index 000000000000..6723894c90ea --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolTypeCustom.cpp - --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeCustom.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeCustom::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp new file mode 100644 index 000000000000..4a25a391f278 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolTypeDimension.cpp - --------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeDimension.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; +void PDBSymbolTypeDimension::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp new file mode 100644 index 000000000000..b9fdf6aec811 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp @@ -0,0 +1,19 @@ +//===- PDBSymbolTypeEnum.cpp - --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeEnum::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp new file mode 100644 index 000000000000..4ffea42cbb0a --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolTypeFriend.cpp - --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFriend.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeFriend::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp new file mode 100644 index 000000000000..683e93548fb1 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp @@ -0,0 +1,20 @@ +//===- PDBSymbolTypeFunctionArg.cpp - --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeFunctionArg::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp new file mode 100644 index 000000000000..1373615522eb --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp @@ -0,0 +1,93 @@ +//===- PDBSymbolTypeFunctionSig.cpp - --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" + +#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +class FunctionArgEnumerator : public IPDBEnumSymbols { +public: + typedef ConcreteSymbolEnumerator<PDBSymbolTypeFunctionArg> ArgEnumeratorType; + + FunctionArgEnumerator(const IPDBSession &PDBSession, + const PDBSymbolTypeFunctionSig &Sig) + : Session(PDBSession), + Enumerator(Sig.findAllChildren<PDBSymbolTypeFunctionArg>()) {} + + FunctionArgEnumerator(const IPDBSession &PDBSession, + std::unique_ptr<ArgEnumeratorType> ArgEnumerator) + : Session(PDBSession), Enumerator(std::move(ArgEnumerator)) {} + + uint32_t getChildCount() const override { + return Enumerator->getChildCount(); + } + + std::unique_ptr<PDBSymbol> getChildAtIndex(uint32_t Index) const override { + auto FunctionArgSymbol = Enumerator->getChildAtIndex(Index); + if (!FunctionArgSymbol) + return nullptr; + return Session.getSymbolById(FunctionArgSymbol->getTypeId()); + } + + std::unique_ptr<PDBSymbol> getNext() override { + auto FunctionArgSymbol = Enumerator->getNext(); + if (!FunctionArgSymbol) + return nullptr; + return Session.getSymbolById(FunctionArgSymbol->getTypeId()); + } + + void reset() override { Enumerator->reset(); } + +private: + const IPDBSession &Session; + std::unique_ptr<ArgEnumeratorType> Enumerator; +}; +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbolTypeFunctionSig::getArguments() const { + return std::make_unique<FunctionArgEnumerator>(Session, *this); +} + +void PDBSymbolTypeFunctionSig::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +void PDBSymbolTypeFunctionSig::dumpRight(PDBSymDumper &Dumper) const { + Dumper.dumpRight(*this); +} + +bool PDBSymbolTypeFunctionSig::isCVarArgs() const { + auto SigArguments = getArguments(); + if (!SigArguments) + return false; + uint32_t NumArgs = SigArguments->getChildCount(); + if (NumArgs == 0) + return false; + auto Last = SigArguments->getChildAtIndex(NumArgs - 1); + if (auto Builtin = llvm::dyn_cast_or_null<PDBSymbolTypeBuiltin>(Last.get())) { + if (Builtin->getBuiltinType() == PDB_BuiltinType::None) + return true; + } + + // Note that for a variadic template signature, this method always returns + // false since the parameters of the template are specialized. + return false; +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp new file mode 100644 index 000000000000..e80e6c716572 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp @@ -0,0 +1,21 @@ +//===- PDBSymboTypelManaged.cpp - ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeManaged.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeManaged::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp new file mode 100644 index 000000000000..462fc315359b --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp @@ -0,0 +1,25 @@ +//===- PDBSymbolTypePointer.cpp -----------------------------------*- C++ -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypePointer::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +void PDBSymbolTypePointer::dumpRight(PDBSymDumper &Dumper) const { + Dumper.dumpRight(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp new file mode 100644 index 000000000000..70749d9bf5f5 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp @@ -0,0 +1,20 @@ +//===- PDBSymbolTypeTypedef.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeTypedef::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp new file mode 100644 index 000000000000..d302c29a3bec --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp @@ -0,0 +1,25 @@ +//===- PDBSymbolTypeUDT.cpp - --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeUDT::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp new file mode 100644 index 000000000000..4e2a45116d51 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp @@ -0,0 +1,20 @@ +//===- PDBSymbolTypeVTable.cpp - --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeVTable::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp new file mode 100644 index 000000000000..78957620e083 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolTypeVTableShape.cpp - ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolTypeVTableShape::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp new file mode 100644 index 000000000000..650d01183171 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp @@ -0,0 +1,19 @@ +//===- PDBSymbolUnknown.cpp - -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolUnknown::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/llvm/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp b/llvm/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp new file mode 100644 index 000000000000..74afbdb18086 --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp @@ -0,0 +1,21 @@ +//===- PDBSymbolUsingNamespace.cpp - ------------------- --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolUsingNamespace.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +void PDBSymbolUsingNamespace::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/llvm/lib/DebugInfo/PDB/UDTLayout.cpp b/llvm/lib/DebugInfo/PDB/UDTLayout.cpp new file mode 100644 index 000000000000..a8e1d0a619ca --- /dev/null +++ b/llvm/lib/DebugInfo/PDB/UDTLayout.cpp @@ -0,0 +1,302 @@ +//===- UDTLayout.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Casting.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <memory> + +using namespace llvm; +using namespace llvm::pdb; + +static std::unique_ptr<PDBSymbol> getSymbolType(const PDBSymbol &Symbol) { + const IPDBSession &Session = Symbol.getSession(); + const IPDBRawSymbol &RawSymbol = Symbol.getRawSymbol(); + uint32_t TypeId = RawSymbol.getTypeId(); + return Session.getSymbolById(TypeId); +} + +static uint32_t getTypeLength(const PDBSymbol &Symbol) { + auto SymbolType = getSymbolType(Symbol); + const IPDBRawSymbol &RawType = SymbolType->getRawSymbol(); + + return RawType.getLength(); +} + +LayoutItemBase::LayoutItemBase(const UDTLayoutBase *Parent, + const PDBSymbol *Symbol, const std::string &Name, + uint32_t OffsetInParent, uint32_t Size, + bool IsElided) + : Symbol(Symbol), Parent(Parent), Name(Name), + OffsetInParent(OffsetInParent), SizeOf(Size), LayoutSize(Size), + IsElided(IsElided) { + UsedBytes.resize(SizeOf, true); +} + +uint32_t LayoutItemBase::deepPaddingSize() const { + return UsedBytes.size() - UsedBytes.count(); +} + +uint32_t LayoutItemBase::tailPadding() const { + int Last = UsedBytes.find_last(); + + return UsedBytes.size() - (Last + 1); +} + +DataMemberLayoutItem::DataMemberLayoutItem( + const UDTLayoutBase &Parent, std::unique_ptr<PDBSymbolData> Member) + : LayoutItemBase(&Parent, Member.get(), Member->getName(), + Member->getOffset(), getTypeLength(*Member), false), + DataMember(std::move(Member)) { + auto Type = DataMember->getType(); + if (auto UDT = unique_dyn_cast<PDBSymbolTypeUDT>(Type)) { + UdtLayout = std::make_unique<ClassLayout>(std::move(UDT)); + UsedBytes = UdtLayout->usedBytes(); + } +} + +VBPtrLayoutItem::VBPtrLayoutItem(const UDTLayoutBase &Parent, + std::unique_ptr<PDBSymbolTypeBuiltin> Sym, + uint32_t Offset, uint32_t Size) + : LayoutItemBase(&Parent, Sym.get(), "<vbptr>", Offset, Size, false), + Type(std::move(Sym)) { +} + +const PDBSymbolData &DataMemberLayoutItem::getDataMember() { + return *cast<PDBSymbolData>(Symbol); +} + +bool DataMemberLayoutItem::hasUDTLayout() const { return UdtLayout != nullptr; } + +const ClassLayout &DataMemberLayoutItem::getUDTLayout() const { + return *UdtLayout; +} + +VTableLayoutItem::VTableLayoutItem(const UDTLayoutBase &Parent, + std::unique_ptr<PDBSymbolTypeVTable> VT) + : LayoutItemBase(&Parent, VT.get(), "<vtbl>", 0, getTypeLength(*VT), false), + VTable(std::move(VT)) { + auto VTableType = cast<PDBSymbolTypePointer>(VTable->getType()); + ElementSize = VTableType->getLength(); +} + +UDTLayoutBase::UDTLayoutBase(const UDTLayoutBase *Parent, const PDBSymbol &Sym, + const std::string &Name, uint32_t OffsetInParent, + uint32_t Size, bool IsElided) + : LayoutItemBase(Parent, &Sym, Name, OffsetInParent, Size, IsElided) { + // UDT storage comes from a union of all the children's storage, so start out + // uninitialized. + UsedBytes.reset(0, Size); + + initializeChildren(Sym); + if (LayoutSize < Size) + UsedBytes.resize(LayoutSize); +} + +uint32_t UDTLayoutBase::tailPadding() const { + uint32_t Abs = LayoutItemBase::tailPadding(); + if (!LayoutItems.empty()) { + const LayoutItemBase *Back = LayoutItems.back(); + uint32_t ChildPadding = Back->LayoutItemBase::tailPadding(); + if (Abs < ChildPadding) + Abs = 0; + else + Abs -= ChildPadding; + } + return Abs; +} + +ClassLayout::ClassLayout(const PDBSymbolTypeUDT &UDT) + : UDTLayoutBase(nullptr, UDT, UDT.getName(), 0, UDT.getLength(), false), + UDT(UDT) { + ImmediateUsedBytes.resize(SizeOf, false); + for (auto &LI : LayoutItems) { + uint32_t Begin = LI->getOffsetInParent(); + uint32_t End = Begin + LI->getLayoutSize(); + End = std::min(SizeOf, End); + ImmediateUsedBytes.set(Begin, End); + } +} + +ClassLayout::ClassLayout(std::unique_ptr<PDBSymbolTypeUDT> UDT) + : ClassLayout(*UDT) { + OwnedStorage = std::move(UDT); +} + +uint32_t ClassLayout::immediatePadding() const { + return SizeOf - ImmediateUsedBytes.count(); +} + +BaseClassLayout::BaseClassLayout(const UDTLayoutBase &Parent, + uint32_t OffsetInParent, bool Elide, + std::unique_ptr<PDBSymbolTypeBaseClass> B) + : UDTLayoutBase(&Parent, *B, B->getName(), OffsetInParent, B->getLength(), + Elide), + Base(std::move(B)) { + if (isEmptyBase()) { + // Special case an empty base so that it doesn't get treated as padding. + UsedBytes.resize(1); + UsedBytes.set(0); + } + IsVirtualBase = Base->isVirtualBaseClass(); +} + +void UDTLayoutBase::initializeChildren(const PDBSymbol &Sym) { + // Handled bases first, followed by VTables, followed by data members, + // followed by functions, followed by other. This ordering is necessary + // so that bases and vtables get initialized before any functions which + // may override them. + UniquePtrVector<PDBSymbolTypeBaseClass> Bases; + UniquePtrVector<PDBSymbolTypeVTable> VTables; + UniquePtrVector<PDBSymbolData> Members; + UniquePtrVector<PDBSymbolTypeBaseClass> VirtualBaseSyms; + + auto Children = Sym.findAllChildren(); + while (auto Child = Children->getNext()) { + if (auto Base = unique_dyn_cast<PDBSymbolTypeBaseClass>(Child)) { + if (Base->isVirtualBaseClass()) + VirtualBaseSyms.push_back(std::move(Base)); + else + Bases.push_back(std::move(Base)); + } + else if (auto Data = unique_dyn_cast<PDBSymbolData>(Child)) { + if (Data->getDataKind() == PDB_DataKind::Member) + Members.push_back(std::move(Data)); + else + Other.push_back(std::move(Data)); + } else if (auto VT = unique_dyn_cast<PDBSymbolTypeVTable>(Child)) + VTables.push_back(std::move(VT)); + else if (auto Func = unique_dyn_cast<PDBSymbolFunc>(Child)) + Funcs.push_back(std::move(Func)); + else { + Other.push_back(std::move(Child)); + } + } + + // We don't want to have any re-allocations in the list of bases, so make + // sure to reserve enough space so that our ArrayRefs don't get invalidated. + AllBases.reserve(Bases.size() + VirtualBaseSyms.size()); + + // Only add non-virtual bases to the class first. Only at the end of the + // class, after all non-virtual bases and data members have been added do we + // add virtual bases. This way the offsets are correctly aligned when we go + // to lay out virtual bases. + for (auto &Base : Bases) { + uint32_t Offset = Base->getOffset(); + // Non-virtual bases never get elided. + auto BL = std::make_unique<BaseClassLayout>(*this, Offset, false, + std::move(Base)); + + AllBases.push_back(BL.get()); + addChildToLayout(std::move(BL)); + } + NonVirtualBases = AllBases; + + assert(VTables.size() <= 1); + if (!VTables.empty()) { + auto VTLayout = + std::make_unique<VTableLayoutItem>(*this, std::move(VTables[0])); + + VTable = VTLayout.get(); + + addChildToLayout(std::move(VTLayout)); + } + + for (auto &Data : Members) { + auto DM = std::make_unique<DataMemberLayoutItem>(*this, std::move(Data)); + + addChildToLayout(std::move(DM)); + } + + // Make sure add virtual bases before adding functions, since functions may be + // overrides of virtual functions declared in a virtual base, so the VTables + // and virtual intros need to be correctly initialized. + for (auto &VB : VirtualBaseSyms) { + int VBPO = VB->getVirtualBasePointerOffset(); + if (!hasVBPtrAtOffset(VBPO)) { + if (auto VBP = VB->getRawSymbol().getVirtualBaseTableType()) { + auto VBPL = std::make_unique<VBPtrLayoutItem>(*this, std::move(VBP), + VBPO, VBP->getLength()); + VBPtr = VBPL.get(); + addChildToLayout(std::move(VBPL)); + } + } + + // Virtual bases always go at the end. So just look for the last place we + // ended when writing something, and put our virtual base there. + // Note that virtual bases get elided unless this is a top-most derived + // class. + uint32_t Offset = UsedBytes.find_last() + 1; + bool Elide = (Parent != nullptr); + auto BL = + std::make_unique<BaseClassLayout>(*this, Offset, Elide, std::move(VB)); + AllBases.push_back(BL.get()); + + // Only lay this virtual base out directly inside of *this* class if this + // is a top-most derived class. Keep track of it regardless, but only + // physically lay it out if it's a topmost derived class. + addChildToLayout(std::move(BL)); + } + VirtualBases = makeArrayRef(AllBases).drop_front(NonVirtualBases.size()); + + if (Parent != nullptr) + LayoutSize = UsedBytes.find_last() + 1; +} + +bool UDTLayoutBase::hasVBPtrAtOffset(uint32_t Off) const { + if (VBPtr && VBPtr->getOffsetInParent() == Off) + return true; + for (BaseClassLayout *BL : AllBases) { + if (BL->hasVBPtrAtOffset(Off - BL->getOffsetInParent())) + return true; + } + return false; +} + +void UDTLayoutBase::addChildToLayout(std::unique_ptr<LayoutItemBase> Child) { + uint32_t Begin = Child->getOffsetInParent(); + + if (!Child->isElided()) { + BitVector ChildBytes = Child->usedBytes(); + + // Suppose the child occupies 4 bytes starting at offset 12 in a 32 byte + // class. When we call ChildBytes.resize(32), the Child's storage will + // still begin at offset 0, so we need to shift it left by offset bytes + // to get it into the right position. + ChildBytes.resize(UsedBytes.size()); + ChildBytes <<= Child->getOffsetInParent(); + UsedBytes |= ChildBytes; + + if (ChildBytes.count() > 0) { + auto Loc = std::upper_bound(LayoutItems.begin(), LayoutItems.end(), Begin, + [](uint32_t Off, const LayoutItemBase *Item) { + return (Off < Item->getOffsetInParent()); + }); + + LayoutItems.insert(Loc, Child.get()); + } + } + + ChildStorage.push_back(std::move(Child)); +} diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp new file mode 100644 index 000000000000..b1a80cbc4580 --- /dev/null +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -0,0 +1,144 @@ +//===- lib/DebugInfo/Symbolize/DIPrinter.cpp ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the DIPrinter class, which is responsible for printing +// structures defined in DebugInfo/DIContext.h +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/Symbolize/DIPrinter.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <string> + +namespace llvm { +namespace symbolize { + +// Prints source code around in the FileName the Line. +void DIPrinter::printContext(const std::string &FileName, int64_t Line) { + if (PrintSourceContext <= 0) + return; + + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return; + + std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get()); + int64_t FirstLine = + std::max(static_cast<int64_t>(1), Line - PrintSourceContext / 2); + int64_t LastLine = FirstLine + PrintSourceContext; + size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine)); + + for (line_iterator I = line_iterator(*Buf, false); + !I.is_at_eof() && I.line_number() <= LastLine; ++I) { + int64_t L = I.line_number(); + if (L >= FirstLine && L <= LastLine) { + OS << format_decimal(L, MaxLineNumberWidth); + if (L == Line) + OS << " >: "; + else + OS << " : "; + OS << *I << "\n"; + } + } +} + +void DIPrinter::print(const DILineInfo &Info, bool Inlined) { + if (PrintFunctionNames) { + std::string FunctionName = Info.FunctionName; + if (FunctionName == DILineInfo::BadString) + FunctionName = DILineInfo::Addr2LineBadString; + + StringRef Delimiter = PrintPretty ? " at " : "\n"; + StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; + OS << Prefix << FunctionName << Delimiter; + } + std::string Filename = Info.FileName; + if (Filename == DILineInfo::BadString) + Filename = DILineInfo::Addr2LineBadString; + else if (Basenames) + Filename = llvm::sys::path::filename(Filename); + if (!Verbose) { + OS << Filename << ":" << Info.Line; + if (Style == OutputStyle::LLVM) + OS << ":" << Info.Column; + OS << "\n"; + printContext(Filename, Info.Line); + return; + } + OS << " Filename: " << Filename << "\n"; + if (Info.StartLine) + OS << "Function start line: " << Info.StartLine << "\n"; + OS << " Line: " << Info.Line << "\n"; + OS << " Column: " << Info.Column << "\n"; + if (Info.Discriminator) + OS << " Discriminator: " << Info.Discriminator << "\n"; +} + +DIPrinter &DIPrinter::operator<<(const DILineInfo &Info) { + print(Info, false); + return *this; +} + +DIPrinter &DIPrinter::operator<<(const DIInliningInfo &Info) { + uint32_t FramesNum = Info.getNumberOfFrames(); + if (FramesNum == 0) { + print(DILineInfo(), false); + return *this; + } + for (uint32_t i = 0; i < FramesNum; i++) + print(Info.getFrame(i), i > 0); + return *this; +} + +DIPrinter &DIPrinter::operator<<(const DIGlobal &Global) { + std::string Name = Global.Name; + if (Name == DILineInfo::BadString) + Name = DILineInfo::Addr2LineBadString; + OS << Name << "\n"; + OS << Global.Start << " " << Global.Size << "\n"; + return *this; +} + +DIPrinter &DIPrinter::operator<<(const DILocal &Local) { + OS << Local.FunctionName << '\n'; + OS << Local.Name << '\n'; + if (Local.DeclFile.empty()) + OS << "??"; + else + OS << Local.DeclFile; + OS << ':' << Local.DeclLine << '\n'; + if (Local.FrameOffset) + OS << *Local.FrameOffset << ' '; + else + OS << "?? "; + if (Local.Size) + OS << *Local.Size << ' '; + else + OS << "?? "; + if (Local.TagOffset) + OS << *Local.TagOffset << '\n'; + else + OS << "??\n"; + return *this; +} + +} // end namespace symbolize +} // end namespace llvm diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp new file mode 100644 index 000000000000..b4d49d9ff958 --- /dev/null +++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp @@ -0,0 +1,331 @@ +//===- SymbolizableObjectFile.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of SymbolizableObjectFile class. +// +//===----------------------------------------------------------------------===// + +#include "SymbolizableObjectFile.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/SymbolSize.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> +#include <memory> +#include <string> +#include <system_error> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace object; +using namespace symbolize; + +static DILineInfoSpecifier +getDILineInfoSpecifier(FunctionNameKind FNKind) { + return DILineInfoSpecifier( + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FNKind); +} + +ErrorOr<std::unique_ptr<SymbolizableObjectFile>> +SymbolizableObjectFile::create(const object::ObjectFile *Obj, + std::unique_ptr<DIContext> DICtx, + bool UntagAddresses) { + assert(DICtx); + std::unique_ptr<SymbolizableObjectFile> res( + new SymbolizableObjectFile(Obj, std::move(DICtx), UntagAddresses)); + std::unique_ptr<DataExtractor> OpdExtractor; + uint64_t OpdAddress = 0; + // Find the .opd (function descriptor) section if any, for big-endian + // PowerPC64 ELF. + if (Obj->getArch() == Triple::ppc64) { + for (section_iterator Section : Obj->sections()) { + Expected<StringRef> NameOrErr = Section->getName(); + if (!NameOrErr) + return errorToErrorCode(NameOrErr.takeError()); + + if (*NameOrErr == ".opd") { + Expected<StringRef> E = Section->getContents(); + if (!E) + return errorToErrorCode(E.takeError()); + OpdExtractor.reset(new DataExtractor(*E, Obj->isLittleEndian(), + Obj->getBytesInAddress())); + OpdAddress = Section->getAddress(); + break; + } + } + } + std::vector<std::pair<SymbolRef, uint64_t>> Symbols = + computeSymbolSizes(*Obj); + for (auto &P : Symbols) + res->addSymbol(P.first, P.second, OpdExtractor.get(), OpdAddress); + + // If this is a COFF object and we didn't find any symbols, try the export + // table. + if (Symbols.empty()) { + if (auto *CoffObj = dyn_cast<COFFObjectFile>(Obj)) + if (auto EC = res->addCoffExportSymbols(CoffObj)) + return EC; + } + + std::vector<std::pair<SymbolDesc, StringRef>> &Fs = res->Functions, + &Os = res->Objects; + auto Uniquify = [](std::vector<std::pair<SymbolDesc, StringRef>> &S) { + // Sort by (Addr,Size,Name). If several SymbolDescs share the same Addr, + // pick the one with the largest Size. This helps us avoid symbols with no + // size information (Size=0). + llvm::sort(S); + auto I = S.begin(), E = S.end(), J = S.begin(); + while (I != E) { + auto OI = I; + while (++I != E && OI->first.Addr == I->first.Addr) { + } + *J++ = I[-1]; + } + S.erase(J, S.end()); + }; + Uniquify(Fs); + Uniquify(Os); + + return std::move(res); +} + +SymbolizableObjectFile::SymbolizableObjectFile(const ObjectFile *Obj, + std::unique_ptr<DIContext> DICtx, + bool UntagAddresses) + : Module(Obj), DebugInfoContext(std::move(DICtx)), + UntagAddresses(UntagAddresses) {} + +namespace { + +struct OffsetNamePair { + uint32_t Offset; + StringRef Name; + + bool operator<(const OffsetNamePair &R) const { + return Offset < R.Offset; + } +}; + +} // end anonymous namespace + +std::error_code SymbolizableObjectFile::addCoffExportSymbols( + const COFFObjectFile *CoffObj) { + // Get all export names and offsets. + std::vector<OffsetNamePair> ExportSyms; + for (const ExportDirectoryEntryRef &Ref : CoffObj->export_directories()) { + StringRef Name; + uint32_t Offset; + if (auto EC = Ref.getSymbolName(Name)) + return EC; + if (auto EC = Ref.getExportRVA(Offset)) + return EC; + ExportSyms.push_back(OffsetNamePair{Offset, Name}); + } + if (ExportSyms.empty()) + return std::error_code(); + + // Sort by ascending offset. + array_pod_sort(ExportSyms.begin(), ExportSyms.end()); + + // Approximate the symbol sizes by assuming they run to the next symbol. + // FIXME: This assumes all exports are functions. + uint64_t ImageBase = CoffObj->getImageBase(); + for (auto I = ExportSyms.begin(), E = ExportSyms.end(); I != E; ++I) { + OffsetNamePair &Export = *I; + // FIXME: The last export has a one byte size now. + uint32_t NextOffset = I != E ? I->Offset : Export.Offset + 1; + uint64_t SymbolStart = ImageBase + Export.Offset; + uint64_t SymbolSize = NextOffset - Export.Offset; + SymbolDesc SD = {SymbolStart, SymbolSize}; + Functions.emplace_back(SD, Export.Name); + } + return std::error_code(); +} + +std::error_code SymbolizableObjectFile::addSymbol(const SymbolRef &Symbol, + uint64_t SymbolSize, + DataExtractor *OpdExtractor, + uint64_t OpdAddress) { + // Avoid adding symbols from an unknown/undefined section. + const ObjectFile *Obj = Symbol.getObject(); + Expected<section_iterator> Sec = Symbol.getSection(); + if (!Sec || (Obj && Obj->section_end() == *Sec)) + return std::error_code(); + Expected<SymbolRef::Type> SymbolTypeOrErr = Symbol.getType(); + if (!SymbolTypeOrErr) + return errorToErrorCode(SymbolTypeOrErr.takeError()); + SymbolRef::Type SymbolType = *SymbolTypeOrErr; + if (SymbolType != SymbolRef::ST_Function && SymbolType != SymbolRef::ST_Data) + return std::error_code(); + Expected<uint64_t> SymbolAddressOrErr = Symbol.getAddress(); + if (!SymbolAddressOrErr) + return errorToErrorCode(SymbolAddressOrErr.takeError()); + uint64_t SymbolAddress = *SymbolAddressOrErr; + if (UntagAddresses) { + // For kernel addresses, bits 56-63 need to be set, so we sign extend bit 55 + // into bits 56-63 instead of masking them out. + SymbolAddress &= (1ull << 56) - 1; + SymbolAddress = (int64_t(SymbolAddress) << 8) >> 8; + } + if (OpdExtractor) { + // For big-endian PowerPC64 ELF, symbols in the .opd section refer to + // function descriptors. The first word of the descriptor is a pointer to + // the function's code. + // For the purposes of symbolization, pretend the symbol's address is that + // of the function's code, not the descriptor. + uint64_t OpdOffset = SymbolAddress - OpdAddress; + if (OpdExtractor->isValidOffsetForAddress(OpdOffset)) + SymbolAddress = OpdExtractor->getAddress(&OpdOffset); + } + Expected<StringRef> SymbolNameOrErr = Symbol.getName(); + if (!SymbolNameOrErr) + return errorToErrorCode(SymbolNameOrErr.takeError()); + StringRef SymbolName = *SymbolNameOrErr; + // Mach-O symbol table names have leading underscore, skip it. + if (Module->isMachO() && !SymbolName.empty() && SymbolName[0] == '_') + SymbolName = SymbolName.drop_front(); + // FIXME: If a function has alias, there are two entries in symbol table + // with same address size. Make sure we choose the correct one. + auto &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects; + SymbolDesc SD = { SymbolAddress, SymbolSize }; + M.emplace_back(SD, SymbolName); + return std::error_code(); +} + +// Return true if this is a 32-bit x86 PE COFF module. +bool SymbolizableObjectFile::isWin32Module() const { + auto *CoffObject = dyn_cast<COFFObjectFile>(Module); + return CoffObject && CoffObject->getMachine() == COFF::IMAGE_FILE_MACHINE_I386; +} + +uint64_t SymbolizableObjectFile::getModulePreferredBase() const { + if (auto *CoffObject = dyn_cast<COFFObjectFile>(Module)) + return CoffObject->getImageBase(); + return 0; +} + +bool SymbolizableObjectFile::getNameFromSymbolTable(SymbolRef::Type Type, + uint64_t Address, + std::string &Name, + uint64_t &Addr, + uint64_t &Size) const { + const auto &Symbols = Type == SymbolRef::ST_Function ? Functions : Objects; + std::pair<SymbolDesc, StringRef> SD{{Address, UINT64_C(-1)}, StringRef()}; + auto SymbolIterator = llvm::upper_bound(Symbols, SD); + if (SymbolIterator == Symbols.begin()) + return false; + --SymbolIterator; + if (SymbolIterator->first.Size != 0 && + SymbolIterator->first.Addr + SymbolIterator->first.Size <= Address) + return false; + Name = SymbolIterator->second.str(); + Addr = SymbolIterator->first.Addr; + Size = SymbolIterator->first.Size; + return true; +} + +bool SymbolizableObjectFile::shouldOverrideWithSymbolTable( + FunctionNameKind FNKind, bool UseSymbolTable) const { + // When DWARF is used with -gline-tables-only / -gmlt, the symbol table gives + // better answers for linkage names than the DIContext. Otherwise, we are + // probably using PEs and PDBs, and we shouldn't do the override. PE files + // generally only contain the names of exported symbols. + return FNKind == FunctionNameKind::LinkageName && UseSymbolTable && + isa<DWARFContext>(DebugInfoContext.get()); +} + +DILineInfo +SymbolizableObjectFile::symbolizeCode(object::SectionedAddress ModuleOffset, + FunctionNameKind FNKind, + bool UseSymbolTable) const { + if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection) + ModuleOffset.SectionIndex = + getModuleSectionIndexForAddress(ModuleOffset.Address); + DILineInfo LineInfo = DebugInfoContext->getLineInfoForAddress( + ModuleOffset, getDILineInfoSpecifier(FNKind)); + + // Override function name from symbol table if necessary. + if (shouldOverrideWithSymbolTable(FNKind, UseSymbolTable)) { + std::string FunctionName; + uint64_t Start, Size; + if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset.Address, + FunctionName, Start, Size)) { + LineInfo.FunctionName = FunctionName; + } + } + return LineInfo; +} + +DIInliningInfo SymbolizableObjectFile::symbolizeInlinedCode( + object::SectionedAddress ModuleOffset, FunctionNameKind FNKind, + bool UseSymbolTable) const { + if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection) + ModuleOffset.SectionIndex = + getModuleSectionIndexForAddress(ModuleOffset.Address); + DIInliningInfo InlinedContext = DebugInfoContext->getInliningInfoForAddress( + ModuleOffset, getDILineInfoSpecifier(FNKind)); + + // Make sure there is at least one frame in context. + if (InlinedContext.getNumberOfFrames() == 0) + InlinedContext.addFrame(DILineInfo()); + + // Override the function name in lower frame with name from symbol table. + if (shouldOverrideWithSymbolTable(FNKind, UseSymbolTable)) { + std::string FunctionName; + uint64_t Start, Size; + if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset.Address, + FunctionName, Start, Size)) { + InlinedContext.getMutableFrame(InlinedContext.getNumberOfFrames() - 1) + ->FunctionName = FunctionName; + } + } + + return InlinedContext; +} + +DIGlobal SymbolizableObjectFile::symbolizeData( + object::SectionedAddress ModuleOffset) const { + DIGlobal Res; + getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset.Address, Res.Name, + Res.Start, Res.Size); + return Res; +} + +std::vector<DILocal> SymbolizableObjectFile::symbolizeFrame( + object::SectionedAddress ModuleOffset) const { + if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection) + ModuleOffset.SectionIndex = + getModuleSectionIndexForAddress(ModuleOffset.Address); + return DebugInfoContext->getLocalsForAddress(ModuleOffset); +} + +/// Search for the first occurence of specified Address in ObjectFile. +uint64_t SymbolizableObjectFile::getModuleSectionIndexForAddress( + uint64_t Address) const { + + for (SectionRef Sec : Module->sections()) { + if (!Sec.isText() || Sec.isVirtual()) + continue; + + if (Address >= Sec.getAddress() && + Address < Sec.getAddress() + Sec.getSize()) + return Sec.getIndex(); + } + + return object::SectionedAddress::UndefSection; +} diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h new file mode 100644 index 000000000000..b5b9793a44d9 --- /dev/null +++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h @@ -0,0 +1,98 @@ +//===- SymbolizableObjectFile.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the SymbolizableObjectFile class. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIB_DEBUGINFO_SYMBOLIZE_SYMBOLIZABLEOBJECTFILE_H +#define LLVM_LIB_DEBUGINFO_SYMBOLIZE_SYMBOLIZABLEOBJECTFILE_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" +#include "llvm/Support/ErrorOr.h" +#include <cstdint> +#include <map> +#include <memory> +#include <string> +#include <system_error> + +namespace llvm { + +class DataExtractor; + +namespace symbolize { + +class SymbolizableObjectFile : public SymbolizableModule { +public: + static ErrorOr<std::unique_ptr<SymbolizableObjectFile>> + create(const object::ObjectFile *Obj, std::unique_ptr<DIContext> DICtx, + bool UntagAddresses); + + DILineInfo symbolizeCode(object::SectionedAddress ModuleOffset, + FunctionNameKind FNKind, + bool UseSymbolTable) const override; + DIInliningInfo symbolizeInlinedCode(object::SectionedAddress ModuleOffset, + FunctionNameKind FNKind, + bool UseSymbolTable) const override; + DIGlobal symbolizeData(object::SectionedAddress ModuleOffset) const override; + std::vector<DILocal> + symbolizeFrame(object::SectionedAddress ModuleOffset) const override; + + // Return true if this is a 32-bit x86 PE COFF module. + bool isWin32Module() const override; + + // Returns the preferred base of the module, i.e. where the loader would place + // it in memory assuming there were no conflicts. + uint64_t getModulePreferredBase() const override; + +private: + bool shouldOverrideWithSymbolTable(FunctionNameKind FNKind, + bool UseSymbolTable) const; + + bool getNameFromSymbolTable(object::SymbolRef::Type Type, uint64_t Address, + std::string &Name, uint64_t &Addr, + uint64_t &Size) const; + // For big-endian PowerPC64 ELF, OpdAddress is the address of the .opd + // (function descriptor) section and OpdExtractor refers to its contents. + std::error_code addSymbol(const object::SymbolRef &Symbol, + uint64_t SymbolSize, + DataExtractor *OpdExtractor = nullptr, + uint64_t OpdAddress = 0); + std::error_code addCoffExportSymbols(const object::COFFObjectFile *CoffObj); + + /// Search for the first occurence of specified Address in ObjectFile. + uint64_t getModuleSectionIndexForAddress(uint64_t Address) const; + + const object::ObjectFile *Module; + std::unique_ptr<DIContext> DebugInfoContext; + bool UntagAddresses; + + struct SymbolDesc { + uint64_t Addr; + // If size is 0, assume that symbol occupies the whole memory range up to + // the following symbol. + uint64_t Size; + + bool operator<(const SymbolDesc &RHS) const { + return Addr != RHS.Addr ? Addr < RHS.Addr : Size < RHS.Size; + } + }; + std::vector<std::pair<SymbolDesc, StringRef>> Functions; + std::vector<std::pair<SymbolDesc, StringRef>> Objects; + + SymbolizableObjectFile(const object::ObjectFile *Obj, + std::unique_ptr<DIContext> DICtx, + bool UntagAddresses); +}; + +} // end namespace symbolize + +} // end namespace llvm + +#endif // LLVM_LIB_DEBUGINFO_SYMBOLIZE_SYMBOLIZABLEOBJECTFILE_H diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp new file mode 100644 index 000000000000..be79d9e637c1 --- /dev/null +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -0,0 +1,544 @@ +//===-- LLVMSymbolize.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation for LLVM symbolization library. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/Symbolize/Symbolize.h" + +#include "SymbolizableObjectFile.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBContext.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <cassert> +#include <cstring> + +namespace llvm { +namespace symbolize { + +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCodeCommon(SymbolizableModule *Info, + object::SectionedAddress ModuleOffset) { + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return DILineInfo(); + + // If the user is giving us relative addresses, add the preferred base of the + // object to the offset before we do the query. It's what DIContext expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts.PrintFunctions, + Opts.UseSymbolTable); + if (Opts.Demangle) + LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info); + return LineInfo; +} + +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj, + object::SectionedAddress ModuleOffset) { + StringRef ModuleName = Obj.getFileName(); + auto I = Modules.find(ModuleName); + if (I != Modules.end()) + return symbolizeCodeCommon(I->second.get(), ModuleOffset); + + std::unique_ptr<DIContext> Context = + DWARFContext::create(Obj, nullptr, DWARFContext::defaultErrorHandler); + Expected<SymbolizableModule *> InfoOrErr = + createModuleInfo(&Obj, std::move(Context), ModuleName); + if (!InfoOrErr) + return InfoOrErr.takeError(); + return symbolizeCodeCommon(*InfoOrErr, ModuleOffset); +} + +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + Expected<SymbolizableModule *> InfoOrErr = getOrCreateModuleInfo(ModuleName); + if (!InfoOrErr) + return InfoOrErr.takeError(); + return symbolizeCodeCommon(*InfoOrErr, ModuleOffset); +} + +Expected<DIInliningInfo> +LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + SymbolizableModule *Info; + if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) + Info = InfoOrErr.get(); + else + return InfoOrErr.takeError(); + + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return DIInliningInfo(); + + // If the user is giving us relative addresses, add the preferred base of the + // object to the offset before we do the query. It's what DIContext expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + DIInliningInfo InlinedContext = Info->symbolizeInlinedCode( + ModuleOffset, Opts.PrintFunctions, Opts.UseSymbolTable); + if (Opts.Demangle) { + for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) { + auto *Frame = InlinedContext.getMutableFrame(i); + Frame->FunctionName = DemangleName(Frame->FunctionName, Info); + } + } + return InlinedContext; +} + +Expected<DIGlobal> +LLVMSymbolizer::symbolizeData(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + SymbolizableModule *Info; + if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) + Info = InfoOrErr.get(); + else + return InfoOrErr.takeError(); + + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return DIGlobal(); + + // If the user is giving us relative addresses, add the preferred base of + // the object to the offset before we do the query. It's what DIContext + // expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + DIGlobal Global = Info->symbolizeData(ModuleOffset); + if (Opts.Demangle) + Global.Name = DemangleName(Global.Name, Info); + return Global; +} + +Expected<std::vector<DILocal>> +LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + SymbolizableModule *Info; + if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName)) + Info = InfoOrErr.get(); + else + return InfoOrErr.takeError(); + + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return std::vector<DILocal>(); + + // If the user is giving us relative addresses, add the preferred base of + // the object to the offset before we do the query. It's what DIContext + // expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + return Info->symbolizeFrame(ModuleOffset); +} + +void LLVMSymbolizer::flush() { + ObjectForUBPathAndArch.clear(); + BinaryForPath.clear(); + ObjectPairForPathArch.clear(); + Modules.clear(); +} + +namespace { + +// For Path="/path/to/foo" and Basename="foo" assume that debug info is in +// /path/to/foo.dSYM/Contents/Resources/DWARF/foo. +// For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in +// /path/to/bar.dSYM/Contents/Resources/DWARF/foo. +std::string getDarwinDWARFResourceForPath( + const std::string &Path, const std::string &Basename) { + SmallString<16> ResourceName = StringRef(Path); + if (sys::path::extension(Path) != ".dSYM") { + ResourceName += ".dSYM"; + } + sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); + sys::path::append(ResourceName, Basename); + return ResourceName.str(); +} + +bool checkFileCRC(StringRef Path, uint32_t CRCHash) { + ErrorOr<std::unique_ptr<MemoryBuffer>> MB = + MemoryBuffer::getFileOrSTDIN(Path); + if (!MB) + return false; + return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer())); +} + +bool findDebugBinary(const std::string &OrigPath, + const std::string &DebuglinkName, uint32_t CRCHash, + const std::string &FallbackDebugPath, + std::string &Result) { + SmallString<16> OrigDir(OrigPath); + llvm::sys::path::remove_filename(OrigDir); + SmallString<16> DebugPath = OrigDir; + // Try relative/path/to/original_binary/debuglink_name + llvm::sys::path::append(DebugPath, DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = DebugPath.str(); + return true; + } + // Try relative/path/to/original_binary/.debug/debuglink_name + DebugPath = OrigDir; + llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = DebugPath.str(); + return true; + } + // Make the path absolute so that lookups will go to + // "/usr/lib/debug/full/path/to/debug", not + // "/usr/lib/debug/to/debug" + llvm::sys::fs::make_absolute(OrigDir); + if (!FallbackDebugPath.empty()) { + // Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name + DebugPath = FallbackDebugPath; + } else { +#if defined(__NetBSD__) + // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name + DebugPath = "/usr/libdata/debug"; +#else + // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name + DebugPath = "/usr/lib/debug"; +#endif + } + llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), + DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = DebugPath.str(); + return true; + } + return false; +} + +bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, + uint32_t &CRCHash) { + if (!Obj) + return false; + for (const SectionRef &Section : Obj->sections()) { + StringRef Name; + if (Expected<StringRef> NameOrErr = Section.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + Name = Name.substr(Name.find_first_not_of("._")); + if (Name == "gnu_debuglink") { + Expected<StringRef> ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) { + consumeError(ContentsOrErr.takeError()); + return false; + } + DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0); + uint64_t Offset = 0; + if (const char *DebugNameStr = DE.getCStr(&Offset)) { + // 4-byte align the offset. + Offset = (Offset + 3) & ~0x3; + if (DE.isValidOffsetForDataOfSize(Offset, 4)) { + DebugName = DebugNameStr; + CRCHash = DE.getU32(&Offset); + return true; + } + } + break; + } + } + return false; +} + +bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, + const MachOObjectFile *Obj) { + ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid(); + ArrayRef<uint8_t> bin_uuid = Obj->getUuid(); + if (dbg_uuid.empty() || bin_uuid.empty()) + return false; + return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); +} + +} // end anonymous namespace + +ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, + const MachOObjectFile *MachExeObj, const std::string &ArchName) { + // On Darwin we may find DWARF in separate object file in + // resource directory. + std::vector<std::string> DsymPaths; + StringRef Filename = sys::path::filename(ExePath); + DsymPaths.push_back(getDarwinDWARFResourceForPath(ExePath, Filename)); + for (const auto &Path : Opts.DsymHints) { + DsymPaths.push_back(getDarwinDWARFResourceForPath(Path, Filename)); + } + for (const auto &Path : DsymPaths) { + auto DbgObjOrErr = getOrCreateObject(Path, ArchName); + if (!DbgObjOrErr) { + // Ignore errors, the file might not exist. + consumeError(DbgObjOrErr.takeError()); + continue; + } + ObjectFile *DbgObj = DbgObjOrErr.get(); + if (!DbgObj) + continue; + const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj); + if (!MachDbgObj) + continue; + if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) + return DbgObj; + } + return nullptr; +} + +ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path, + const ObjectFile *Obj, + const std::string &ArchName) { + std::string DebuglinkName; + uint32_t CRCHash; + std::string DebugBinaryPath; + if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash)) + return nullptr; + if (!findDebugBinary(Path, DebuglinkName, CRCHash, Opts.FallbackDebugPath, + DebugBinaryPath)) + return nullptr; + auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); + if (!DbgObjOrErr) { + // Ignore errors, the file might not exist. + consumeError(DbgObjOrErr.takeError()); + return nullptr; + } + return DbgObjOrErr.get(); +} + +Expected<LLVMSymbolizer::ObjectPair> +LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, + const std::string &ArchName) { + auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectPairForPathArch.end()) + return I->second; + + auto ObjOrErr = getOrCreateObject(Path, ArchName); + if (!ObjOrErr) { + ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), + ObjectPair(nullptr, nullptr)); + return ObjOrErr.takeError(); + } + + ObjectFile *Obj = ObjOrErr.get(); + assert(Obj != nullptr); + ObjectFile *DbgObj = nullptr; + + if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj)) + DbgObj = lookUpDsymFile(Path, MachObj, ArchName); + if (!DbgObj) + DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName); + if (!DbgObj) + DbgObj = Obj; + ObjectPair Res = std::make_pair(Obj, DbgObj); + ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res); + return Res; +} + +Expected<ObjectFile *> +LLVMSymbolizer::getOrCreateObject(const std::string &Path, + const std::string &ArchName) { + Binary *Bin; + auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>()); + if (!Pair.second) { + Bin = Pair.first->second.getBinary(); + } else { + Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path); + if (!BinOrErr) + return BinOrErr.takeError(); + Pair.first->second = std::move(BinOrErr.get()); + Bin = Pair.first->second.getBinary(); + } + + if (!Bin) + return static_cast<ObjectFile *>(nullptr); + + if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) { + auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectForUBPathAndArch.end()) + return I->second.get(); + + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = + UB->getMachOObjectForArch(ArchName); + if (!ObjOrErr) { + ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), + std::unique_ptr<ObjectFile>()); + return ObjOrErr.takeError(); + } + ObjectFile *Res = ObjOrErr->get(); + ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), + std::move(ObjOrErr.get())); + return Res; + } + if (Bin->isObject()) { + return cast<ObjectFile>(Bin); + } + return errorCodeToError(object_error::arch_not_found); +} + +Expected<SymbolizableModule *> +LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj, + std::unique_ptr<DIContext> Context, + StringRef ModuleName) { + auto InfoOrErr = SymbolizableObjectFile::create(Obj, std::move(Context), + Opts.UntagAddresses); + std::unique_ptr<SymbolizableModule> SymMod; + if (InfoOrErr) + SymMod = std::move(*InfoOrErr); + auto InsertResult = + Modules.insert(std::make_pair(ModuleName, std::move(SymMod))); + assert(InsertResult.second); + if (std::error_code EC = InfoOrErr.getError()) + return errorCodeToError(EC); + return InsertResult.first->second.get(); +} + +Expected<SymbolizableModule *> +LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { + auto I = Modules.find(ModuleName); + if (I != Modules.end()) + return I->second.get(); + + std::string BinaryName = ModuleName; + std::string ArchName = Opts.DefaultArch; + size_t ColonPos = ModuleName.find_last_of(':'); + // Verify that substring after colon form a valid arch name. + if (ColonPos != std::string::npos) { + std::string ArchStr = ModuleName.substr(ColonPos + 1); + if (Triple(ArchStr).getArch() != Triple::UnknownArch) { + BinaryName = ModuleName.substr(0, ColonPos); + ArchName = ArchStr; + } + } + auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName); + if (!ObjectsOrErr) { + // Failed to find valid object file. + Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); + return ObjectsOrErr.takeError(); + } + ObjectPair Objects = ObjectsOrErr.get(); + + std::unique_ptr<DIContext> Context; + // If this is a COFF object containing PDB info, use a PDBContext to + // symbolize. Otherwise, use DWARF. + if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) { + const codeview::DebugInfo *DebugInfo; + StringRef PDBFileName; + auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName); + if (!EC && DebugInfo != nullptr && !PDBFileName.empty()) { + using namespace pdb; + std::unique_ptr<IPDBSession> Session; + if (auto Err = loadDataForEXE(PDB_ReaderType::DIA, + Objects.first->getFileName(), Session)) { + Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); + // Return along the PDB filename to provide more context + return createFileError(PDBFileName, std::move(Err)); + } + Context.reset(new PDBContext(*CoffObject, std::move(Session))); + } + } + if (!Context) + Context = + DWARFContext::create(*Objects.second, nullptr, + DWARFContext::defaultErrorHandler, Opts.DWPName); + return createModuleInfo(Objects.first, std::move(Context), ModuleName); +} + +namespace { + +// Undo these various manglings for Win32 extern "C" functions: +// cdecl - _foo +// stdcall - _foo@12 +// fastcall - @foo@12 +// vectorcall - foo@@12 +// These are all different linkage names for 'foo'. +StringRef demanglePE32ExternCFunc(StringRef SymbolName) { + // Remove any '_' or '@' prefix. + char Front = SymbolName.empty() ? '\0' : SymbolName[0]; + if (Front == '_' || Front == '@') + SymbolName = SymbolName.drop_front(); + + // Remove any '@[0-9]+' suffix. + if (Front != '?') { + size_t AtPos = SymbolName.rfind('@'); + if (AtPos != StringRef::npos && + std::all_of(SymbolName.begin() + AtPos + 1, SymbolName.end(), + [](char C) { return C >= '0' && C <= '9'; })) { + SymbolName = SymbolName.substr(0, AtPos); + } + } + + // Remove any ending '@' for vectorcall. + if (SymbolName.endswith("@")) + SymbolName = SymbolName.drop_back(); + + return SymbolName; +} + +} // end anonymous namespace + +std::string +LLVMSymbolizer::DemangleName(const std::string &Name, + const SymbolizableModule *DbiModuleDescriptor) { + // We can spoil names of symbols with C linkage, so use an heuristic + // approach to check if the name should be demangled. + if (Name.substr(0, 2) == "_Z") { + int status = 0; + char *DemangledName = itaniumDemangle(Name.c_str(), nullptr, nullptr, &status); + if (status != 0) + return Name; + std::string Result = DemangledName; + free(DemangledName); + return Result; + } + + if (!Name.empty() && Name.front() == '?') { + // Only do MSVC C++ demangling on symbols starting with '?'. + int status = 0; + char *DemangledName = microsoftDemangle( + Name.c_str(), nullptr, nullptr, &status, + MSDemangleFlags(MSDF_NoAccessSpecifier | MSDF_NoCallingConvention | + MSDF_NoMemberType | MSDF_NoReturnType)); + if (status != 0) + return Name; + std::string Result = DemangledName; + free(DemangledName); + return Result; + } + + if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) + return std::string(demanglePE32ExternCFunc(Name)); + return Name; +} + +} // namespace symbolize +} // namespace llvm |