diff options
Diffstat (limited to 'contrib/llvm/lib/DebugInfo')
143 files changed, 23618 insertions, 0 deletions
diff --git a/contrib/llvm/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp b/contrib/llvm/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp new file mode 100644 index 000000000000..4c78caf03477 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/CVSymbolVisitor.cpp @@ -0,0 +1,64 @@ +//===- CVSymbolVisitor.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/Support/BinaryByteStream.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.Type); + T KnownRecord(RK); + if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord)) + return EC; + return Error::success(); +} + +Error CVSymbolVisitor::visitSymbolRecord(CVSymbol &Record) { + if (auto EC = Callbacks.visitSymbolBegin(Record)) + return EC; + + switch (Record.Type) { + 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/CVSymbolTypes.def" + } + + if (auto EC = Callbacks.visitSymbolEnd(Record)) + return EC; + + return Error::success(); +} + +Error CVSymbolVisitor::visitSymbolStream(const CVSymbolArray &Symbols) { + for (auto I : Symbols) { + if (auto EC = visitSymbolRecord(I)) + return EC; + } + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/contrib/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp new file mode 100644 index 000000000000..705b548141b0 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -0,0 +1,345 @@ +//===- CVTypeVisitor.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" + +#include "llvm/ADT/TinyPtrVector.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/TypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.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.Type); + 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 Expected<TypeServer2Record> deserializeTypeServerRecord(CVType &Record) { + TypeServer2Record R(TypeRecordKind::TypeServer2); + if (auto EC = TypeDeserializer::deserializeAs(Record, R)) + return std::move(EC); + return R; +} + +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/TypeRecords.def" + } + + if (auto EC = Callbacks.visitMemberEnd(Record)) + return EC; + + return Error::success(); +} + +namespace { + +class CVTypeVisitor { +public: + explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks); + + void addTypeServerHandler(TypeServerHandler &Handler); + + 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: + Expected<bool> handleTypeServer(CVType &Record); + Error finishVisitation(CVType &Record); + + /// The interface to the class that gets notified of each visitation. + TypeVisitorCallbacks &Callbacks; + + TinyPtrVector<TypeServerHandler *> Handlers; +}; + +CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks) + : Callbacks(Callbacks) {} + +void CVTypeVisitor::addTypeServerHandler(TypeServerHandler &Handler) { + Handlers.push_back(&Handler); +} + +Expected<bool> CVTypeVisitor::handleTypeServer(CVType &Record) { + if (Record.Type == TypeLeafKind::LF_TYPESERVER2 && !Handlers.empty()) { + auto TS = deserializeTypeServerRecord(Record); + if (!TS) + return TS.takeError(); + + for (auto Handler : Handlers) { + auto ExpectedResult = Handler->handle(*TS, Callbacks); + // If there was an error, return the error. + if (!ExpectedResult) + return ExpectedResult.takeError(); + + // If the handler processed the record, return success. + if (*ExpectedResult) + return true; + + // Otherwise keep searching for a handler, eventually falling out and + // using the default record handler. + } + } + return false; +} + +Error CVTypeVisitor::finishVisitation(CVType &Record) { + switch (Record.Type) { + 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/TypeRecords.def" + } + + if (auto EC = Callbacks.visitTypeEnd(Record)) + return EC; + + return Error::success(); +} + +Error CVTypeVisitor::visitTypeRecord(CVType &Record, TypeIndex Index) { + auto ExpectedResult = handleTypeServer(Record); + if (!ExpectedResult) + return ExpectedResult.takeError(); + if (*ExpectedResult) + return Error::success(); + + if (auto EC = Callbacks.visitTypeBegin(Record, Index)) + return EC; + + return finishVisitation(Record); +} + +Error CVTypeVisitor::visitTypeRecord(CVType &Record) { + auto ExpectedResult = handleTypeServer(Record); + if (!ExpectedResult) + return ExpectedResult.takeError(); + if (*ExpectedResult) + return Error::success(); + + 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, + TypeServerHandler *TS) { + VisitHelper V(Callbacks, Source); + if (TS) + V.Visitor.addTypeServerHandler(*TS); + return V.Visitor.visitTypeRecord(Record, Index); +} + +Error llvm::codeview::visitTypeRecord(CVType &Record, + TypeVisitorCallbacks &Callbacks, + VisitorDataSource Source, + TypeServerHandler *TS) { + VisitHelper V(Callbacks, Source); + if (TS) + V.Visitor.addTypeServerHandler(*TS); + return V.Visitor.visitTypeRecord(Record); +} + +Error llvm::codeview::visitTypeStream(const CVTypeArray &Types, + TypeVisitorCallbacks &Callbacks, + VisitorDataSource Source, + TypeServerHandler *TS) { + VisitHelper V(Callbacks, Source); + if (TS) + V.Visitor.addTypeServerHandler(*TS); + return V.Visitor.visitTypeStream(Types); +} + +Error llvm::codeview::visitTypeStream(CVTypeRange Types, + TypeVisitorCallbacks &Callbacks, + TypeServerHandler *TS) { + VisitHelper V(Callbacks, VDS_BytesPresent); + if (TS) + V.Visitor.addTypeServerHandler(*TS); + return V.Visitor.visitTypeStream(Types); +} + +Error llvm::codeview::visitTypeStream(TypeCollection &Types, + TypeVisitorCallbacks &Callbacks, + TypeServerHandler *TS) { + // 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); + if (TS) + V.Visitor.addTypeServerHandler(*TS); + 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/contrib/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp b/contrib/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp new file mode 100644 index 000000000000..8de266b836b4 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp @@ -0,0 +1,71 @@ +//===- CodeViewError.cpp - Error extensions for CodeView --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/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 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"); + } +}; +} // end anonymous namespace + +static ManagedStatic<CodeViewErrorCategory> Category; + +char CodeViewError::ID = 0; + +CodeViewError::CodeViewError(cv_error_code C) : CodeViewError(C, "") {} + +CodeViewError::CodeViewError(const std::string &Context) + : CodeViewError(cv_error_code::unspecified, Context) {} + +CodeViewError::CodeViewError(cv_error_code C, const std::string &Context) + : Code(C) { + ErrMsg = "CodeView Error: "; + std::error_code EC = convertToErrorCode(); + if (Code != cv_error_code::unspecified) + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void CodeViewError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +const std::string &CodeViewError::getErrorMessage() const { return ErrMsg; } + +std::error_code CodeViewError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp b/contrib/llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp new file mode 100644 index 000000000000..282e3103adc9 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp @@ -0,0 +1,242 @@ +//===- CodeViewRecordIO.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/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(); + return Error::success(); +} + +uint32_t CodeViewRecordIO::maxFieldLength() const { + 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::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) { + 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) { + ArrayRef<uint8_t> BytesRef(Bytes); + if (auto EC = mapByteVectorTail(BytesRef)) + return EC; + if (!isWriting()) + Bytes.assign(BytesRef.begin(), BytesRef.end()); + + return Error::success(); +} + +Error CodeViewRecordIO::mapInteger(TypeIndex &TypeInd) { + if (isWriting()) { + if (auto EC = Writer->writeInteger(TypeInd.getIndex())) + return EC; + return Error::success(); + } + + uint32_t I; + if (auto EC = Reader->readInteger(I)) + return EC; + TypeInd.setIndex(I); + return Error::success(); +} + +Error CodeViewRecordIO::mapEncodedInteger(int64_t &Value) { + 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) { + 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) { + if (isWriting()) { + if (Value.isSigned()) + return writeEncodedSignedInteger(Value.getSExtValue()); + return writeEncodedUnsignedInteger(Value.getZExtValue()); + } + + return consume(*Reader, Value); +} + +Error CodeViewRecordIO::mapStringZ(StringRef &Value) { + 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(StringRef &Guid) { + constexpr uint32_t GuidSize = 16; + if (maxFieldLength() < GuidSize) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + + if (isWriting()) { + assert(Guid.size() == 16 && "Invalid Guid Size!"); + if (auto EC = Writer->writeFixedString(Guid)) + return EC; + } else { + if (auto EC = Reader->readFixedString(Guid, 16)) + return EC; + } + return Error::success(); +} + +Error CodeViewRecordIO::mapStringZVectorZ(std::vector<StringRef> &Value) { + if (isWriting()) { + for (auto V : Value) { + if (auto EC = mapStringZ(V)) + return EC; + } + if (auto EC = Writer->writeInteger<uint8_t>(0)) + 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(); +} + +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/contrib/llvm/lib/DebugInfo/CodeView/DebugChecksumsSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugChecksumsSubsection.cpp new file mode 100644 index 000000000000..1a85a339f8c3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugChecksumsSubsection.cpp @@ -0,0 +1,108 @@ +//===- DebugChecksumsSubsection.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/Support/BinaryStreamReader.h" + +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 llvm::VarStreamArrayExtractor<FileChecksumEntry>::extract( + 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.getStringId(FileName); + auto Iter = OffsetMap.find(Offset); + assert(Iter != OffsetMap.end()); + return Iter->second; +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/DebugFrameDataSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugFrameDataSubsection.cpp new file mode 100644 index 000000000000..fd558aa9cc8a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugFrameDataSubsection.cpp @@ -0,0 +1,44 @@ +//===- DebugFrameDataSubsection.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error DebugFrameDataSubsectionRef::initialize(BinaryStreamReader Reader) { + 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(); +} + +uint32_t DebugFrameDataSubsection::calculateSerializedSize() const { + return 4 + sizeof(FrameData) * Frames.size(); +} + +Error DebugFrameDataSubsection::commit(BinaryStreamWriter &Writer) const { + if (auto EC = Writer.writeInteger<uint32_t>(0)) + return EC; + + if (auto EC = Writer.writeArray(makeArrayRef(Frames))) + return EC; + return Error::success(); +} + +void DebugFrameDataSubsection::addFrameData(const FrameData &Frame) { + Frames.push_back(Frame); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/DebugInlineeLinesSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugInlineeLinesSubsection.cpp new file mode 100644 index 000000000000..520a0ee4454f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugInlineeLinesSubsection.cpp @@ -0,0 +1,123 @@ +//===- DebugInlineeLinesSubsection.cpp ------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error VarStreamArrayExtractor<InlineeSourceLine>::extract( + BinaryStreamRef Stream, uint32_t &Len, InlineeSourceLine &Item, + bool HasExtraFiles) { + 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; + + if (auto EC = + Reader.readArray(Lines, Reader.bytesRemaining(), hasExtraFiles())) + 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/contrib/llvm/lib/DebugInfo/CodeView/DebugLinesSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugLinesSubsection.cpp new file mode 100644 index 000000000000..2fce06ca2a17 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugLinesSubsection.cpp @@ -0,0 +1,159 @@ +//===- DebugLinesSubsection.cpp -------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error LineColumnExtractor::extract(BinaryStreamRef Stream, uint32_t &Len, + LineColumnEntry &Item, + const LineFragmentHeader *Header) { + using namespace codeview; + 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; + + if (auto EC = + Reader.readArray(LinesAndColumns, Reader.bytesRemaining(), Header)) + 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, + uint16_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/contrib/llvm/lib/DebugInfo/CodeView/DebugStringTableSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugStringTableSubsection.cpp new file mode 100644 index 000000000000..b8741eb0b675 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugStringTableSubsection.cpp @@ -0,0 +1,78 @@ +//===- DebugStringTableSubsection.cpp - CodeView String Table ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" + +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; + +DebugStringTableSubsectionRef::DebugStringTableSubsectionRef() + : DebugSubsectionRef(DebugSubsectionKind::StringTable) {} + +Error DebugStringTableSubsectionRef::initialize(BinaryStreamRef Contents) { + Stream = Contents; + return Error::success(); +} + +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 = Strings.insert({S, StringSize}); + + // If a given string didn't exist in the string table, we want to increment + // the string table size. + if (P.second) + StringSize += S.size() + 1; // +1 for '\0' + return P.first->second; +} + +uint32_t DebugStringTableSubsection::calculateSerializedSize() const { + return StringSize; +} + +Error DebugStringTableSubsection::commit(BinaryStreamWriter &Writer) const { + assert(Writer.bytesRemaining() == StringSize); + uint32_t MaxOffset = 1; + + for (auto &Pair : Strings) { + StringRef S = Pair.getKey(); + uint32_t Offset = Pair.getValue(); + Writer.setOffset(Offset); + if (auto EC = Writer.writeCString(S)) + return EC; + MaxOffset = std::max<uint32_t>(MaxOffset, Offset + S.size() + 1); + } + + Writer.setOffset(MaxOffset); + assert(Writer.bytesRemaining() == 0); + return Error::success(); +} + +uint32_t DebugStringTableSubsection::size() const { return Strings.size(); } + +uint32_t DebugStringTableSubsection::getStringId(StringRef S) const { + auto P = Strings.find(S); + assert(P != Strings.end()); + return P->second; +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsection.cpp new file mode 100644 index 000000000000..67b428bfa713 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsection.cpp @@ -0,0 +1,16 @@ +//===- DebugSubsection.cpp -----------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSubsection.h" + +using namespace llvm::codeview; + +DebugSubsectionRef::~DebugSubsectionRef() {} + +DebugSubsection::~DebugSubsection() {} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsectionRecord.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsectionRecord.cpp new file mode 100644 index 000000000000..511f36d0020a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsectionRecord.cpp @@ -0,0 +1,81 @@ +//===- DebugSubsectionRecord.cpp -----------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/DebugSubsection.h" + +#include "llvm/Support/BinaryStreamReader.h" + +using namespace llvm; +using namespace llvm::codeview; + +DebugSubsectionRecord::DebugSubsectionRecord() + : Kind(DebugSubsectionKind::None) {} + +DebugSubsectionRecord::DebugSubsectionRecord(DebugSubsectionKind Kind, + BinaryStreamRef Data) + : Kind(Kind), Data(Data) {} + +Error DebugSubsectionRecord::initialize(BinaryStreamRef Stream, + DebugSubsectionRecord &Info) { + const DebugSubsectionHeader *Header; + BinaryStreamReader Reader(Stream); + if (auto EC = Reader.readObject(Header)) + return EC; + + DebugSubsectionKind Kind = + static_cast<DebugSubsectionKind>(uint32_t(Header->Kind)); + switch (Kind) { + case DebugSubsectionKind::FileChecksums: + case DebugSubsectionKind::Lines: + case DebugSubsectionKind::InlineeLines: + break; + default: + llvm_unreachable("Unexpected debug fragment kind!"); + } + if (auto EC = Reader.readStreamRef(Info.Data, Header->Length)) + return EC; + Info.Kind = Kind; + return Error::success(); +} + +uint32_t DebugSubsectionRecord::getRecordLength() const { + uint32_t Result = sizeof(DebugSubsectionHeader) + Data.getLength(); + assert(Result % 4 == 0); + return Result; +} + +DebugSubsectionKind DebugSubsectionRecord::kind() const { return Kind; } + +BinaryStreamRef DebugSubsectionRecord::getRecordData() const { return Data; } + +DebugSubsectionRecordBuilder::DebugSubsectionRecordBuilder( + DebugSubsectionKind Kind, DebugSubsection &Frag) + : Kind(Kind), Frag(Frag) {} + +uint32_t DebugSubsectionRecordBuilder::calculateSerializedLength() { + uint32_t Size = sizeof(DebugSubsectionHeader) + + alignTo(Frag.calculateSerializedSize(), 4); + return Size; +} + +Error DebugSubsectionRecordBuilder::commit(BinaryStreamWriter &Writer) { + DebugSubsectionHeader Header; + Header.Kind = uint32_t(Kind); + Header.Length = calculateSerializedLength() - sizeof(DebugSubsectionHeader); + + if (auto EC = Writer.writeObject(Header)) + return EC; + if (auto EC = Frag.commit(Writer)) + return EC; + if (auto EC = Writer.padToAlignment(4)) + return EC; + + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsectionVisitor.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsectionVisitor.cpp new file mode 100644 index 000000000000..f2c4dea8685f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugSubsectionVisitor.cpp @@ -0,0 +1,52 @@ +//===- DebugSubsectionVisitor.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" + +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.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) { + BinaryStreamReader Reader(R.getRecordData()); + switch (R.kind()) { + case DebugSubsectionKind::Lines: { + DebugLinesSubsectionRef Fragment; + if (auto EC = Fragment.initialize(Reader)) + return EC; + + return V.visitLines(Fragment); + } + case DebugSubsectionKind::FileChecksums: { + DebugChecksumsSubsectionRef Fragment; + if (auto EC = Fragment.initialize(Reader)) + return EC; + + return V.visitFileChecksums(Fragment); + } + case DebugSubsectionKind::InlineeLines: { + DebugInlineeLinesSubsectionRef Fragment; + if (auto EC = Fragment.initialize(Reader)) + return EC; + return V.visitInlineeLines(Fragment); + } + default: { + DebugUnknownSubsectionRef Fragment(R.kind(), R.getRecordData()); + return V.visitUnknown(Fragment); + } + } +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/DebugSymbolsSubsection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/DebugSymbolsSubsection.cpp new file mode 100644 index 000000000000..dc8ba8c929ae --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/DebugSymbolsSubsection.cpp @@ -0,0 +1,34 @@ +//===- DebugSymbolsSubsection.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/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(); +}
\ No newline at end of file diff --git a/contrib/llvm/lib/DebugInfo/CodeView/EnumTables.cpp b/contrib/llvm/lib/DebugInfo/CodeView/EnumTables.cpp new file mode 100644 index 000000000000..0441110c85ef --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/EnumTables.cpp @@ -0,0 +1,385 @@ +//===- EnumTables.cpp - Enum to string conversion tables --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/EnumTables.h" + +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/CVSymbolTypes.def" +#undef CV_SYMBOL +}; + +static const EnumEntry<TypeLeafKind> TypeLeafNames[] = { +#define CV_TYPE(name, val) {#name, name}, +#include "llvm/DebugInfo/CodeView/TypeRecords.def" +#undef CV_TYPE +}; + +static const EnumEntry<uint16_t> RegisterNames[] = { + CV_ENUM_CLASS_ENT(RegisterId, Unknown), + CV_ENUM_CLASS_ENT(RegisterId, VFrame), + CV_ENUM_CLASS_ENT(RegisterId, AL), + CV_ENUM_CLASS_ENT(RegisterId, CL), + CV_ENUM_CLASS_ENT(RegisterId, DL), + CV_ENUM_CLASS_ENT(RegisterId, BL), + CV_ENUM_CLASS_ENT(RegisterId, AH), + CV_ENUM_CLASS_ENT(RegisterId, CH), + CV_ENUM_CLASS_ENT(RegisterId, DH), + CV_ENUM_CLASS_ENT(RegisterId, BH), + CV_ENUM_CLASS_ENT(RegisterId, AX), + CV_ENUM_CLASS_ENT(RegisterId, CX), + CV_ENUM_CLASS_ENT(RegisterId, DX), + CV_ENUM_CLASS_ENT(RegisterId, BX), + CV_ENUM_CLASS_ENT(RegisterId, SP), + CV_ENUM_CLASS_ENT(RegisterId, BP), + CV_ENUM_CLASS_ENT(RegisterId, SI), + CV_ENUM_CLASS_ENT(RegisterId, DI), + CV_ENUM_CLASS_ENT(RegisterId, EAX), + CV_ENUM_CLASS_ENT(RegisterId, ECX), + CV_ENUM_CLASS_ENT(RegisterId, EDX), + CV_ENUM_CLASS_ENT(RegisterId, EBX), + CV_ENUM_CLASS_ENT(RegisterId, ESP), + CV_ENUM_CLASS_ENT(RegisterId, EBP), + CV_ENUM_CLASS_ENT(RegisterId, ESI), + CV_ENUM_CLASS_ENT(RegisterId, EDI), + CV_ENUM_CLASS_ENT(RegisterId, ES), + CV_ENUM_CLASS_ENT(RegisterId, CS), + CV_ENUM_CLASS_ENT(RegisterId, SS), + CV_ENUM_CLASS_ENT(RegisterId, DS), + CV_ENUM_CLASS_ENT(RegisterId, FS), + CV_ENUM_CLASS_ENT(RegisterId, GS), + CV_ENUM_CLASS_ENT(RegisterId, IP), + CV_ENUM_CLASS_ENT(RegisterId, RAX), + CV_ENUM_CLASS_ENT(RegisterId, RBX), + CV_ENUM_CLASS_ENT(RegisterId, RCX), + CV_ENUM_CLASS_ENT(RegisterId, RDX), + CV_ENUM_CLASS_ENT(RegisterId, RSI), + CV_ENUM_CLASS_ENT(RegisterId, RDI), + CV_ENUM_CLASS_ENT(RegisterId, RBP), + CV_ENUM_CLASS_ENT(RegisterId, RSP), + CV_ENUM_CLASS_ENT(RegisterId, R8), + CV_ENUM_CLASS_ENT(RegisterId, R9), + CV_ENUM_CLASS_ENT(RegisterId, R10), + CV_ENUM_CLASS_ENT(RegisterId, R11), + CV_ENUM_CLASS_ENT(RegisterId, R12), + CV_ENUM_CLASS_ENT(RegisterId, R13), + CV_ENUM_CLASS_ENT(RegisterId, R14), + CV_ENUM_CLASS_ENT(RegisterId, R15), +}; + +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), +}; + +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, 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, 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)}; + +namespace llvm { +namespace codeview { +ArrayRef<EnumEntry<SymbolKind>> getSymbolTypeNames() { + return makeArrayRef(SymbolTypeNames); +} + +ArrayRef<EnumEntry<TypeLeafKind>> getTypeLeafNames() { + return makeArrayRef(TypeLeafNames); +} + +ArrayRef<EnumEntry<uint16_t>> getRegisterNames() { + return makeArrayRef(RegisterNames); +} + +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); +} +} +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/Formatters.cpp b/contrib/llvm/lib/DebugInfo/CodeView/Formatters.cpp new file mode 100644 index 000000000000..ef00bd8570fa --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/Formatters.cpp @@ -0,0 +1,37 @@ +//===- Formatters.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/Formatters.h" + +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(llvm::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 << "}"; +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp new file mode 100644 index 000000000000..39eb4099ce9e --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -0,0 +1,229 @@ +//===- LazyRandomTypeCollection.cpp ---------------------------- *- C++--*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +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) + : Database(RecordCountHint), Types(Types), DatabaseVisitor(Database), + PartialOffsets(PartialOffsets) { + KnownOffsets.resize(Database.capacity()); +} + +LazyRandomTypeCollection::LazyRandomTypeCollection(ArrayRef<uint8_t> Data, + uint32_t RecordCountHint) + : LazyRandomTypeCollection(RecordCountHint) { + reset(Data); +} + +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(StringRef Data) { + reset(makeArrayRef(Data.bytes_begin(), Data.bytes_end())); +} + +void LazyRandomTypeCollection::reset(ArrayRef<uint8_t> Data) { + PartialOffsets = PartialOffsetArray(); + + BinaryStreamReader Reader(Data, support::little); + error(Reader.readArray(Types, Reader.getLength())); + + KnownOffsets.resize(Database.capacity()); +} + +CVType LazyRandomTypeCollection::getType(TypeIndex Index) { + error(ensureTypeExists(Index)); + return Database.getTypeRecord(Index); +} + +StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { + if (!Index.isSimple()) { + // 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. + consumeError(ensureTypeExists(Index)); + } + + return Database.getTypeName(Index); +} + +bool LazyRandomTypeCollection::contains(TypeIndex Index) { + return Database.contains(Index); +} + +uint32_t LazyRandomTypeCollection::size() { return Database.size(); } + +uint32_t LazyRandomTypeCollection::capacity() { return Database.capacity(); } + +Error LazyRandomTypeCollection::ensureTypeExists(TypeIndex TI) { + if (!Database.contains(TI)) { + if (auto EC = visitRangeForType(TI)) + return EC; + } + return Error::success(); +} + +Error LazyRandomTypeCollection::visitRangeForType(TypeIndex TI) { + 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 (Database.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(Database.capacity()); + } else { + TIE = Next->Type; + } + + if (auto EC = visitRange(TIB, Prev->Offset, TIE)) + return EC; + 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(PartialOffsets.empty()); + + TypeIndex CurrentTI = TypeIndex::fromArrayIndex(0); + uint32_t Offset = 0; + auto Begin = Types.begin(); + + if (!Database.empty()) { + // 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. So we ask the + // database for the largest type index less than the one we're searching for + // and only do the forward scan from there. + auto Prev = Database.largestTypeIndexLessThan(TI); + assert(Prev.hasValue() && "Empty database with valid types?"); + Offset = KnownOffsets[Prev->toArrayIndex()]; + CurrentTI = *Prev; + ++CurrentTI; + Begin = Types.at(Offset); + ++Begin; + Offset = Begin.offset(); + } + + auto End = Types.end(); + while (Begin != End) { + if (auto EC = visitOneRecord(CurrentTI, Offset, *Begin)) + return EC; + + Offset += Begin.getRecordLength(); + ++Begin; + ++CurrentTI; + } + if (CurrentTI <= TI) { + return make_error<CodeViewError>("Type Index does not exist!"); + } + return Error::success(); +} + +Error LazyRandomTypeCollection::visitRange(TypeIndex Begin, + uint32_t BeginOffset, + TypeIndex End) { + + auto RI = Types.at(BeginOffset); + assert(RI != Types.end()); + + while (Begin != End) { + if (auto EC = visitOneRecord(Begin, BeginOffset, *RI)) + return EC; + + BeginOffset += RI.getRecordLength(); + ++Begin; + ++RI; + } + + return Error::success(); +} + +Error LazyRandomTypeCollection::visitOneRecord(TypeIndex TI, uint32_t Offset, + CVType &Record) { + assert(!Database.contains(TI)); + if (auto EC = codeview::visitTypeRecord(Record, TI, DatabaseVisitor)) + return EC; + // Keep the KnownOffsets array the same size as the Database's capacity. Since + // we don't always know how many records are in the type stream, we need to be + // prepared for the database growing and receicing a type index that can't fit + // in our current buffer. + if (KnownOffsets.size() < Database.capacity()) + KnownOffsets.resize(Database.capacity()); + KnownOffsets[TI.toArrayIndex()] = Offset; + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/Line.cpp b/contrib/llvm/lib/DebugInfo/CodeView/Line.cpp new file mode 100644 index 000000000000..4cb766b5fd26 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/Line.cpp @@ -0,0 +1,22 @@ +//===-- Line.cpp ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/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/contrib/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp b/contrib/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp new file mode 100644 index 000000000000..6446670f60d8 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp @@ -0,0 +1,149 @@ +//===-- RecordSerialization.cpp -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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/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); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp b/contrib/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp new file mode 100644 index 000000000000..2f5a7d256c60 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -0,0 +1,689 @@ +//===-- SymbolDumper.cpp - CodeView symbol info dumper ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/ADT/DenseMap.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, bool PrintRecordBytes) + : Types(Types), ObjDelegate(ObjDelegate), W(W), + 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/CVSymbolTypes.def" + + Error visitSymbolBegin(CVSymbol &Record) override; + Error visitSymbolEnd(CVSymbol &Record) override; + Error visitUnknownSymbol(CVSymbol &Record) override; + +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; + + bool PrintRecordBytes; + bool InFunctionScope; +}; +} + +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) { + return Error::success(); +} + +Error CVSymbolDumperImpl::visitSymbolEnd(CVSymbol &CVR) { + if (PrintRecordBytes && ObjDelegate) + ObjDelegate->printBinaryBlockWithRelocs("SymData", CVR.content()); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) { + DictScope S(W, "BlockStart"); + + 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) { + DictScope S(W, "Thunk32"); + 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) { + DictScope S(W, "Trampoline"); + 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) { + DictScope S(W, "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) { + DictScope S(W, "COFF Group"); + 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) { + DictScope S(W, "BPRelativeSym"); + + W.printNumber("Offset", BPRel.Offset); + printTypeIndex("Type", BPRel.Type); + W.printString("VarName", BPRel.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + DictScope S(W, "BuildInfo"); + + W.printNumber("BuildId", BuildInfo.BuildId); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + CallSiteInfoSym &CallSiteInfo) { + DictScope S(W, "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) { + DictScope S(W, "EnvBlock"); + + ListScope L(W, "Entries"); + for (auto Entry : EnvBlock.Fields) { + W.printString(Entry); + } + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + FileStaticSym &FileStatic) { + DictScope S(W, "FileStatic"); + W.printNumber("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) { + DictScope S(W, "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) { + DictScope S(W, "CompilerFlags2"); + + W.printEnum("Language", Compile2.getLanguage(), getSourceLanguageNames()); + W.printFlags("Flags", Compile2.getFlags(), getCompileSym2FlagNames()); + W.printEnum("Machine", unsigned(Compile2.Machine), getCPUTypeNames()); + 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) { + DictScope S(W, "CompilerFlags3"); + + W.printEnum("Language", Compile3.getLanguage(), getSourceLanguageNames()); + W.printFlags("Flags", Compile3.getFlags(), getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile3.Machine), getCPUTypeNames()); + 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) { + DictScope S(W, "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) { + DictScope S(W, "DataSym"); + + W.printEnum("Kind", uint16_t(CVR.kind()), getSymbolTypeNames()); + 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) { + DictScope S(W, "DefRangeFramePointerRelFullScope"); + W.printNumber("Offset", DefRangeFramePointerRelFullScope.Offset); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + DictScope S(W, "DefRangeFramePointerRel"); + + W.printNumber("Offset", DefRangeFramePointerRel.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterRelSym &DefRangeRegisterRel) { + DictScope S(W, "DefRangeRegisterRel"); + + W.printNumber("BaseRegister", DefRangeRegisterRel.Hdr.Register); + 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) { + DictScope S(W, "DefRangeRegister"); + + W.printNumber("Register", DefRangeRegister.Hdr.Register); + W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); + printLocalVariableAddrRange(DefRangeRegister.Range, + DefRangeRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegister.Gaps); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + DictScope S(W, "DefRangeSubfieldRegister"); + + W.printNumber("Register", DefRangeSubfieldRegister.Hdr.Register); + 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) { + DictScope S(W, "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) { + DictScope S(W, "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) { + DictScope S(W, "FrameCookie"); + + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", + FrameCookie.getRelocationOffset(), + FrameCookie.CodeOffset, &LinkageName); + } + W.printHex("Register", FrameCookie.Register); + W.printEnum("CookieKind", uint16_t(FrameCookie.CookieKind), + getFrameCookieKindNames()); + W.printHex("Flags", FrameCookie.Flags); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + FrameProcSym &FrameProc) { + DictScope S(W, "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()); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord( + CVSymbol &CVR, HeapAllocationSiteSym &HeapAllocSite) { + DictScope S(W, "HeapAllocationSite"); + + 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) { + DictScope S(W, "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) { + DictScope S(W, "RegisterSym"); + W.printNumber("Type", Register.Index); + W.printEnum("Seg", uint16_t(Register.Register), getRegisterNames()); + W.printString("Name", Register.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, PublicSym32 &Public) { + DictScope S(W, "PublicSym"); + W.printNumber("Type", Public.Index); + 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) { + DictScope S(W, "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) { + DictScope S(W, "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) { + DictScope S(W, "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) { + DictScope S(W, "ObjectName"); + + W.printHex("Signature", ObjName.Signature); + W.printString("ObjectName", ObjName.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) { + DictScope S(W, "ProcStart"); + + if (InFunctionScope) + return llvm::make_error<CodeViewError>( + "Visiting a ProcSym while inside function scope!"); + + InFunctionScope = true; + + StringRef LinkageName; + W.printEnum("Kind", uint16_t(CVR.kind()), getSymbolTypeNames()); + 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) { + if (CVR.kind() == SymbolKind::S_END) + DictScope S(W, "BlockEnd"); + else if (CVR.kind() == SymbolKind::S_PROC_ID_END) + DictScope S(W, "ProcEnd"); + else if (CVR.kind() == SymbolKind::S_INLINESITE_END) + DictScope S(W, "InlineSiteEnd"); + + 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) { + DictScope S(W, "RegRelativeSym"); + + W.printHex("Offset", RegRel.Offset); + printTypeIndex("Type", RegRel.Type); + W.printHex("Register", RegRel.Register); + W.printString("VarName", RegRel.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + ThreadLocalDataSym &Data) { + DictScope S(W, "ThreadLocalDataSym"); + + 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) { + DictScope S(W, "UDT"); + printTypeIndex("Type", UDT.Type); + W.printString("UDTName", UDT.Name); + return Error::success(); +} + +Error CVSymbolDumperImpl::visitUnknownSymbol(CVSymbol &CVR) { + DictScope S(W, "UnknownSym"); + W.printEnum("Kind", uint16_t(CVR.kind()), getSymbolTypeNames()); + W.printNumber("Length", CVR.length()); + return Error::success(); +} + +Error CVSymbolDumper::dump(CVRecord<SymbolKind> &Record) { + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(ObjDelegate.get()); + CVSymbolDumperImpl Dumper(Types, ObjDelegate.get(), W, PrintRecordBytes); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + return Visitor.visitSymbolRecord(Record); +} + +Error CVSymbolDumper::dump(const CVSymbolArray &Symbols) { + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(ObjDelegate.get()); + CVSymbolDumperImpl Dumper(Types, ObjDelegate.get(), W, PrintRecordBytes); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + return Visitor.visitSymbolStream(Symbols); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp b/contrib/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp new file mode 100644 index 000000000000..bb1731465495 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp @@ -0,0 +1,464 @@ +//===- SymbolRecordMapping.cpp -----------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/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.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.mapInteger(DefRangeFramePointerRel.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.mapInteger(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.mapInteger(Public.Index)); + 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.mapInteger(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(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp b/contrib/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp new file mode 100644 index 000000000000..251cc431f52b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp @@ -0,0 +1,52 @@ +//===- SymbolSerializer.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" + +using namespace llvm; +using namespace llvm::codeview; + +SymbolSerializer::SymbolSerializer(BumpPtrAllocator &Allocator) + : Storage(Allocator), RecordBuffer(MaxRecordLength), Stream(RecordBuffer, llvm::support::little), + Writer(Stream), Mapping(Writer) { } + +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/contrib/llvm/lib/DebugInfo/CodeView/TypeDatabase.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeDatabase.cpp new file mode 100644 index 000000000000..af05d2dc294b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeDatabase.cpp @@ -0,0 +1,213 @@ +//===- TypeDatabase.cpp --------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeDatabase.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}, +}; + +TypeDatabase::TypeDatabase(uint32_t Capacity) : TypeNameStorage(Allocator) { + CVUDTNames.resize(Capacity); + TypeRecords.resize(Capacity); + ValidRecords.resize(Capacity); +} + +TypeIndex TypeDatabase::appendType(StringRef Name, const CVType &Data) { + LargestTypeIndex = getAppendIndex(); + if (LargestTypeIndex.toArrayIndex() >= capacity()) + grow(); + recordType(Name, LargestTypeIndex, Data); + return LargestTypeIndex; +} + +void TypeDatabase::recordType(StringRef Name, TypeIndex Index, + const CVType &Data) { + LargestTypeIndex = empty() ? Index : std::max(Index, LargestTypeIndex); + + if (LargestTypeIndex.toArrayIndex() >= capacity()) + grow(Index); + + uint32_t AI = Index.toArrayIndex(); + + assert(!contains(Index)); + assert(AI < capacity()); + + CVUDTNames[AI] = Name; + TypeRecords[AI] = Data; + ValidRecords.set(AI); + ++Count; +} + +/// Saves the name in a StringSet and creates a stable StringRef. +StringRef TypeDatabase::saveTypeName(StringRef TypeName) { + return TypeNameStorage.save(TypeName); +} + +StringRef TypeDatabase::getTypeName(TypeIndex Index) const { + if (Index.isNoneType()) + return "<no type>"; + + if (Index.isSimple()) { + // This is a simple type. + for (const auto &SimpleTypeName : SimpleTypeNames) { + if (SimpleTypeName.Kind == Index.getSimpleKind()) { + if (Index.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>"; + } + + if (contains(Index)) + return CVUDTNames[Index.toArrayIndex()]; + + return "<unknown UDT>"; +} + +const CVType &TypeDatabase::getTypeRecord(TypeIndex Index) const { + assert(contains(Index)); + return TypeRecords[Index.toArrayIndex()]; +} + +CVType &TypeDatabase::getTypeRecord(TypeIndex Index) { + assert(contains(Index)); + return TypeRecords[Index.toArrayIndex()]; +} + +bool TypeDatabase::contains(TypeIndex Index) const { + uint32_t AI = Index.toArrayIndex(); + if (AI >= capacity()) + return false; + + return ValidRecords.test(AI); +} + +uint32_t TypeDatabase::size() const { return Count; } + +uint32_t TypeDatabase::capacity() const { return TypeRecords.size(); } + +CVType TypeDatabase::getType(TypeIndex Index) { return getTypeRecord(Index); } + +StringRef TypeDatabase::getTypeName(TypeIndex Index) { + return static_cast<const TypeDatabase *>(this)->getTypeName(Index); +} + +bool TypeDatabase::contains(TypeIndex Index) { + return static_cast<const TypeDatabase *>(this)->contains(Index); +} + +uint32_t TypeDatabase::size() { + return static_cast<const TypeDatabase *>(this)->size(); +} + +uint32_t TypeDatabase::capacity() { + return static_cast<const TypeDatabase *>(this)->capacity(); +} + +void TypeDatabase::grow() { grow(LargestTypeIndex + 1); } + +void TypeDatabase::grow(TypeIndex NewIndex) { + uint32_t NewSize = NewIndex.toArrayIndex() + 1; + + if (NewSize <= capacity()) + return; + + uint32_t NewCapacity = NewSize * 3 / 2; + + TypeRecords.resize(NewCapacity); + CVUDTNames.resize(NewCapacity); + ValidRecords.resize(NewCapacity); +} + +bool TypeDatabase::empty() const { return size() == 0; } + +Optional<TypeIndex> TypeDatabase::largestTypeIndexLessThan(TypeIndex TI) const { + uint32_t AI = TI.toArrayIndex(); + int N = ValidRecords.find_prev(AI); + if (N == -1) + return None; + return TypeIndex::fromArrayIndex(N); +} + +TypeIndex TypeDatabase::getAppendIndex() const { + if (empty()) + return TypeIndex::fromArrayIndex(0); + + return LargestTypeIndex + 1; +} + +Optional<TypeIndex> TypeDatabase::getFirst() { + int N = ValidRecords.find_first(); + if (N == -1) + return None; + return TypeIndex::fromArrayIndex(N); +} + +Optional<TypeIndex> TypeDatabase::getNext(TypeIndex Prev) { + int N = ValidRecords.find_next(Prev.toArrayIndex()); + if (N == -1) + return None; + return TypeIndex::fromArrayIndex(N); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeDatabaseVisitor.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeDatabaseVisitor.cpp new file mode 100644 index 000000000000..8d97f8b1cb40 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeDatabaseVisitor.cpp @@ -0,0 +1,330 @@ +//===- TypeDatabaseVisitor.cpp -------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" + +#include "llvm/ADT/SmallString.h" + +using namespace llvm; + +using namespace llvm::codeview; + +Error TypeDatabaseVisitor::visitTypeBegin(CVType &Record) { + assert(!IsInFieldList); + // Reset Name to the empty string. If the visitor sets it, we know it. + Name = ""; + + if (Record.Type == LF_FIELDLIST) { + // Record that we're in a field list so that members do not get assigned + // type indices. + IsInFieldList = true; + } + return Error::success(); +} + +Error TypeDatabaseVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + if (auto EC = visitTypeBegin(Record)) + return EC; + + CurrentTypeIndex = Index; + return Error::success(); +} + +StringRef TypeDatabaseVisitor::getTypeName(TypeIndex Index) const { + return TypeDB->getTypeName(Index); +} + +StringRef TypeDatabaseVisitor::saveTypeName(StringRef Name) { + return TypeDB->saveTypeName(Name); +} + +Error TypeDatabaseVisitor::visitTypeEnd(CVType &CVR) { + if (CVR.Type == LF_FIELDLIST) { + assert(IsInFieldList); + IsInFieldList = false; + } + assert(!IsInFieldList); + + // Record every type that is not a field list member, even if Name is empty. + // CVUDTNames is indexed by type index, and must have one entry for every + // type. Field list members are not recorded, and are only referenced by + // their containing field list record. + if (CurrentTypeIndex) + TypeDB->recordType(Name, *CurrentTypeIndex, CVR); + else + TypeDB->appendType(Name, CVR); + + CurrentTypeIndex.reset(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitMemberBegin(CVMemberRecord &Record) { + assert(IsInFieldList); + // Reset Name to the empty string. If the visitor sets it, we know it. + Name = ""; + return Error::success(); +} + +Error TypeDatabaseVisitor::visitMemberEnd(CVMemberRecord &Record) { + assert(IsInFieldList); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + FieldListRecord &FieldList) { + Name = "<field list>"; + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVRecord<TypeLeafKind> &CVR, + StringIdRecord &String) { + // Put this in the database so it gets printed with LF_UDT_SRC_LINE. + Name = String.getString(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, ArgListRecord &Args) { + auto Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + SmallString<256> TypeName("("); + for (uint32_t I = 0; I < Size; ++I) { + StringRef ArgTypeName = getTypeName(Indices[I]); + TypeName.append(ArgTypeName); + if (I + 1 != Size) + TypeName.append(", "); + } + TypeName.push_back(')'); + Name = saveTypeName(TypeName); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + StringListRecord &Strings) { + auto Indices = Strings.getIndices(); + uint32_t Size = Indices.size(); + SmallString<256> TypeName("\""); + for (uint32_t I = 0; I < Size; ++I) { + StringRef ArgTypeName = getTypeName(Indices[I]); + TypeName.append(ArgTypeName); + if (I + 1 != Size) + TypeName.append("\" \""); + } + TypeName.push_back('\"'); + Name = saveTypeName(TypeName); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, ClassRecord &Class) { + Name = Class.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, UnionRecord &Union) { + Name = Union.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { + Name = Enum.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { + Name = AT.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) { + Name = VFT.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + MemberFuncIdRecord &Id) { + Name = Id.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + ProcedureRecord &Proc) { + StringRef ReturnTypeName = getTypeName(Proc.getReturnType()); + StringRef ArgListTypeName = getTypeName(Proc.getArgumentList()); + SmallString<256> TypeName(ReturnTypeName); + TypeName.push_back(' '); + TypeName.append(ArgListTypeName); + Name = saveTypeName(TypeName); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + MemberFunctionRecord &MF) { + StringRef ReturnTypeName = getTypeName(MF.getReturnType()); + StringRef ClassTypeName = getTypeName(MF.getClassType()); + StringRef ArgListTypeName = getTypeName(MF.getArgumentList()); + SmallString<256> TypeName(ReturnTypeName); + TypeName.push_back(' '); + TypeName.append(ClassTypeName); + TypeName.append("::"); + TypeName.append(ArgListTypeName); + Name = saveTypeName(TypeName); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) { + Name = Func.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + TypeServer2Record &TS) { + Name = TS.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) { + + if (Ptr.isPointerToMember()) { + const MemberPointerInfo &MI = Ptr.getMemberInfo(); + + StringRef PointeeName = getTypeName(Ptr.getReferentType()); + StringRef ClassName = getTypeName(MI.getContainingType()); + SmallString<256> TypeName(PointeeName); + TypeName.push_back(' '); + TypeName.append(ClassName); + TypeName.append("::*"); + Name = saveTypeName(TypeName); + } else { + SmallString<256> TypeName; + if (Ptr.isConst()) + TypeName.append("const "); + if (Ptr.isVolatile()) + TypeName.append("volatile "); + if (Ptr.isUnaligned()) + TypeName.append("__unaligned "); + + TypeName.append(getTypeName(Ptr.getReferentType())); + + if (Ptr.getMode() == PointerMode::LValueReference) + TypeName.append("&"); + else if (Ptr.getMode() == PointerMode::RValueReference) + TypeName.append("&&"); + else if (Ptr.getMode() == PointerMode::Pointer) + TypeName.append("*"); + + if (!TypeName.empty()) + Name = saveTypeName(TypeName); + } + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) { + uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); + + StringRef ModifiedName = getTypeName(Mod.getModifiedType()); + SmallString<256> TypeName; + if (Mods & uint16_t(ModifierOptions::Const)) + TypeName.append("const "); + if (Mods & uint16_t(ModifierOptions::Volatile)) + TypeName.append("volatile "); + if (Mods & uint16_t(ModifierOptions::Unaligned)) + TypeName.append("__unaligned "); + TypeName.append(ModifiedName); + Name = saveTypeName(TypeName); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Shape) { + Name = + saveTypeName("<vftable " + utostr(Shape.getEntryCount()) + " methods>"); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Nested) { + Name = Nested.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + OneMethodRecord &Method) { + Name = Method.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + OverloadedMethodRecord &Method) { + Name = Method.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + DataMemberRecord &Field) { + Name = Field.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + StaticDataMemberRecord &Field) { + Name = Field.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + EnumeratorRecord &Enum) { + Name = Enum.getName(); + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + BaseClassRecord &Base) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + VirtualBaseClassRecord &VBase) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + ListContinuationRecord &Cont) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord( + CVType &CVR, UdtModSourceLineRecord &ModSourceLine) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &SourceLine) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord( + CVType &CVR, MethodOverloadListRecord &Overloads) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) { + return Error::success(); +} + +Error TypeDatabaseVisitor::visitKnownMember(CVMemberRecord &CVR, + VFPtrRecord &VFP) { + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp new file mode 100644 index 000000000000..84f52a055815 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp @@ -0,0 +1,557 @@ +//===-- TypeDumpVisitor.cpp - CodeView type info dumper ----------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/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/TypeDatabase.h" +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +static const EnumEntry<TypeLeafKind> LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/TypeRecords.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/TypeRecords.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.Type); + W->getOStream() << " (" << HexNumber(Index.getIndex()) << ")"; + W->getOStream() << " {\n"; + W->indent(); + W->printEnum("TypeLeafKind", unsigned(Record.Type), + 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}", fmt_guid(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->printHex("PointerAttributes", uint32_t(Ptr.getOptions())); + 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("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(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeIndex.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeIndex.cpp new file mode 100644 index 000000000000..20ba6470cd5b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeIndex.cpp @@ -0,0 +1,27 @@ +//===-- TypeIndex.cpp - CodeView type index ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeIndex.h" + +#include "llvm/DebugInfo/CodeView/TypeCollection.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +void llvm::codeview::printTypeIndex(ScopedPrinter &Printer, StringRef FieldName, + TypeIndex TI, TypeCollection &Types) { + StringRef TypeName; + if (!TI.isNoneType()) + TypeName = Types.getTypeName(TI); + if (!TypeName.empty()) + Printer.printHex(FieldName, TypeName, TI.getIndex()); + else + Printer.printHex(FieldName, TI.getIndex()); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp new file mode 100644 index 000000000000..11e2e215303c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -0,0 +1,371 @@ +//===- TypeIndexDiscovery.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/CodeView/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::PointerToDataMember; +} + +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 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; + } +} + +void llvm::codeview::discoverTypeIndices(const CVType &Type, + SmallVectorImpl<TiReference> &Refs) { + ::discoverTypeIndices(Type.content(), Type.kind(), Refs); +} + +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); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp new file mode 100644 index 000000000000..114f6fd2897e --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -0,0 +1,481 @@ +//===- TypeRecordMapping.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" + +using namespace llvm; +using namespace llvm::codeview; + +#define error(X) \ + if (auto EC = X) \ + return EC; + +namespace { +struct MapOneMethodRecord { + explicit MapOneMethodRecord(bool IsFromOverloadList) + : IsFromOverloadList(IsFromOverloadList) {} + + Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const { + error(IO.mapInteger(Method.Attrs.Attrs)); + if (IsFromOverloadList) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding)); + } + error(IO.mapInteger(Method.Type)); + if (Method.isIntroducingVirtual()) { + error(IO.mapInteger(Method.VFTableOffset)); + } else if (!IO.isWriting()) + Method.VFTableOffset = -1; + + if (!IsFromOverloadList) + error(IO.mapStringZ(Method.Name)); + + return Error::success(); + } + +private: + bool IsFromOverloadList; +}; +} + +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 { + error(IO.mapStringZ(Name)); + if (HasUniqueName) + error(IO.mapStringZ(UniqueName)); + } + + 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.Type != TypeLeafKind::LF_FIELDLIST && + CVR.Type != TypeLeafKind::LF_METHODLIST) + MaxLen = MaxRecordLength - sizeof(RecordPrefix); + error(IO.beginRecord(MaxLen)); + TypeKind = CVR.Type; + return Error::success(); +} + +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; + 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.isWriting()) { + if (auto EC = IO.skipPadding()) + return EC; + } + + MemberKind.reset(); + error(IO.endRecord()); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { + error(IO.mapInteger(Record.ModifiedType)); + error(IO.mapEnum(Record.Modifiers)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + ProcedureRecord &Record) { + error(IO.mapInteger(Record.ReturnType)); + error(IO.mapEnum(Record.CallConv)); + error(IO.mapEnum(Record.Options)); + error(IO.mapInteger(Record.ParameterCount)); + error(IO.mapInteger(Record.ArgumentList)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + MemberFunctionRecord &Record) { + error(IO.mapInteger(Record.ReturnType)); + error(IO.mapInteger(Record.ClassType)); + error(IO.mapInteger(Record.ThisType)); + error(IO.mapEnum(Record.CallConv)); + error(IO.mapEnum(Record.Options)); + error(IO.mapInteger(Record.ParameterCount)); + error(IO.mapInteger(Record.ArgumentList)); + error(IO.mapInteger(Record.ThisPointerAdjustment)); + + 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); })); + + 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); })); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) { + error(IO.mapInteger(Record.ReferentType)); + error(IO.mapInteger(Record.Attrs)); + + if (Record.isPointerToMember()) { + if (!IO.isWriting()) + Record.MemberInfo.emplace(); + + MemberPointerInfo &M = *Record.MemberInfo; + error(IO.mapInteger(M.ContainingType)); + error(IO.mapEnum(M.Representation)); + } + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) { + error(IO.mapInteger(Record.ElementType)); + error(IO.mapInteger(Record.IndexType)); + error(IO.mapEncodedInteger(Record.Size)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) { + assert((CVR.Type == TypeLeafKind::LF_STRUCTURE) || + (CVR.Type == TypeLeafKind::LF_CLASS) || + (CVR.Type == TypeLeafKind::LF_INTERFACE)); + + error(IO.mapInteger(Record.MemberCount)); + error(IO.mapEnum(Record.Options)); + error(IO.mapInteger(Record.FieldList)); + error(IO.mapInteger(Record.DerivationList)); + error(IO.mapInteger(Record.VTableShape)); + error(IO.mapEncodedInteger(Record.Size)); + error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, + Record.hasUniqueName())); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) { + error(IO.mapInteger(Record.MemberCount)); + error(IO.mapEnum(Record.Options)); + error(IO.mapInteger(Record.FieldList)); + error(IO.mapEncodedInteger(Record.Size)); + error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, + Record.hasUniqueName())); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) { + error(IO.mapInteger(Record.MemberCount)); + error(IO.mapEnum(Record.Options)); + error(IO.mapInteger(Record.UnderlyingType)); + error(IO.mapInteger(Record.FieldList)); + error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, + Record.hasUniqueName())); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) { + error(IO.mapInteger(Record.Type)); + error(IO.mapInteger(Record.BitSize)); + error(IO.mapInteger(Record.BitOffset)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Record) { + uint16_t Size; + if (IO.isWriting()) { + ArrayRef<VFTableSlotKind> Slots = Record.getSlots(); + Size = Slots.size(); + error(IO.mapInteger(Size)); + + 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)); + error(IO.mapInteger(Record.OverriddenVFTable)); + error(IO.mapInteger(Record.VFPtrOffset)); + uint32_t NamesLen = 0; + if (IO.isWriting()) { + 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); })); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) { + error(IO.mapInteger(Record.Id)); + error(IO.mapStringZ(Record.String)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &Record) { + error(IO.mapInteger(Record.UDT)); + error(IO.mapInteger(Record.SourceFile)); + error(IO.mapInteger(Record.LineNumber)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &Record) { + error(IO.mapInteger(Record.UDT)); + error(IO.mapInteger(Record.SourceFile)); + error(IO.mapInteger(Record.LineNumber)); + error(IO.mapInteger(Record.Module)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) { + error(IO.mapInteger(Record.ParentScope)); + error(IO.mapInteger(Record.FunctionType)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + MemberFuncIdRecord &Record) { + error(IO.mapInteger(Record.ClassType)); + error(IO.mapInteger(Record.FunctionType)); + error(IO.mapStringZ(Record.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); })); + + 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))); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + FieldListRecord &Record) { + error(IO.mapByteVectorTail(Record.Data)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, + TypeServer2Record &Record) { + error(IO.mapGuid(Record.Guid)); + error(IO.mapInteger(Record.Age)); + error(IO.mapStringZ(Record.Name)); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) { + error(IO.mapEnum(Record.Mode)); + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + BaseClassRecord &Record) { + error(IO.mapInteger(Record.Attrs.Attrs)); + error(IO.mapInteger(Record.Type)); + error(IO.mapEncodedInteger(Record.Offset)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + EnumeratorRecord &Record) { + error(IO.mapInteger(Record.Attrs.Attrs)); + + // FIXME: Handle full APInt such as __int128. + error(IO.mapEncodedInteger(Record.Value)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + DataMemberRecord &Record) { + error(IO.mapInteger(Record.Attrs.Attrs)); + error(IO.mapInteger(Record.Type)); + error(IO.mapEncodedInteger(Record.FieldOffset)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + OverloadedMethodRecord &Record) { + error(IO.mapInteger(Record.NumOverloads)); + error(IO.mapInteger(Record.MethodList)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + OneMethodRecord &Record) { + MapOneMethodRecord Mapper(false); + return Mapper(IO, Record); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Record) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding)); + error(IO.mapInteger(Record.Type)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + StaticDataMemberRecord &Record) { + + error(IO.mapInteger(Record.Attrs.Attrs)); + error(IO.mapInteger(Record.Type)); + error(IO.mapStringZ(Record.Name)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + VirtualBaseClassRecord &Record) { + + error(IO.mapInteger(Record.Attrs.Attrs)); + error(IO.mapInteger(Record.BaseType)); + error(IO.mapInteger(Record.VBPtrType)); + error(IO.mapEncodedInteger(Record.VBPtrOffset)); + error(IO.mapEncodedInteger(Record.VTableIndex)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + VFPtrRecord &Record) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding)); + error(IO.mapInteger(Record.Type)); + + return Error::success(); +} + +Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, + ListContinuationRecord &Record) { + uint16_t Padding = 0; + error(IO.mapInteger(Padding)); + error(IO.mapInteger(Record.ContinuationIndex)); + + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeSerializer.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeSerializer.cpp new file mode 100644 index 000000000000..93c1198e36ce --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeSerializer.cpp @@ -0,0 +1,371 @@ +//===- TypeSerialzier.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/BinaryStreamWriter.h" + +#include <string.h> + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +struct HashedType { + uint64_t Hash; + const uint8_t *Data; + unsigned Size; // FIXME: Go to uint16_t? + TypeIndex Index; +}; + +/// Wrapper around a poitner to a HashedType. Hash and equality operations are +/// based on data in the pointee. +struct HashedTypePtr { + HashedTypePtr() = default; + HashedTypePtr(HashedType *Ptr) : Ptr(Ptr) {} + HashedType *Ptr = nullptr; +}; +} // namespace + +namespace llvm { +template <> struct DenseMapInfo<HashedTypePtr> { + static inline HashedTypePtr getEmptyKey() { return HashedTypePtr(nullptr); } + static inline HashedTypePtr getTombstoneKey() { + return HashedTypePtr(reinterpret_cast<HashedType *>(1)); + } + static unsigned getHashValue(HashedTypePtr Val) { + assert(Val.Ptr != getEmptyKey().Ptr && Val.Ptr != getTombstoneKey().Ptr); + return Val.Ptr->Hash; + } + static bool isEqual(HashedTypePtr LHSP, HashedTypePtr RHSP) { + HashedType *LHS = LHSP.Ptr; + HashedType *RHS = RHSP.Ptr; + if (RHS == getEmptyKey().Ptr || RHS == getTombstoneKey().Ptr) + return LHS == RHS; + if (LHS->Hash != RHS->Hash || LHS->Size != RHS->Size) + return false; + return ::memcmp(LHS->Data, RHS->Data, LHS->Size) == 0; + } +}; +} + +/// Private implementation so that we don't leak our DenseMap instantiations to +/// users. +class llvm::codeview::TypeHasher { +private: + /// Storage for type record provided by the caller. Records will outlive the + /// hasher object, so they should be allocated here. + BumpPtrAllocator &RecordStorage; + + /// Storage for hash keys. These only need to live as long as the hashing + /// operation. + BumpPtrAllocator KeyStorage; + + /// Hash table. We really want a DenseMap<ArrayRef<uint8_t>, TypeIndex> here, + /// but DenseMap is inefficient when the keys are long (like type records) + /// because it recomputes the hash value of every key when it grows. This + /// value type stores the hash out of line in KeyStorage, so that table + /// entries are small and easy to rehash. + DenseSet<HashedTypePtr> HashedRecords; + +public: + TypeHasher(BumpPtrAllocator &RecordStorage) : RecordStorage(RecordStorage) {} + + void reset() { HashedRecords.clear(); } + + /// Takes the bytes of type record, inserts them into the hash table, saves + /// them, and returns a pointer to an identical stable type record along with + /// its type index in the destination stream. + TypeIndex getOrCreateRecord(ArrayRef<uint8_t> &Record, TypeIndex TI); +}; + +TypeIndex TypeHasher::getOrCreateRecord(ArrayRef<uint8_t> &Record, + TypeIndex TI) { + assert(Record.size() < UINT32_MAX && "Record too big"); + assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!"); + + // Compute the hash up front so we can store it in the key. + HashedType TempHashedType = {hash_value(Record), Record.data(), + unsigned(Record.size()), TI}; + auto Result = HashedRecords.insert(HashedTypePtr(&TempHashedType)); + HashedType *&Hashed = Result.first->Ptr; + + if (Result.second) { + // This was a new type record. We need stable storage for both the key and + // the record. The record should outlive the hashing operation. + Hashed = KeyStorage.Allocate<HashedType>(); + *Hashed = TempHashedType; + + uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); + memcpy(Stable, Record.data(), Record.size()); + Hashed->Data = Stable; + assert(Hashed->Size == Record.size()); + } + + // Update the caller's copy of Record to point a stable copy. + Record = ArrayRef<uint8_t>(Hashed->Data, Hashed->Size); + return Hashed->Index; +} + +TypeIndex TypeSerializer::nextTypeIndex() const { + return TypeIndex::fromArrayIndex(SeenRecords.size()); +} + +bool TypeSerializer::isInFieldList() const { + return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST; +} + +MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() { + assert(isInFieldList()); + return getCurrentRecordData().drop_front(CurrentSegment.length()); +} + +MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() { + return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset()); +} + +Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) { + RecordPrefix Prefix; + Prefix.RecordKind = Kind; + Prefix.RecordLen = 0; + if (auto EC = Writer.writeObject(Prefix)) + return EC; + return Error::success(); +} + +Expected<MutableArrayRef<uint8_t>> +TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) { + uint32_t Align = Record.size() % 4; + if (Align == 0) + return Record; + + int PaddingBytes = 4 - Align; + int N = PaddingBytes; + while (PaddingBytes > 0) { + uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes); + if (auto EC = Writer.writeInteger(Pad)) + return std::move(EC); + --PaddingBytes; + } + return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N); +} + +TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage, bool Hash) + : RecordStorage(Storage), RecordBuffer(MaxRecordLength * 2), + Stream(RecordBuffer, llvm::support::little), Writer(Stream), + Mapping(Writer) { + // RecordBuffer needs to be able to hold enough data so that if we are 1 + // byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes, + // we won't overflow. + if (Hash) + Hasher = make_unique<TypeHasher>(Storage); +} + +TypeSerializer::~TypeSerializer() = default; + +ArrayRef<ArrayRef<uint8_t>> TypeSerializer::records() const { + return SeenRecords; +} + +void TypeSerializer::reset() { + if (Hasher) + Hasher->reset(); + Writer.setOffset(0); + CurrentSegment = RecordSegment(); + FieldListSegments.clear(); + TypeKind.reset(); + MemberKind.reset(); + SeenRecords.clear(); +} + +TypeIndex TypeSerializer::insertRecordBytes(ArrayRef<uint8_t> &Record) { + assert(!TypeKind.hasValue() && "Already in a type mapping!"); + assert(Writer.getOffset() == 0 && "Stream has data already!"); + + if (Hasher) { + TypeIndex ActualTI = Hasher->getOrCreateRecord(Record, nextTypeIndex()); + if (nextTypeIndex() == ActualTI) + SeenRecords.push_back(Record); + return ActualTI; + } + + TypeIndex NewTI = nextTypeIndex(); + uint8_t *Stable = RecordStorage.Allocate<uint8_t>(Record.size()); + memcpy(Stable, Record.data(), Record.size()); + Record = ArrayRef<uint8_t>(Stable, Record.size()); + SeenRecords.push_back(Record); + return NewTI; +} + +TypeIndex TypeSerializer::insertRecord(const RemappedType &Record) { + assert(!TypeKind.hasValue() && "Already in a type mapping!"); + assert(Writer.getOffset() == 0 && "Stream has data already!"); + + TypeIndex TI; + ArrayRef<uint8_t> OriginalData = Record.OriginalRecord.RecordData; + if (Record.Mappings.empty()) { + // This record did not remap any type indices. Just write it. + return insertRecordBytes(OriginalData); + } + + // At least one type index was remapped. Before we can hash it we have to + // copy the full record bytes, re-write each type index, then hash the copy. + // We do this in temporary storage since only the DenseMap can decide whether + // this record already exists, and if it does we don't want the memory to + // stick around. + RemapStorage.resize(OriginalData.size()); + ::memcpy(&RemapStorage[0], OriginalData.data(), OriginalData.size()); + uint8_t *ContentBegin = RemapStorage.data() + sizeof(RecordPrefix); + for (const auto &M : Record.Mappings) { + // First 4 bytes of every record are the record prefix, but the mapping + // offset is relative to the content which starts after. + *(TypeIndex *)(ContentBegin + M.first) = M.second; + } + auto RemapRef = makeArrayRef(RemapStorage); + return insertRecordBytes(RemapRef); +} + +Error TypeSerializer::visitTypeBegin(CVType &Record) { + assert(!TypeKind.hasValue() && "Already in a type mapping!"); + assert(Writer.getOffset() == 0 && "Stream has data already!"); + + if (auto EC = writeRecordPrefix(Record.kind())) + return EC; + + TypeKind = Record.kind(); + if (auto EC = Mapping.visitTypeBegin(Record)) + return EC; + + return Error::success(); +} + +Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) { + assert(TypeKind.hasValue() && "Not in a type mapping!"); + if (auto EC = Mapping.visitTypeEnd(Record)) + return std::move(EC); + + // Update the record's length and fill out the CVType members to point to + // the stable memory holding the record's data. + auto ThisRecordData = getCurrentRecordData(); + auto ExpectedData = addPadding(ThisRecordData); + if (!ExpectedData) + return ExpectedData.takeError(); + ThisRecordData = *ExpectedData; + + RecordPrefix *Prefix = + reinterpret_cast<RecordPrefix *>(ThisRecordData.data()); + Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t); + + Record.Type = *TypeKind; + Record.RecordData = ThisRecordData; + + // insertRecordBytes assumes we're not in a mapping, so do this first. + TypeKind.reset(); + Writer.setOffset(0); + + TypeIndex InsertedTypeIndex = insertRecordBytes(Record.RecordData); + + // Write out each additional segment in reverse order, and update each + // record's continuation index to point to the previous one. + for (auto X : reverse(FieldListSegments)) { + auto CIBytes = X.take_back(sizeof(uint32_t)); + support::ulittle32_t *CI = + reinterpret_cast<support::ulittle32_t *>(CIBytes.data()); + assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder"); + *CI = InsertedTypeIndex.getIndex(); + InsertedTypeIndex = insertRecordBytes(X); + } + + FieldListSegments.clear(); + CurrentSegment.SubRecords.clear(); + + return InsertedTypeIndex; +} + +Error TypeSerializer::visitTypeEnd(CVType &Record) { + auto ExpectedIndex = visitTypeEndGetIndex(Record); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + return Error::success(); +} + +Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) { + assert(isInFieldList() && "Not in a field list!"); + assert(!MemberKind.hasValue() && "Already in a member record!"); + MemberKind = Record.Kind; + + if (auto EC = Mapping.visitMemberBegin(Record)) + return EC; + + return Error::success(); +} + +Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) { + if (auto EC = Mapping.visitMemberEnd(Record)) + return EC; + + // Check if this subrecord makes the current segment not fit in 64K minus + // the space for a continuation record (8 bytes). If the segment does not + // fit, insert a continuation record. + if (Writer.getOffset() > MaxRecordLength - ContinuationLength) { + MutableArrayRef<uint8_t> Data = getCurrentRecordData(); + SubRecord LastSubRecord = CurrentSegment.SubRecords.back(); + uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size; + auto CopyData = Data.take_front(CopySize); + auto LeftOverData = Data.drop_front(CopySize); + assert(LastSubRecord.Size == LeftOverData.size()); + + // Allocate stable storage for the record and copy the old record plus + // continuation over. + uint16_t LengthWithSize = CopySize + ContinuationLength; + assert(LengthWithSize <= MaxRecordLength); + RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data()); + Prefix->RecordLen = LengthWithSize - sizeof(uint16_t); + + uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize); + auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize); + MutableBinaryByteStream CS(SavedSegment, llvm::support::little); + BinaryStreamWriter CW(CS); + if (auto EC = CW.writeBytes(CopyData)) + return EC; + if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX)) + return EC; + if (auto EC = CW.writeInteger<uint16_t>(0)) + return EC; + if (auto EC = CW.writeInteger<uint32_t>(0xB0C0B0C0)) + return EC; + FieldListSegments.push_back(SavedSegment); + + // Write a new placeholder record prefix to mark the start of this new + // top-level record. + Writer.setOffset(0); + if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST)) + return EC; + + // Then move over the subrecord that overflowed the old segment to the + // beginning of this segment. Note that we have to use memmove here + // instead of Writer.writeBytes(), because the new and old locations + // could overlap. + ::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(), + LeftOverData.size()); + // And point the segment writer at the end of that subrecord. + Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix)); + + CurrentSegment.SubRecords.clear(); + CurrentSegment.SubRecords.push_back(LastSubRecord); + } + + // Update the CVMemberRecord since we may have shifted around or gotten + // padded. + Record.Data = getCurrentSubRecordData(); + + MemberKind.reset(); + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp new file mode 100644 index 000000000000..71a0966df036 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -0,0 +1,350 @@ +//===-- TypeStreamMerger.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.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/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +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 TypeVisitorCallbacks { +public: + explicit TypeStreamMerger(SmallVectorImpl<TypeIndex> &SourceToDest, + TypeServerHandler *Handler) + : Handler(Handler), IndexMap(SourceToDest) { + SourceToDest.clear(); + } + + static const TypeIndex Untranslated; + + Error visitTypeBegin(CVType &Record) override; + Error visitTypeEnd(CVType &Record) override; + + Error mergeTypesAndIds(TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes); + Error mergeIdRecords(TypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids); + Error mergeTypeRecords(TypeTableBuilder &Dest, const CVTypeArray &Types); + +private: + Error doit(const CVTypeArray &Types); + + void addMapping(TypeIndex Idx); + + bool remapTypeIndex(TypeIndex &Idx); + bool remapItemIndex(TypeIndex &Idx); + + bool remapIndices(RemappedType &Record, ArrayRef<TiReference> Refs) { + auto OriginalData = Record.OriginalRecord.content(); + bool Success = true; + for (auto &Ref : Refs) { + uint32_t Offset = Ref.Offset; + ArrayRef<uint8_t> Bytes = + OriginalData.slice(Ref.Offset, sizeof(TypeIndex)); + ArrayRef<TypeIndex> TIs(reinterpret_cast<const TypeIndex *>(Bytes.data()), + Ref.Count); + for (auto TI : TIs) { + TypeIndex NewTI = TI; + bool ThisSuccess = (Ref.Kind == TiRefKind::IndexRef) + ? remapItemIndex(NewTI) + : remapTypeIndex(NewTI); + if (ThisSuccess && NewTI != TI) + Record.Mappings.emplace_back(Offset, NewTI); + Offset += sizeof(TypeIndex); + Success &= ThisSuccess; + } + } + return Success; + } + + bool remapIndex(TypeIndex &Idx, ArrayRef<TypeIndex> Map); + + size_t slotForIndex(TypeIndex Idx) const { + assert(!Idx.isSimple() && "simple type indices have no slots"); + return Idx.getIndex() - TypeIndex::FirstNonSimpleIndex; + } + + Error errorCorruptRecord() const { + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); + } + + Error writeRecord(TypeTableBuilder &Dest, const RemappedType &Record, + bool RemapSuccess) { + TypeIndex DestIdx = Untranslated; + if (RemapSuccess) + DestIdx = Dest.writeSerializedRecord(Record); + addMapping(DestIdx); + return Error::success(); + } + + Error writeTypeRecord(const CVType &Record) { + TypeIndex DestIdx = + DestTypeStream->writeSerializedRecord(Record.RecordData); + addMapping(DestIdx); + return Error::success(); + } + + Error writeTypeRecord(const RemappedType &Record, bool RemapSuccess) { + return writeRecord(*DestTypeStream, Record, RemapSuccess); + } + + Error writeIdRecord(const RemappedType &Record, bool RemapSuccess) { + return writeRecord(*DestIdStream, Record, RemapSuccess); + } + + Optional<Error> LastError; + + bool IsSecondPass = false; + + unsigned NumBadIndices = 0; + + TypeIndex CurIndex{TypeIndex::FirstNonSimpleIndex}; + + TypeTableBuilder *DestIdStream = nullptr; + TypeTableBuilder *DestTypeStream = nullptr; + TypeServerHandler *Handler = nullptr; + + // 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; +}; + +} // end anonymous namespace + +const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); + +Error TypeStreamMerger::visitTypeBegin(CVType &Rec) { + RemappedType R(Rec); + SmallVector<TiReference, 32> Refs; + discoverTypeIndices(Rec.RecordData, Refs); + bool Success = remapIndices(R, Refs); + switch (Rec.kind()) { + 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 writeIdRecord(R, Success); + default: + return writeTypeRecord(R, Success); + } + return Error::success(); +} + +Error TypeStreamMerger::visitTypeEnd(CVType &Rec) { + ++CurIndex; + if (!IsSecondPass) + assert(IndexMap.size() == slotForIndex(CurIndex) && + "visitKnownRecord should add one index map entry"); + return Error::success(); +} + +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::remapIndex(TypeIndex &Idx, ArrayRef<TypeIndex> Map) { + // 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 (MapPos < Map.size() && Map[MapPos] != Untranslated) { + Idx = Map[MapPos]; + return true; + } + + // 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. + LastError = joinErrors(std::move(*LastError), errorCorruptRecord()); + } + + ++NumBadIndices; + + // This type index is invalid. Remap this to "not translated by cvpack", + // and return failure. + Idx = Untranslated; + return false; +} + +bool TypeStreamMerger::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 (DestTypeStream == nullptr) + return remapIndex(Idx, TypeLookup); + + assert(TypeLookup.empty()); + return remapIndex(Idx, IndexMap); +} + +bool TypeStreamMerger::remapItemIndex(TypeIndex &Idx) { + assert(DestIdStream); + return remapIndex(Idx, IndexMap); +} + +Error TypeStreamMerger::mergeTypeRecords(TypeTableBuilder &Dest, + const CVTypeArray &Types) { + DestTypeStream = &Dest; + + return doit(Types); +} + +Error TypeStreamMerger::mergeIdRecords(TypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + const CVTypeArray &Ids) { + DestIdStream = &Dest; + TypeLookup = TypeSourceToDest; + + return doit(Ids); +} + +Error TypeStreamMerger::mergeTypesAndIds(TypeTableBuilder &DestIds, + TypeTableBuilder &DestTypes, + const CVTypeArray &IdsAndTypes) { + DestIdStream = &DestIds; + DestTypeStream = &DestTypes; + + return doit(IdsAndTypes); +} + +Error TypeStreamMerger::doit(const CVTypeArray &Types) { + LastError = Error::success(); + + // We don't want to deserialize records. I guess this flag is poorly named, + // but it really means "Don't deserialize records before switching on the + // concrete type. + // FIXME: We can probably get even more speed here if we don't use the visitor + // pipeline here, but instead write the switch ourselves. I don't think it + // would buy us much since it's already pretty fast, but it's probably worth + // a few cycles. + if (auto EC = + codeview::visitTypeStream(Types, *this, VDS_BytesExternal, Handler)) + 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 = + codeview::visitTypeStream(Types, *this, VDS_BytesExternal, Handler)) + 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"); + } + } + + Error Ret = std::move(*LastError); + LastError.reset(); + return Ret; +} + +Error llvm::codeview::mergeTypeRecords(TypeTableBuilder &Dest, + SmallVectorImpl<TypeIndex> &SourceToDest, + TypeServerHandler *Handler, + const CVTypeArray &Types) { + TypeStreamMerger M(SourceToDest, Handler); + return M.mergeTypeRecords(Dest, Types); +} + +Error llvm::codeview::mergeIdRecords(TypeTableBuilder &Dest, + ArrayRef<TypeIndex> TypeSourceToDest, + SmallVectorImpl<TypeIndex> &SourceToDest, + const CVTypeArray &Ids) { + TypeStreamMerger M(SourceToDest, nullptr); + return M.mergeIdRecords(Dest, TypeSourceToDest, Ids); +} + +Error llvm::codeview::mergeTypeAndIdRecords( + TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, + SmallVectorImpl<TypeIndex> &SourceToDest, TypeServerHandler *Handler, + const CVTypeArray &IdsAndTypes) { + + TypeStreamMerger M(SourceToDest, Handler); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes); +} diff --git a/contrib/llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp b/contrib/llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp new file mode 100644 index 000000000000..699694fde928 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/CodeView/TypeTableCollection.cpp @@ -0,0 +1,82 @@ +//===- TypeTableCollection.cpp -------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeTableCollection.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryStreamReader.h" + +using namespace llvm; +using namespace llvm::codeview; + +static void error(Error &&EC) { + assert(!static_cast<bool>(EC)); + if (EC) + consumeError(std::move(EC)); +} + +TypeTableCollection::TypeTableCollection(ArrayRef<ArrayRef<uint8_t>> Records) + : Records(Records), Database(Records.size()) {} + +Optional<TypeIndex> TypeTableCollection::getFirst() { + if (empty()) + return None; + return TypeIndex::fromArrayIndex(0); +} + +Optional<TypeIndex> TypeTableCollection::getNext(TypeIndex Prev) { + ++Prev; + assert(Prev.toArrayIndex() <= size()); + if (Prev.toArrayIndex() == size()) + return None; + return Prev; +} + +void TypeTableCollection::ensureTypeExists(TypeIndex Index) { + assert(hasCapacityFor(Index)); + + if (Database.contains(Index)) + return; + + BinaryByteStream Bytes(Records[Index.toArrayIndex()], support::little); + + CVType Type; + uint32_t Len; + error(VarStreamArrayExtractor<CVType>::extract(Bytes, Len, Type)); + + TypeDatabaseVisitor DBV(Database); + error(codeview::visitTypeRecord(Type, Index, DBV)); + assert(Database.contains(Index)); +} + +CVType TypeTableCollection::getType(TypeIndex Index) { + ensureTypeExists(Index); + return Database.getTypeRecord(Index); +} + +StringRef TypeTableCollection::getTypeName(TypeIndex Index) { + if (!Index.isSimple()) + ensureTypeExists(Index); + return Database.getTypeName(Index); +} + +bool TypeTableCollection::contains(TypeIndex Index) { + return Database.contains(Index); +} + +uint32_t TypeTableCollection::size() { return Records.size(); } + +uint32_t TypeTableCollection::capacity() { return Records.size(); } + +bool TypeTableCollection::hasCapacityFor(TypeIndex Index) const { + return Index.toArrayIndex() < Records.size(); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 000000000000..e7b4b777b43f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,224 @@ +//===- DWARFAbbreviationDeclaration.cpp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +void DWARFAbbreviationDeclaration::clear() { + Code = 0; + Tag = DW_TAG_null; + CodeByteSize = 0; + HasChildren = false; + AttributeSpecs.clear(); + FixedAttributeSize.reset(); +} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() { + clear(); +} + +bool +DWARFAbbreviationDeclaration::extract(DataExtractor Data, + uint32_t* OffsetPtr) { + clear(); + const uint32_t Offset = *OffsetPtr; + Code = Data.getULEB128(OffsetPtr); + if (Code == 0) { + return false; + } + CodeByteSize = *OffsetPtr - Offset; + Tag = static_cast<llvm::dwarf::Tag>(Data.getULEB128(OffsetPtr)); + if (Tag == DW_TAG_null) { + clear(); + return false; + } + uint8_t ChildrenByte = Data.getU8(OffsetPtr); + HasChildren = (ChildrenByte == DW_CHILDREN_yes); + // Assign a value to our optional FixedAttributeSize member variable. If + // this member variable still has a value after the while loop below, then + // all attribute data in this abbreviation declaration has a fixed byte size. + FixedAttributeSize = FixedSizeInfo(); + + // Read all of the abbreviation attributes and forms. + while (true) { + auto A = static_cast<Attribute>(Data.getULEB128(OffsetPtr)); + auto F = static_cast<Form>(Data.getULEB128(OffsetPtr)); + if (A && F) { + Optional<int64_t> V; + bool IsImplicitConst = (F == DW_FORM_implicit_const); + if (IsImplicitConst) + V = Data.getSLEB128(OffsetPtr); + else if (auto Size = DWARFFormValue::getFixedByteSize(F)) + V = *Size; + AttributeSpecs.push_back(AttributeSpec(A, F, V)); + if (IsImplicitConst) + continue; + // If this abbrevation still has a fixed byte size, then update the + // FixedAttributeSize as needed. + if (FixedAttributeSize) { + if (V) + FixedAttributeSize->NumBytes += *V; + else { + switch (F) { + case DW_FORM_addr: + ++FixedAttributeSize->NumAddrs; + break; + + case DW_FORM_ref_addr: + ++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: + ++FixedAttributeSize->NumDwarfOffsets; + break; + + default: + // 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; + } + } + } + } 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 { + auto tagString = TagString(getTag()); + OS << '[' << getCode() << "] "; + if (!tagString.empty()) + OS << tagString; + else + OS << format("DW_TAG_Unknown_%x", getTag()); + OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no") << '\n'; + for (const AttributeSpec &Spec : AttributeSpecs) { + OS << '\t'; + auto attrString = AttributeString(Spec.Attr); + if (!attrString.empty()) + OS << attrString; + else + OS << format("DW_AT_Unknown_%x", Spec.Attr); + OS << '\t'; + auto formString = FormEncodingString(Spec.Form); + if (!formString.empty()) + OS << formString; + else + OS << format("DW_FORM_Unknown_%x", Spec.Form); + if (Spec.isImplicitConst()) + OS << '\t' << *Spec.ByteSizeOrValue; + OS << '\n'; + } + OS << '\n'; +} + +Optional<uint32_t> +DWARFAbbreviationDeclaration::findAttributeIndex(dwarf::Attribute Attr) const { + for (uint32_t i = 0, e = AttributeSpecs.size(); i != e; ++i) { + if (AttributeSpecs[i].Attr == Attr) + return i; + } + return None; +} + +Optional<DWARFFormValue> DWARFAbbreviationDeclaration::getAttributeValue( + const uint32_t DIEOffset, const dwarf::Attribute Attr, + const DWARFUnit &U) const { + Optional<uint32_t> MatchAttrIndex = findAttributeIndex(Attr); + if (!MatchAttrIndex) + return None; + + auto DebugInfoData = U.getDebugInfoExtractor(); + + // Add the byte size of ULEB that for the abbrev Code so we can start + // skipping the attribute data. + uint32_t Offset = DIEOffset + CodeByteSize; + uint32_t AttrIndex = 0; + for (const auto &Spec : AttributeSpecs) { + if (*MatchAttrIndex == AttrIndex) { + // We have arrived at the attribute to extract, extract if from Offset. + DWARFFormValue FormValue(Spec.Form); + if (Spec.isImplicitConst()) { + FormValue.setSValue(*Spec.ByteSizeOrValue); + return FormValue; + } + if (FormValue.extractValue(DebugInfoData, &Offset, &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); + ++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 (ByteSizeOrValue) + return ByteSizeOrValue; + Optional<int64_t> S; + auto FixedByteSize = DWARFFormValue::getFixedByteSize(Form, &U); + if (FixedByteSize) + S = *FixedByteSize; + return S; +} + +Optional<size_t> DWARFAbbreviationDeclaration::getFixedAttributesByteSize( + const DWARFUnit &U) const { + if (FixedAttributeSize) + return FixedAttributeSize->getByteSize(U); + return None; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp new file mode 100644 index 000000000000..a12f8adfafe5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -0,0 +1,139 @@ +//===- DWARFAcceleratorTable.cpp ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cstddef> +#include <cstdint> +#include <utility> + +using namespace llvm; + +bool DWARFAcceleratorTable::extract() { + uint32_t Offset = 0; + + // Check that we can at least read the header. + if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength)+4)) + return false; + + Hdr.Magic = AccelSection.getU32(&Offset); + Hdr.Version = AccelSection.getU16(&Offset); + Hdr.HashFunction = AccelSection.getU16(&Offset); + Hdr.NumBuckets = AccelSection.getU32(&Offset); + Hdr.NumHashes = AccelSection.getU32(&Offset); + Hdr.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). + if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength + + Hdr.NumBuckets*4 + Hdr.NumHashes*8)) + return false; + + 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)); + } + + return true; +} + +LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { + // Dump the header. + OS << "Magic = " << format("0x%08x", Hdr.Magic) << '\n' + << "Version = " << format("0x%04x", Hdr.Version) << '\n' + << "Hash function = " << format("0x%08x", Hdr.HashFunction) << '\n' + << "Bucket count = " << Hdr.NumBuckets << '\n' + << "Hashes count = " << Hdr.NumHashes << '\n' + << "HeaderData length = " << Hdr.HeaderDataLength << '\n' + << "DIE offset base = " << HdrData.DIEOffsetBase << '\n' + << "Number of atoms = " << HdrData.Atoms.size() << '\n'; + + unsigned i = 0; + SmallVector<DWARFFormValue, 3> AtomForms; + for (const auto &Atom: HdrData.Atoms) { + OS << format("Atom[%d] Type: ", i++); + auto TypeString = dwarf::AtomTypeString(Atom.first); + if (!TypeString.empty()) + OS << TypeString; + else + OS << format("DW_ATOM_Unknown_0x%x", Atom.first); + OS << " Form: "; + auto FormString = dwarf::FormEncodingString(Atom.second); + if (!FormString.empty()) + OS << FormString; + else + OS << format("DW_FORM_Unknown_0x%x", Atom.second); + OS << '\n'; + AtomForms.push_back(DWARFFormValue(Atom.second)); + } + + // Now go through the actual tables and dump them. + uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength; + unsigned HashesBase = Offset + Hdr.NumBuckets * 4; + unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4; + + for (unsigned Bucket = 0; Bucket < Hdr.NumBuckets; ++Bucket) { + unsigned Index = AccelSection.getU32(&Offset); + + OS << format("Bucket[%d]\n", Bucket); + if (Index == UINT32_MAX) { + OS << " EMPTY\n"; + continue; + } + + for (unsigned HashIdx = Index; HashIdx < Hdr.NumHashes; ++HashIdx) { + unsigned HashOffset = HashesBase + HashIdx*4; + unsigned OffsetsOffset = OffsetsBase + HashIdx*4; + uint32_t Hash = AccelSection.getU32(&HashOffset); + + if (Hash % Hdr.NumBuckets != Bucket) + break; + + unsigned DataOffset = AccelSection.getU32(&OffsetsOffset); + OS << format(" Hash = 0x%08x Offset = 0x%08x\n", Hash, DataOffset); + if (!AccelSection.isValidOffset(DataOffset)) { + OS << " Invalid section offset\n"; + continue; + } + while (AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) { + unsigned StringOffset = + getRelocatedValue(AccelSection, 4, &DataOffset, &Relocs); + if (!StringOffset) + break; + OS << format(" Name: %08x \"%s\"\n", StringOffset, + StringSection.getCStr(&StringOffset)); + unsigned NumData = AccelSection.getU32(&DataOffset); + for (unsigned Data = 0; Data < NumData; ++Data) { + OS << format(" Data[%d] => ", Data); + unsigned i = 0; + for (auto &Atom : AtomForms) { + OS << format("{Atom[%d]: ", i++); + if (Atom.extractValue(AccelSection, &DataOffset, nullptr)) + Atom.dump(OS); + else + OS << "Error extracting the value"; + OS << "} "; + } + OS << '\n'; + } + } + } + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp new file mode 100644 index 000000000000..6e550f2e9ec9 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,36 @@ +//===-- DWARFCompileUnit.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +void DWARFCompileUnit::dump(raw_ostream &OS) { + OS << format("0x%08x", getOffset()) << ": Compile Unit:" + << " length = " << format("0x%08x", getLength()) + << " version = " << format("0x%04x", getVersion()); + if (getVersion() >= 5) + OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << " abbr_offset = " << format("0x%04x", getAbbreviations()->getOffset()) + << " addr_size = " << format("0x%02x", getAddressByteSize()) + << " (next unit at " << format("0x%08x", getNextUnitOffset()) + << ")\n"; + + if (DWARFDie CUDie = getUnitDIE(false)) + CUDie.dump(OS, -1U); + else + OS << "<compile unit can't be parsed!>\n\n"; +} + +// VTable anchor. +DWARFCompileUnit::~DWARFCompileUnit() = default; diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp new file mode 100644 index 000000000000..5ed55ce4c0dc --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -0,0 +1,1235 @@ +//===- DWARFContext.cpp ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.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/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/Object/Decompressor.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/RelocVisitor.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cstdint> +#include <map> +#include <set> +#include <string> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +#define DEBUG_TYPE "dwarf" + +typedef DWARFDebugLine::LineTable DWARFLineTable; +typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind; +typedef DILineInfoSpecifier::FunctionNameKind FunctionNameKind; + +uint64_t llvm::getRelocatedValue(const DataExtractor &Data, uint32_t Size, + uint32_t *Off, const RelocAddrMap *Relocs, + uint64_t *SectionIndex) { + if (!Relocs) + return Data.getUnsigned(Off, Size); + RelocAddrMap::const_iterator AI = Relocs->find(*Off); + if (AI == Relocs->end()) + return Data.getUnsigned(Off, Size); + if (SectionIndex) + *SectionIndex = AI->second.SectionIndex; + return Data.getUnsigned(Off, Size) + AI->second.Value; +} + +static void dumpAccelSection(raw_ostream &OS, StringRef Name, + const DWARFSection& Section, StringRef StringSection, + bool LittleEndian) { + DataExtractor AccelSection(Section.Data, LittleEndian, 0); + DataExtractor StrData(StringSection, LittleEndian, 0); + OS << "\n." << Name << " contents:\n"; + DWARFAcceleratorTable Accel(AccelSection, StrData, Section.Relocs); + if (!Accel.extract()) + return; + Accel.dump(OS); +} + +void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH, + bool SummarizeTypes) { + if (DumpType == DIDT_All || DumpType == DIDT_Abbrev) { + OS << ".debug_abbrev contents:\n"; + getDebugAbbrev()->dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_AbbrevDwo) + if (const DWARFDebugAbbrev *D = getDebugAbbrevDWO()) { + OS << "\n.debug_abbrev.dwo contents:\n"; + D->dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_Info) { + OS << "\n.debug_info contents:\n"; + for (const auto &CU : compile_units()) + CU->dump(OS); + } + + if ((DumpType == DIDT_All || DumpType == DIDT_InfoDwo) && + getNumDWOCompileUnits()) { + OS << "\n.debug_info.dwo contents:\n"; + for (const auto &DWOCU : dwo_compile_units()) + DWOCU->dump(OS); + } + + if ((DumpType == DIDT_All || DumpType == DIDT_Types) && getNumTypeUnits()) { + OS << "\n.debug_types contents:\n"; + for (const auto &TUS : type_unit_sections()) + for (const auto &TU : TUS) + TU->dump(OS, SummarizeTypes); + } + + if ((DumpType == DIDT_All || DumpType == DIDT_TypesDwo) && + getNumDWOTypeUnits()) { + OS << "\n.debug_types.dwo contents:\n"; + for (const auto &DWOTUS : dwo_type_unit_sections()) + for (const auto &DWOTU : DWOTUS) + DWOTU->dump(OS, SummarizeTypes); + } + + if (DumpType == DIDT_All || DumpType == DIDT_Loc) { + OS << "\n.debug_loc contents:\n"; + getDebugLoc()->dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_LocDwo) { + OS << "\n.debug_loc.dwo contents:\n"; + getDebugLocDWO()->dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_Frames) { + OS << "\n.debug_frame contents:\n"; + getDebugFrame()->dump(OS); + if (DumpEH) { + OS << "\n.eh_frame contents:\n"; + getEHFrame()->dump(OS); + } + } + + if (DumpType == DIDT_All || DumpType == DIDT_Macro) { + OS << "\n.debug_macinfo contents:\n"; + getDebugMacro()->dump(OS); + } + + uint32_t offset = 0; + if (DumpType == DIDT_All || DumpType == DIDT_Aranges) { + OS << "\n.debug_aranges contents:\n"; + DataExtractor arangesData(getARangeSection(), isLittleEndian(), 0); + DWARFDebugArangeSet set; + while (set.extract(arangesData, &offset)) + set.dump(OS); + } + + uint8_t savedAddressByteSize = 0; + if (DumpType == DIDT_All || DumpType == DIDT_Line) { + OS << "\n.debug_line contents:\n"; + for (const auto &CU : compile_units()) { + savedAddressByteSize = CU->getAddressByteSize(); + auto CUDIE = CU->getUnitDIE(); + if (!CUDIE) + continue; + if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) { + DataExtractor lineData(getLineSection().Data, isLittleEndian(), + savedAddressByteSize); + DWARFDebugLine::LineTable LineTable; + uint32_t Offset = *StmtOffset; + LineTable.parse(lineData, &getLineSection().Relocs, &Offset); + LineTable.dump(OS); + } + } + } + + if (DumpType == DIDT_All || DumpType == DIDT_CUIndex) { + OS << "\n.debug_cu_index contents:\n"; + getCUIndex().dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_TUIndex) { + OS << "\n.debug_tu_index contents:\n"; + getTUIndex().dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_LineDwo) { + OS << "\n.debug_line.dwo contents:\n"; + unsigned stmtOffset = 0; + DataExtractor lineData(getLineDWOSection().Data, isLittleEndian(), + savedAddressByteSize); + DWARFDebugLine::LineTable LineTable; + while (LineTable.Prologue.parse(lineData, &stmtOffset)) { + LineTable.dump(OS); + LineTable.clear(); + } + } + + if (DumpType == DIDT_All || DumpType == DIDT_Str) { + OS << "\n.debug_str contents:\n"; + DataExtractor strData(getStringSection(), isLittleEndian(), 0); + offset = 0; + uint32_t strOffset = 0; + while (const char *s = strData.getCStr(&offset)) { + OS << format("0x%8.8x: \"%s\"\n", strOffset, s); + strOffset = offset; + } + } + + if ((DumpType == DIDT_All || DumpType == DIDT_StrDwo) && + !getStringDWOSection().empty()) { + OS << "\n.debug_str.dwo contents:\n"; + DataExtractor strDWOData(getStringDWOSection(), isLittleEndian(), 0); + offset = 0; + uint32_t strDWOOffset = 0; + while (const char *s = strDWOData.getCStr(&offset)) { + OS << format("0x%8.8x: \"%s\"\n", strDWOOffset, s); + strDWOOffset = offset; + } + } + + if (DumpType == DIDT_All || DumpType == DIDT_Ranges) { + OS << "\n.debug_ranges contents:\n"; + // In fact, different compile units may have different address byte + // sizes, but for simplicity we just use the address byte size of the last + // compile unit (there is no easy and fast way to associate address range + // list and the compile unit it describes). + DataExtractor rangesData(getRangeSection().Data, isLittleEndian(), + savedAddressByteSize); + offset = 0; + DWARFDebugRangeList rangeList; + while (rangeList.extract(rangesData, &offset, getRangeSection().Relocs)) + rangeList.dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_Pubnames) + DWARFDebugPubTable(getPubNamesSection(), isLittleEndian(), false) + .dump("debug_pubnames", OS); + + if (DumpType == DIDT_All || DumpType == DIDT_Pubtypes) + DWARFDebugPubTable(getPubTypesSection(), isLittleEndian(), false) + .dump("debug_pubtypes", OS); + + if (DumpType == DIDT_All || DumpType == DIDT_GnuPubnames) + DWARFDebugPubTable(getGnuPubNamesSection(), isLittleEndian(), + true /* GnuStyle */) + .dump("debug_gnu_pubnames", OS); + + if (DumpType == DIDT_All || DumpType == DIDT_GnuPubtypes) + DWARFDebugPubTable(getGnuPubTypesSection(), isLittleEndian(), + true /* GnuStyle */) + .dump("debug_gnu_pubtypes", OS); + + if ((DumpType == DIDT_All || DumpType == DIDT_StrOffsetsDwo) && + !getStringOffsetDWOSection().empty()) { + OS << "\n.debug_str_offsets.dwo contents:\n"; + DataExtractor strOffsetExt(getStringOffsetDWOSection(), isLittleEndian(), + 0); + offset = 0; + uint64_t size = getStringOffsetDWOSection().size(); + while (offset < size) { + OS << format("0x%8.8x: ", offset); + OS << format("%8.8x\n", strOffsetExt.getU32(&offset)); + } + } + + if ((DumpType == DIDT_All || DumpType == DIDT_GdbIndex) && + !getGdbIndexSection().empty()) { + OS << "\n.gnu_index contents:\n"; + getGdbIndex().dump(OS); + } + + if (DumpType == DIDT_All || DumpType == DIDT_AppleNames) + dumpAccelSection(OS, "apple_names", getAppleNamesSection(), + getStringSection(), isLittleEndian()); + + if (DumpType == DIDT_All || DumpType == DIDT_AppleTypes) + dumpAccelSection(OS, "apple_types", getAppleTypesSection(), + getStringSection(), isLittleEndian()); + + if (DumpType == DIDT_All || DumpType == DIDT_AppleNamespaces) + dumpAccelSection(OS, "apple_namespaces", getAppleNamespacesSection(), + getStringSection(), isLittleEndian()); + + if (DumpType == DIDT_All || DumpType == DIDT_AppleObjC) + dumpAccelSection(OS, "apple_objc", getAppleObjCSection(), + getStringSection(), isLittleEndian()); +} + +DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { + // FIXME: Improve this for the case where this DWO file is really a DWP file + // with an index - use the index for lookup instead of a linear search. + for (const auto &DWOCU : dwo_compile_units()) + if (DWOCU->getDWOId() == Hash) + return DWOCU.get(); + return nullptr; +} + +DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) { + parseCompileUnits(); + if (auto *CU = CUs.getUnitForOffset(Offset)) + return CU->getDIEForOffset(Offset); + return DWARFDie(); +} + +namespace { + +class Verifier { + raw_ostream &OS; + DWARFContext &DCtx; +public: + Verifier(raw_ostream &S, DWARFContext &D) : OS(S), DCtx(D) {} + + bool HandleDebugInfo() { + bool Success = true; + // A map that tracks all references (converted absolute references) so we + // can verify each reference points to a valid DIE and not an offset that + // lies between to valid DIEs. + std::map<uint64_t, std::set<uint32_t>> ReferenceToDIEOffsets; + + OS << "Verifying .debug_info...\n"; + for (const auto &CU : DCtx.compile_units()) { + unsigned NumDies = CU->getNumDIEs(); + for (unsigned I = 0; I < NumDies; ++I) { + auto Die = CU->getDIEAtIndex(I); + const auto Tag = Die.getTag(); + if (Tag == DW_TAG_null) + continue; + for (auto AttrValue : Die.attributes()) { + const auto Attr = AttrValue.Attr; + const auto Form = AttrValue.Value.getForm(); + switch (Attr) { + case DW_AT_ranges: + // Make sure the offset in the DW_AT_ranges attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DCtx.getRangeSection().Data.size()) { + Success = false; + OS << "error: DW_AT_ranges offset is beyond .debug_ranges " + "bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + } else { + Success = false; + OS << "error: DIE has invalid DW_AT_ranges encoding:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + case DW_AT_stmt_list: + // Make sure the offset in the DW_AT_stmt_list attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DCtx.getLineSection().Data.size()) { + Success = false; + OS << "error: DW_AT_stmt_list offset is beyond .debug_line " + "bounds: " + << format("0x%08" PRIx32, *SectionOffset) << "\n"; + CU->getUnitDIE().dump(OS, 0); + OS << "\n"; + } + } else { + Success = false; + OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + + default: + break; + } + 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 DieCU = Die.getDwarfUnit(); + auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset(); + auto CUOffset = AttrValue.Value.getRawUValue(); + if (CUOffset >= CUSize) { + Success = false; + OS << "error: " << FormEncodingString(Form) << " CU offset " + << format("0x%08" PRIx32, CUOffset) + << " is invalid (must be less than CU size of " + << format("0x%08" PRIx32, CUSize) << "):\n"; + Die.dump(OS, 0); + OS << "\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 >= DCtx.getInfoSection().Data.size()) { + Success = false; + OS << "error: DW_FORM_ref_addr offset beyond .debug_info " + "bounds:\n"; + Die.dump(OS, 0); + OS << "\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 >= DCtx.getStringSection().size()) { + Success = false; + OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + } + default: + break; + } + } + } + } + + // 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"; + for (auto Pair: ReferenceToDIEOffsets) { + auto Die = DCtx.getDIEForOffset(Pair.first); + if (Die) + continue; + Success = false; + OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first) + << ". Offset is in between DIEs:\n"; + for (auto Offset: Pair.second) { + auto ReferencingDie = DCtx.getDIEForOffset(Offset); + ReferencingDie.dump(OS, 0); + OS << "\n"; + } + OS << "\n"; + } + return Success; + } + + bool HandleDebugLine() { + std::map<uint64_t, DWARFDie> StmtListToDie; + bool Success = true; + OS << "Verifying .debug_line...\n"; + for (const auto &CU : DCtx.compile_units()) { + uint32_t LineTableOffset = 0; + auto CUDie = CU->getUnitDIE(); + auto StmtFormValue = CUDie.find(DW_AT_stmt_list); + if (!StmtFormValue) { + // No line table for this compile unit. + continue; + } + // 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. + if (auto StmtSectionOffset = toSectionOffset(StmtFormValue)) { + LineTableOffset = *StmtSectionOffset; + if (LineTableOffset >= DCtx.getLineSection().Data.size()) { + // Make sure we don't get a valid line table back if the offset + // is wrong. + assert(DCtx.getLineTableForUnit(CU.get()) == 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; + } else { + auto Iter = StmtListToDie.find(LineTableOffset); + if (Iter != StmtListToDie.end()) { + Success = false; + OS << "error: two compile unit DIEs, " + << format("0x%08" PRIx32, Iter->second.getOffset()) << " and " + << format("0x%08" PRIx32, CUDie.getOffset()) + << ", have the same DW_AT_stmt_list section offset:\n"; + Iter->second.dump(OS, 0); + CUDie.dump(OS, 0); + OS << '\n'; + // Already verified this line table before, no need to do it again. + continue; + } + StmtListToDie[LineTableOffset] = CUDie; + } + } + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + if (!LineTable) { + Success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + CUDie.dump(OS, 0); + OS << '\n'; + continue; + } + uint32_t MaxFileIndex = LineTable->Prologue.FileNames.size(); + uint64_t PrevAddress = 0; + uint32_t RowIndex = 0; + for (const auto &Row : LineTable->Rows) { + if (Row.Address < PrevAddress) { + Success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] 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'; + } + + if (Row.File > MaxFileIndex) { + Success = false; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "][" << RowIndex << "] has invalid file index " << Row.File + << " (valid values are [1," << MaxFileIndex << "]):\n"; + DWARFDebugLine::Row::dumpTableHeader(OS); + Row.dump(OS); + OS << '\n'; + } + if (Row.EndSequence) + PrevAddress = 0; + else + PrevAddress = Row.Address; + ++RowIndex; + } + } + return Success; + } +}; + +} // anonymous namespace + +bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) { + bool Success = true; + DWARFVerifier verifier(OS, *this); + if (DumpType == DIDT_All || DumpType == DIDT_Info) { + if (!verifier.handleDebugInfo()) + Success = false; + } + if (DumpType == DIDT_All || DumpType == DIDT_Line) { + if (!verifier.handleDebugLine()) + Success = false; + } + return Success; +} +const DWARFUnitIndex &DWARFContext::getCUIndex() { + if (CUIndex) + return *CUIndex; + + DataExtractor CUIndexData(getCUIndexSection(), isLittleEndian(), 0); + + CUIndex = llvm::make_unique<DWARFUnitIndex>(DW_SECT_INFO); + CUIndex->parse(CUIndexData); + return *CUIndex; +} + +const DWARFUnitIndex &DWARFContext::getTUIndex() { + if (TUIndex) + return *TUIndex; + + DataExtractor TUIndexData(getTUIndexSection(), isLittleEndian(), 0); + + TUIndex = llvm::make_unique<DWARFUnitIndex>(DW_SECT_TYPES); + TUIndex->parse(TUIndexData); + return *TUIndex; +} + +DWARFGdbIndex &DWARFContext::getGdbIndex() { + if (GdbIndex) + return *GdbIndex; + + DataExtractor GdbIndexData(getGdbIndexSection(), true /*LE*/, 0); + GdbIndex = llvm::make_unique<DWARFGdbIndex>(); + GdbIndex->parse(GdbIndexData); + return *GdbIndex; +} + +const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { + if (Abbrev) + return Abbrev.get(); + + DataExtractor abbrData(getAbbrevSection(), isLittleEndian(), 0); + + Abbrev.reset(new DWARFDebugAbbrev()); + Abbrev->extract(abbrData); + return Abbrev.get(); +} + +const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { + if (AbbrevDWO) + return AbbrevDWO.get(); + + DataExtractor abbrData(getAbbrevDWOSection(), isLittleEndian(), 0); + AbbrevDWO.reset(new DWARFDebugAbbrev()); + AbbrevDWO->extract(abbrData); + return AbbrevDWO.get(); +} + +const DWARFDebugLoc *DWARFContext::getDebugLoc() { + if (Loc) + return Loc.get(); + + DataExtractor LocData(getLocSection().Data, isLittleEndian(), 0); + Loc.reset(new DWARFDebugLoc(getLocSection().Relocs)); + // assume all compile units have the same address byte size + if (getNumCompileUnits()) + Loc->parse(LocData, getCompileUnitAtIndex(0)->getAddressByteSize()); + return Loc.get(); +} + +const DWARFDebugLocDWO *DWARFContext::getDebugLocDWO() { + if (LocDWO) + return LocDWO.get(); + + DataExtractor LocData(getLocDWOSection().Data, isLittleEndian(), 0); + LocDWO.reset(new DWARFDebugLocDWO()); + LocDWO->parse(LocData); + 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 + DataExtractor debugFrameData(getDebugFrameSection(), isLittleEndian(), + getAddressSize()); + DebugFrame.reset(new DWARFDebugFrame(false /* IsEH */)); + DebugFrame->parse(debugFrameData); + return DebugFrame.get(); +} + +const DWARFDebugFrame *DWARFContext::getEHFrame() { + if (EHFrame) + return EHFrame.get(); + + DataExtractor debugFrameData(getEHFrameSection(), isLittleEndian(), + getAddressSize()); + DebugFrame.reset(new DWARFDebugFrame(true /* IsEH */)); + DebugFrame->parse(debugFrameData); + return DebugFrame.get(); +} + +const DWARFDebugMacro *DWARFContext::getDebugMacro() { + if (Macro) + return Macro.get(); + + DataExtractor MacinfoData(getMacinfoSection(), isLittleEndian(), 0); + Macro.reset(new DWARFDebugMacro()); + Macro->parse(MacinfoData); + return Macro.get(); +} + +const DWARFLineTable * +DWARFContext::getLineTableForUnit(DWARFUnit *U) { + if (!Line) + Line.reset(new DWARFDebugLine(&getLineSection().Relocs)); + + auto UnitDIE = U->getUnitDIE(); + if (!UnitDIE) + return nullptr; + + auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); + if (!Offset) + return nullptr; // No line table for this compile unit. + + uint32_t stmtOffset = *Offset + U->getLineTableOffset(); + // See if the line table is cached. + if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) + return lt; + + // Make sure the offset is good before we try to parse. + if (stmtOffset >= U->getLineSection().size()) + return nullptr; + + // We have to parse it first. + DataExtractor lineData(U->getLineSection(), isLittleEndian(), + U->getAddressByteSize()); + return Line->getOrParseLineTable(lineData, stmtOffset); +} + +void DWARFContext::parseCompileUnits() { + CUs.parse(*this, getInfoSection()); +} + +void DWARFContext::parseTypeUnits() { + if (!TUs.empty()) + return; + for (const auto &I : getTypesSections()) { + TUs.emplace_back(); + TUs.back().parse(*this, I.second); + } +} + +void DWARFContext::parseDWOCompileUnits() { + DWOCUs.parseDWO(*this, getInfoDWOSection()); +} + +void DWARFContext::parseDWOTypeUnits() { + if (!DWOTUs.empty()) + return; + for (const auto &I : getTypesDWOSections()) { + DWOTUs.emplace_back(); + DWOTUs.back().parseDWO(*this, I.second); + } +} + +DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint32_t Offset) { + parseCompileUnits(); + return CUs.getUnitForOffset(Offset); +} + +DWARFCompileUnit *DWARFContext::getCompileUnitForAddress(uint64_t Address) { + // First, get the offset of the compile unit. + uint32_t CUOffset = getDebugAranges()->findAddress(Address); + // Retrieve the compile unit. + return getCompileUnitForOffset(CUOffset); +} + +static bool getFunctionNameAndStartLineForAddress(DWARFCompileUnit *CU, + uint64_t Address, + FunctionNameKind Kind, + std::string &FunctionName, + uint32_t &StartLine) { + // The address may correspond to instruction in some inlined function, + // so we have to build the chain of inlined functions and take the + // name of the topmost function in it. + SmallVector<DWARFDie, 4> InlinedChain; + CU->getInlinedChainForAddress(Address, InlinedChain); + if (InlinedChain.empty()) + return false; + + const DWARFDie &DIE = InlinedChain[0]; + bool FoundResult = false; + const char *Name = nullptr; + if (Kind != FunctionNameKind::None && (Name = DIE.getSubroutineName(Kind))) { + FunctionName = Name; + FoundResult = true; + } + if (auto DeclLineResult = DIE.getDeclLine()) { + StartLine = DeclLineResult; + FoundResult = true; + } + + return FoundResult; +} + +DILineInfo DWARFContext::getLineInfoForAddress(uint64_t Address, + DILineInfoSpecifier Spec) { + DILineInfo Result; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return Result; + getFunctionNameAndStartLineForAddress(CU, Address, Spec.FNKind, + Result.FunctionName, + Result.StartLine); + if (Spec.FLIKind != FileLineInfoKind::None) { + if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) + LineTable->getFileLineInfoForAddress(Address, CU->getCompilationDir(), + Spec.FLIKind, Result); + } + return Result; +} + +DILineInfoTable +DWARFContext::getLineInfoForAddressRange(uint64_t Address, uint64_t Size, + DILineInfoSpecifier Spec) { + DILineInfoTable Lines; + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return Lines; + + std::string FunctionName = "<invalid>"; + uint32_t StartLine = 0; + getFunctionNameAndStartLineForAddress(CU, Address, Spec.FNKind, FunctionName, + StartLine); + + // If the Specifier says we don't need FileLineInfo, just + // return the top-most function at the starting address. + if (Spec.FLIKind == FileLineInfoKind::None) { + DILineInfo Result; + Result.FunctionName = FunctionName; + Result.StartLine = StartLine; + Lines.push_back(std::make_pair(Address, Result)); + return Lines; + } + + const DWARFLineTable *LineTable = getLineTableForUnit(CU); + + // Get the index of row we're looking for in the line table. + std::vector<uint32_t> RowVector; + if (!LineTable->lookupAddressRange(Address, Size, RowVector)) + return Lines; + + for (uint32_t RowIndex : RowVector) { + // Take file number and line/column from the row. + const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex]; + DILineInfo Result; + LineTable->getFileNameByIndex(Row.File, CU->getCompilationDir(), + Spec.FLIKind, Result.FileName); + Result.FunctionName = FunctionName; + Result.Line = Row.Line; + Result.Column = Row.Column; + Result.StartLine = StartLine; + Lines.push_back(std::make_pair(Row.Address, Result)); + } + + return Lines; +} + +DIInliningInfo +DWARFContext::getInliningInfoForAddress(uint64_t Address, + DILineInfoSpecifier Spec) { + DIInliningInfo InliningInfo; + + DWARFCompileUnit *CU = getCompileUnitForAddress(Address); + if (!CU) + return InliningInfo; + + const DWARFLineTable *LineTable = nullptr; + SmallVector<DWARFDie, 4> InlinedChain; + CU->getInlinedChainForAddress(Address, InlinedChain); + if (InlinedChain.size() == 0) { + // If there is no DIE for address (e.g. it is in unavailable .dwo file), + // try to at least get file/line info from symbol table. + if (Spec.FLIKind != FileLineInfoKind::None) { + DILineInfo Frame; + LineTable = getLineTableForUnit(CU); + if (LineTable && + LineTable->getFileLineInfoForAddress(Address, CU->getCompilationDir(), + Spec.FLIKind, Frame)) + InliningInfo.addFrame(Frame); + } + return InliningInfo; + } + + uint32_t CallFile = 0, CallLine = 0, CallColumn = 0, CallDiscriminator = 0; + for (uint32_t i = 0, n = InlinedChain.size(); i != n; i++) { + DWARFDie &FunctionDIE = InlinedChain[i]; + DILineInfo Frame; + // Get function name if necessary. + if (const char *Name = FunctionDIE.getSubroutineName(Spec.FNKind)) + Frame.FunctionName = Name; + if (auto DeclLineResult = FunctionDIE.getDeclLine()) + Frame.StartLine = DeclLineResult; + if (Spec.FLIKind != FileLineInfoKind::None) { + if (i == 0) { + // For the topmost frame, initialize the line table of this + // compile unit and fetch file/line info from it. + LineTable = getLineTableForUnit(CU); + // For the topmost routine, get file/line info from line table. + if (LineTable) + LineTable->getFileLineInfoForAddress(Address, CU->getCompilationDir(), + Spec.FLIKind, Frame); + } else { + // Otherwise, use call file, call line and call column from + // previous DIE in inlined chain. + if (LineTable) + LineTable->getFileNameByIndex(CallFile, CU->getCompilationDir(), + Spec.FLIKind, Frame.FileName); + Frame.Line = CallLine; + Frame.Column = CallColumn; + Frame.Discriminator = CallDiscriminator; + } + // Get call file/line/column of a current DIE. + if (i + 1 < n) { + FunctionDIE.getCallerFrame(CallFile, CallLine, CallColumn, + CallDiscriminator); + } + } + InliningInfo.addFrame(Frame); + } + return InliningInfo; +} + +std::shared_ptr<DWARFContext> +DWARFContext::getDWOContext(StringRef AbsolutePath) { + if (auto S = DWP.lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + std::weak_ptr<DWOFile> *Entry = &DWOFiles[AbsolutePath]; + + if (auto S = Entry->lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + SmallString<128> DWPName; + Expected<OwningBinary<ObjectFile>> Obj = [&] { + if (!CheckedForDWP) { + (getFileName() + ".dwp").toVector(DWPName); + auto Obj = object::ObjectFile::createObjectFile(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 = llvm::make_unique<DWARFContextInMemory>(*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("error: 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("error: 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); +} + +Error DWARFContextInMemory::maybeDecompress(const 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<32> Out; + if (auto Err = Decompressor->resizeAndDecompress(Out)) + return Err; + + UncompressedSections.emplace_back(std::move(Out)); + Data = UncompressedSections.back(); + + return Error::success(); +} + +DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, + const LoadedObjectInfo *L) + : FileName(Obj.getFileName()), IsLittleEndian(Obj.isLittleEndian()), + AddressSize(Obj.getBytesInAddress()) { + for (const SectionRef &Section : Obj.sections()) { + StringRef name; + Section.getName(name); + // Skip BSS and Virtual sections, they aren't interesting. + bool IsBSS = Section.isBSS(); + if (IsBSS) + continue; + bool IsVirtual = Section.isVirtual(); + if (IsVirtual) + continue; + StringRef data; + + section_iterator RelocatedSection = Section.getRelocatedSection(); + // Try to obtain an already relocated version of this section. + // Else use the unrelocated section from the object file. We'll have to + // apply relocations ourselves later. + if (!L || !L->getLoadedSectionContents(*RelocatedSection, data)) + Section.getContents(data); + + if (auto Err = maybeDecompress(Section, name, data)) { + errs() << "error: failed to decompress '" + name + "', " + + toString(std::move(Err)) + << '\n'; + 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. + + if (StringRef *SectionData = MapSectionToMember(name)) { + *SectionData = data; + if (name == "debug_ranges") { + // FIXME: Use the other dwo range section when we emit it. + RangeDWOSection.Data = data; + } + } else if (name == "debug_types") { + // Find debug_types data by section rather than name as there are + // multiple, comdat grouped, debug_types sections. + TypesSections[Section].Data = data; + } else if (name == "debug_types.dwo") { + TypesDWOSections[Section].Data = data; + } + + if (RelocatedSection == Obj.section_end()) + continue; + + StringRef RelSecName; + StringRef RelSecData; + RelocatedSection->getName(RelSecName); + + // If the section we're relocating was relocated already by the JIT, + // then we used the relocated version above, so we do not need to process + // relocations for it now. + if (L && L->getLoadedSectionContents(*RelocatedSection, RelSecData)) + continue; + + // In Mach-o files, the relocations do not need to be applied if + // there is no load offset to apply. The value read at the + // relocation point already factors in the section address + // (actually applying the relocations will produce wrong results + // as the section address will be added twice). + if (!L && isa<MachOObjectFile>(&Obj)) + continue; + + RelSecName = RelSecName.substr( + RelSecName.find_first_not_of("._")); // Skip . and _ prefixes. + + // TODO: Add support for relocations in other sections as needed. + // Record relocations for the debug_info and debug_line sections. + RelocAddrMap *Map = + StringSwitch<RelocAddrMap *>(RelSecName) + .Case("debug_info", &InfoSection.Relocs) + .Case("debug_loc", &LocSection.Relocs) + .Case("debug_info.dwo", &InfoDWOSection.Relocs) + .Case("debug_line", &LineSection.Relocs) + .Case("debug_ranges", &RangeSection.Relocs) + .Case("debug_addr", &AddrSection.Relocs) + .Case("apple_names", &AppleNamesSection.Relocs) + .Case("apple_types", &AppleTypesSection.Relocs) + .Case("apple_namespaces", &AppleNamespacesSection.Relocs) + .Case("apple_namespac", &AppleNamespacesSection.Relocs) + .Case("apple_objc", &AppleObjCSection.Relocs) + .Default(nullptr); + if (!Map) { + // Find debug_types relocs by section rather than name as there are + // multiple, comdat grouped, debug_types sections. + if (RelSecName == "debug_types") + Map = &TypesSections[*RelocatedSection].Relocs; + else if (RelSecName == "debug_types.dwo") + Map = &TypesDWOSections[*RelocatedSection].Relocs; + else + continue; + } + + if (Section.relocation_begin() == Section.relocation_end()) + continue; + + // Symbol to [address, section index] cache mapping. + std::map<SymbolRef, SymInfo> AddrCache; + for (const RelocationRef &Reloc : Section.relocations()) { + // FIXME: it's not clear how to correctly handle scattered + // relocations. + if (isRelocScattered(Obj, Reloc)) + continue; + + Expected<SymInfo> SymInfoOrErr = getSymbolInfo(Obj, Reloc, L, AddrCache); + if (!SymInfoOrErr) { + errs() << toString(SymInfoOrErr.takeError()) << '\n'; + continue; + } + + object::RelocVisitor V(Obj); + uint64_t Val = V.visit(Reloc.getType(), Reloc, SymInfoOrErr->Address); + if (V.error()) { + SmallString<32> Name; + Reloc.getTypeName(Name); + errs() << "error: failed to compute relocation: " << Name << "\n"; + continue; + } + llvm::RelocAddrEntry Rel = {SymInfoOrErr->SectionIndex, Val}; + Map->insert({Reloc.getOffset(), Rel}); + } + } +} + +DWARFContextInMemory::DWARFContextInMemory( + const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, uint8_t AddrSize, + bool isLittleEndian) + : IsLittleEndian(isLittleEndian), AddressSize(AddrSize) { + for (const auto &SecIt : Sections) { + if (StringRef *SectionData = MapSectionToMember(SecIt.first())) + *SectionData = SecIt.second->getBuffer(); + } +} + +StringRef *DWARFContextInMemory::MapSectionToMember(StringRef Name) { + return StringSwitch<StringRef *>(Name) + .Case("debug_info", &InfoSection.Data) + .Case("debug_abbrev", &AbbrevSection) + .Case("debug_loc", &LocSection.Data) + .Case("debug_line", &LineSection.Data) + .Case("debug_aranges", &ARangeSection) + .Case("debug_frame", &DebugFrameSection) + .Case("eh_frame", &EHFrameSection) + .Case("debug_str", &StringSection) + .Case("debug_ranges", &RangeSection.Data) + .Case("debug_macinfo", &MacinfoSection) + .Case("debug_pubnames", &PubNamesSection) + .Case("debug_pubtypes", &PubTypesSection) + .Case("debug_gnu_pubnames", &GnuPubNamesSection) + .Case("debug_gnu_pubtypes", &GnuPubTypesSection) + .Case("debug_info.dwo", &InfoDWOSection.Data) + .Case("debug_abbrev.dwo", &AbbrevDWOSection) + .Case("debug_loc.dwo", &LocDWOSection.Data) + .Case("debug_line.dwo", &LineDWOSection.Data) + .Case("debug_str.dwo", &StringDWOSection) + .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) + .Case("debug_addr", &AddrSection.Data) + .Case("apple_names", &AppleNamesSection.Data) + .Case("apple_types", &AppleTypesSection.Data) + .Case("apple_namespaces", &AppleNamespacesSection.Data) + .Case("apple_namespac", &AppleNamespacesSection.Data) + .Case("apple_objc", &AppleObjCSection.Data) + .Case("debug_cu_index", &CUIndexSection) + .Case("debug_tu_index", &TUIndexSection) + .Case("gdb_index", &GdbIndexSection) + // Any more debug info sections go here. + .Default(nullptr); +} + +void DWARFContextInMemory::anchor() {} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 000000000000..76dd2e4c21bc --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,119 @@ +//===- DWARFDebugAbbrev.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +DWARFAbbreviationDeclarationSet::DWARFAbbreviationDeclarationSet() { + clear(); +} + +void DWARFAbbreviationDeclarationSet::clear() { + Offset = 0; + FirstAbbrCode = 0; + Decls.clear(); +} + +bool DWARFAbbreviationDeclarationSet::extract(DataExtractor Data, + uint32_t *OffsetPtr) { + clear(); + const uint32_t BeginOffset = *OffsetPtr; + Offset = BeginOffset; + DWARFAbbreviationDeclaration AbbrDecl; + uint32_t PrevAbbrCode = 0; + while (AbbrDecl.extract(Data, OffsetPtr)) { + if (FirstAbbrCode == 0) { + FirstAbbrCode = AbbrDecl.getCode(); + } else { + if (PrevAbbrCode + 1 != AbbrDecl.getCode()) { + // Codes are not consecutive, can't do O(1) lookups. + FirstAbbrCode = UINT32_MAX; + } + } + PrevAbbrCode = AbbrDecl.getCode(); + Decls.push_back(std::move(AbbrDecl)); + } + return BeginOffset != *OffsetPtr; +} + +void DWARFAbbreviationDeclarationSet::dump(raw_ostream &OS) const { + for (const auto &Decl : Decls) + Decl.dump(OS); +} + +const DWARFAbbreviationDeclaration * +DWARFAbbreviationDeclarationSet::getAbbreviationDeclaration( + uint32_t AbbrCode) const { + if (FirstAbbrCode == UINT32_MAX) { + for (const auto &Decl : Decls) { + if (Decl.getCode() == AbbrCode) + return &Decl; + } + return nullptr; + } + if (AbbrCode < FirstAbbrCode || AbbrCode >= FirstAbbrCode + Decls.size()) + return nullptr; + return &Decls[AbbrCode - FirstAbbrCode]; +} + +DWARFDebugAbbrev::DWARFDebugAbbrev() { + clear(); +} + +void DWARFDebugAbbrev::clear() { + AbbrDeclSets.clear(); + PrevAbbrOffsetPos = AbbrDeclSets.end(); +} + +void DWARFDebugAbbrev::extract(DataExtractor Data) { + clear(); + + uint32_t Offset = 0; + DWARFAbbreviationDeclarationSet AbbrDecls; + while (Data.isValidOffset(Offset)) { + uint32_t CUAbbrOffset = Offset; + if (!AbbrDecls.extract(Data, &Offset)) + break; + AbbrDeclSets[CUAbbrOffset] = std::move(AbbrDecls); + } +} + +void DWARFDebugAbbrev::dump(raw_ostream &OS) const { + 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); + } + + return nullptr; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 000000000000..ed5d726ae4e2 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,107 @@ +//===- DWARFDebugArangeSet.cpp --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cinttypes> +#include <cstdint> +#include <cstring> + +using namespace llvm; + +void DWARFDebugArangeSet::clear() { + Offset = -1U; + std::memset(&HeaderData, 0, sizeof(Header)); + ArangeDescriptors.clear(); +} + +bool +DWARFDebugArangeSet::extract(DataExtractor data, uint32_t *offset_ptr) { + if (data.isValidOffset(*offset_ptr)) { + ArangeDescriptors.clear(); + Offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header consisting of: a + // 4-byte length containing the length of the set of entries for this + // compilation unit, not including the length field itself; a 2-byte + // version identifier containing the value 2 for DWARF Version 2; a + // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer + // containing the size in bytes of an address (or the offset portion of + // an address for segmented addressing) on the target system; and a + // 1-byte unsigned integer containing the size in bytes of a segment + // descriptor on the target system. This header is followed by a series + // of tuples. Each tuple consists of an address and a length, each in + // the size appropriate for an address on the target architecture. + HeaderData.Length = data.getU32(offset_ptr); + HeaderData.Version = data.getU16(offset_ptr); + HeaderData.CuOffset = data.getU32(offset_ptr); + HeaderData.AddrSize = data.getU8(offset_ptr); + HeaderData.SegSize = data.getU8(offset_ptr); + + // Perform basic validation of the header fields. + if (!data.isValidOffsetForDataOfSize(Offset, HeaderData.Length) || + (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)) { + clear(); + return false; + } + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - Offset; + const uint32_t tuple_size = HeaderData.AddrSize * 2; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = Offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + static_assert(sizeof(arangeDescriptor.Address) == + sizeof(arangeDescriptor.Length), + "Different datatypes for addresses and sizes!"); + assert(sizeof(arangeDescriptor.Address) >= HeaderData.AddrSize); + + while (data.isValidOffset(*offset_ptr)) { + arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.Address || arangeDescriptor.Length) + ArangeDescriptors.push_back(arangeDescriptor); + else + break; // We are done if we get a zero address and length + } + + return !ArangeDescriptors.empty(); + } + return false; +} + +void DWARFDebugArangeSet::dump(raw_ostream &OS) const { + OS << format("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, ", + HeaderData.Length, HeaderData.Version) + << format("cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", + HeaderData.CuOffset, HeaderData.AddrSize, HeaderData.SegSize); + + const uint32_t hex_width = HeaderData.AddrSize * 2; + for (const auto &Desc : ArangeDescriptors) { + OS << format("[0x%*.*" PRIx64 " -", hex_width, hex_width, Desc.Address) + << format(" 0x%*.*" PRIx64 ")\n", + hex_width, hex_width, Desc.getEndAddress()); + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp new file mode 100644 index 000000000000..6601393d7459 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,130 @@ +//===- DWARFDebugAranges.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" +#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/Support/DataExtractor.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <set> +#include <vector> + +using namespace llvm; + +void DWARFDebugAranges::extract(DataExtractor DebugArangesData) { + if (!DebugArangesData.isValidOffset(0)) + return; + uint32_t Offset = 0; + DWARFDebugArangeSet Set; + + while (Set.extract(DebugArangesData, &Offset)) { + uint32_t CUOffset = Set.getCompileUnitDIEOffset(); + for (const auto &Desc : Set.descriptors()) { + uint64_t LowPC = Desc.Address; + uint64_t HighPC = Desc.getEndAddress(); + appendRange(CUOffset, LowPC, HighPC); + } + ParsedCUOffsets.insert(CUOffset); + } +} + +void DWARFDebugAranges::generate(DWARFContext *CTX) { + clear(); + if (!CTX) + return; + + // Extract aranges from .debug_aranges section. + DataExtractor ArangesData(CTX->getARangeSection(), CTX->isLittleEndian(), 0); + extract(ArangesData); + + // Generate aranges from DIEs: even if .debug_aranges section is present, + // it may describe only a small subset of compilation units, so we need to + // manually build aranges for the rest of them. + for (const auto &CU : CTX->compile_units()) { + uint32_t CUOffset = CU->getOffset(); + if (ParsedCUOffsets.insert(CUOffset).second) { + DWARFAddressRangesVector CURanges; + CU->collectAddressRanges(CURanges); + for (const auto &R : CURanges) + appendRange(CUOffset, R.LowPC, R.HighPC); + } + } + + construct(); +} + +void DWARFDebugAranges::clear() { + Endpoints.clear(); + Aranges.clear(); + ParsedCUOffsets.clear(); +} + +void DWARFDebugAranges::appendRange(uint32_t CUOffset, uint64_t LowPC, + uint64_t HighPC) { + if (LowPC >= HighPC) + return; + Endpoints.emplace_back(LowPC, CUOffset, true); + Endpoints.emplace_back(HighPC, CUOffset, false); +} + +void DWARFDebugAranges::construct() { + std::multiset<uint32_t> ValidCUs; // Maintain the set of CUs describing + // a current address range. + std::sort(Endpoints.begin(), Endpoints.end()); + 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. + std::vector<RangeEndpoint> EmptyEndpoints; + EmptyEndpoints.swap(Endpoints); +} + +uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { + if (!Aranges.empty()) { + Range range(Address); + RangeCollIterator begin = Aranges.begin(); + RangeCollIterator end = Aranges.end(); + RangeCollIterator pos = + std::lower_bound(begin, end, range); + + if (pos != end && pos->containsAddress(Address)) { + return pos->CUOffset; + } else if (pos != begin) { + --pos; + if (pos->containsAddress(Address)) + return pos->CUOffset; + } + } + return -1U; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp new file mode 100644 index 000000000000..b55ed6a46849 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -0,0 +1,687 @@ +//===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Dwarf.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; + +/// \brief Abstract frame entry defining the common interface concrete +/// entries implement. +class llvm::FrameEntry { +public: + enum FrameKind {FK_CIE, FK_FDE}; + + FrameEntry(FrameKind K, uint64_t Offset, uint64_t Length) + : Kind(K), Offset(Offset), Length(Length) {} + + virtual ~FrameEntry() = default; + + FrameKind getKind() const { return Kind; } + virtual uint64_t getOffset() const { return Offset; } + + /// \brief Parse and store a sequence of CFI instructions from Data, + /// starting at *Offset and ending at EndOffset. If everything + /// goes well, *Offset should be equal to EndOffset when this method + /// returns. Otherwise, an error occurred. + virtual void parseInstructions(DataExtractor Data, uint32_t *Offset, + uint32_t EndOffset); + + /// \brief Dump the entry header to the given output stream. + virtual void dumpHeader(raw_ostream &OS) const = 0; + + /// \brief Dump the entry's instructions to the given output stream. + virtual void dumpInstructions(raw_ostream &OS) const; + +protected: + const FrameKind Kind; + + /// \brief Offset of this entry in the section. + uint64_t Offset; + + /// \brief Entry length as specified in DWARF. + uint64_t Length; + + /// An entry may contain CFI instructions. An instruction consists of an + /// opcode and an optional sequence of operands. + typedef std::vector<uint64_t> Operands; + struct Instruction { + Instruction(uint8_t Opcode) + : Opcode(Opcode) + {} + + uint8_t Opcode; + Operands Ops; + }; + + std::vector<Instruction> Instructions; + + /// Convenience methods to add a new instruction with the given opcode and + /// operands to the Instructions vector. + void addInstruction(uint8_t Opcode) { + Instructions.push_back(Instruction(Opcode)); + } + + void addInstruction(uint8_t Opcode, uint64_t Operand1) { + Instructions.push_back(Instruction(Opcode)); + Instructions.back().Ops.push_back(Operand1); + } + + void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) { + Instructions.push_back(Instruction(Opcode)); + Instructions.back().Ops.push_back(Operand1); + Instructions.back().Ops.push_back(Operand2); + } +}; + +// See DWARF standard v3, section 7.23 +const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0; +const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; + +void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, + uint32_t EndOffset) { + while (*Offset < EndOffset) { + uint8_t Opcode = Data.getU8(Offset); + // Some instructions have a primary opcode encoded in the top bits. + uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; + + if (Primary) { + // If it's a primary opcode, the first operand is encoded in the bottom + // bits of the opcode itself. + uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; + switch (Primary) { + default: llvm_unreachable("Impossible primary CFI opcode"); + case DW_CFA_advance_loc: + case DW_CFA_restore: + addInstruction(Primary, Op1); + break; + case DW_CFA_offset: + addInstruction(Primary, Op1, Data.getULEB128(Offset)); + break; + } + } else { + // Extended opcode - its value is Opcode itself. + switch (Opcode) { + default: llvm_unreachable("Invalid extended CFI opcode"); + case DW_CFA_nop: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_GNU_window_save: + // No operands + addInstruction(Opcode); + break; + case DW_CFA_set_loc: + // Operands: Address + addInstruction(Opcode, Data.getAddress(Offset)); + break; + case DW_CFA_advance_loc1: + // Operands: 1-byte delta + addInstruction(Opcode, Data.getU8(Offset)); + break; + case DW_CFA_advance_loc2: + // Operands: 2-byte delta + addInstruction(Opcode, Data.getU16(Offset)); + break; + case DW_CFA_advance_loc4: + // Operands: 4-byte delta + addInstruction(Opcode, Data.getU32(Offset)); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + // 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: + case DW_CFA_expression: + case DW_CFA_val_expression: + // TODO: implement this + report_fatal_error("Values with expressions not implemented yet!"); + } + } + } +} + +namespace { + +/// \brief DWARF Common Information Entry (CIE) +class CIE : public FrameEntry { +public: + // CIEs (and FDEs) are simply container classes, so the only sensible way to + // create them is by providing the full parsed contents in the constructor. + CIE(uint64_t Offset, uint64_t Length, uint8_t Version, + SmallString<8> Augmentation, uint8_t AddressSize, + uint8_t SegmentDescriptorSize, uint64_t CodeAlignmentFactor, + int64_t DataAlignmentFactor, uint64_t ReturnAddressRegister, + SmallString<8> AugmentationData, uint32_t FDEPointerEncoding, + uint32_t LSDAPointerEncoding) + : FrameEntry(FK_CIE, Offset, Length), Version(Version), + Augmentation(std::move(Augmentation)), AddressSize(AddressSize), + SegmentDescriptorSize(SegmentDescriptorSize), + CodeAlignmentFactor(CodeAlignmentFactor), + DataAlignmentFactor(DataAlignmentFactor), + ReturnAddressRegister(ReturnAddressRegister), + AugmentationData(std::move(AugmentationData)), + FDEPointerEncoding(FDEPointerEncoding), + LSDAPointerEncoding(LSDAPointerEncoding) {} + + ~CIE() override = default; + + StringRef getAugmentationString() const { return Augmentation; } + uint64_t getCodeAlignmentFactor() const { return CodeAlignmentFactor; } + int64_t getDataAlignmentFactor() const { return DataAlignmentFactor; } + + uint32_t getFDEPointerEncoding() const { + return FDEPointerEncoding; + } + + uint32_t getLSDAPointerEncoding() const { + return LSDAPointerEncoding; + } + + void dumpHeader(raw_ostream &OS) const override { + OS << format("%08x %08x %08x CIE", + (uint32_t)Offset, (uint32_t)Length, DW_CIE_ID) + << "\n"; + OS << format(" Version: %d\n", Version); + OS << " Augmentation: \"" << Augmentation << "\"\n"; + if (Version >= 4) { + OS << format(" Address size: %u\n", + (uint32_t)AddressSize); + OS << format(" Segment desc size: %u\n", + (uint32_t)SegmentDescriptorSize); + } + OS << format(" Code alignment factor: %u\n", + (uint32_t)CodeAlignmentFactor); + OS << format(" Data alignment factor: %d\n", + (int32_t)DataAlignmentFactor); + OS << format(" Return address column: %d\n", + (int32_t)ReturnAddressRegister); + if (!AugmentationData.empty()) { + OS << " Augmentation data: "; + for (uint8_t Byte : AugmentationData) + OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf); + OS << "\n"; + } + OS << "\n"; + } + + static bool classof(const FrameEntry *FE) { + return FE->getKind() == FK_CIE; + } + +private: + /// The following fields are defined in section 6.4.1 of the DWARF standard v4 + uint8_t Version; + SmallString<8> Augmentation; + uint8_t AddressSize; + uint8_t SegmentDescriptorSize; + uint64_t CodeAlignmentFactor; + int64_t DataAlignmentFactor; + uint64_t ReturnAddressRegister; + + // The following are used when the CIE represents an EH frame entry. + SmallString<8> AugmentationData; + uint32_t FDEPointerEncoding; + uint32_t LSDAPointerEncoding; +}; + +/// \brief DWARF Frame Description Entry (FDE) +class FDE : public FrameEntry { +public: + // Each FDE has a CIE it's "linked to". Our FDE contains is constructed with + // an offset to the CIE (provided by parsing the FDE header). The CIE itself + // is obtained lazily once it's actually required. + FDE(uint64_t Offset, uint64_t Length, int64_t LinkedCIEOffset, + uint64_t InitialLocation, uint64_t AddressRange, + CIE *Cie) + : FrameEntry(FK_FDE, Offset, Length), LinkedCIEOffset(LinkedCIEOffset), + InitialLocation(InitialLocation), AddressRange(AddressRange), + LinkedCIE(Cie) {} + + ~FDE() override = default; + + CIE *getLinkedCIE() const { return LinkedCIE; } + + void dumpHeader(raw_ostream &OS) const override { + OS << format("%08x %08x %08x FDE ", + (uint32_t)Offset, (uint32_t)Length, (int32_t)LinkedCIEOffset); + OS << format("cie=%08x pc=%08x...%08x\n", + (int32_t)LinkedCIEOffset, + (uint32_t)InitialLocation, + (uint32_t)InitialLocation + (uint32_t)AddressRange); + } + + static bool classof(const FrameEntry *FE) { + return FE->getKind() == FK_FDE; + } + +private: + /// The following fields are defined in section 6.4.1 of the DWARF standard v3 + uint64_t LinkedCIEOffset; + uint64_t InitialLocation; + uint64_t AddressRange; + CIE *LinkedCIE; +}; + +/// \brief Types of operands to CF instructions. +enum OperandType { + OT_Unset, + OT_None, + OT_Address, + OT_Offset, + OT_FactoredCodeOffset, + OT_SignedFactDataOffset, + OT_UnsignedFactDataOffset, + OT_Register, + OT_Expression +}; + +} // end anonymous namespace + +/// \brief Initialize the array describing the types of operands. +static ArrayRef<OperandType[2]> getOperandTypes() { + static OperandType OpTypes[DW_CFA_restore+1][2]; + +#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); +} + +static ArrayRef<OperandType[2]> OpTypes = getOperandTypes(); + +/// \brief Print \p Opcode's operand number \p OperandIdx which has +/// value \p Operand. +static void printOperand(raw_ostream &OS, uint8_t Opcode, unsigned OperandIdx, + uint64_t Operand, uint64_t CodeAlignmentFactor, + int64_t DataAlignmentFactor) { + assert(OperandIdx < 2); + OperandType Type = OpTypes[Opcode][OperandIdx]; + + switch (Type) { + case OT_Unset: { + OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; + auto OpcodeName = CallFrameString(Opcode); + 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: + OS << " expression"; + break; + } +} + +void FrameEntry::dumpInstructions(raw_ostream &OS) const { + uint64_t CodeAlignmentFactor = 0; + int64_t DataAlignmentFactor = 0; + const CIE *Cie = dyn_cast<CIE>(this); + + if (!Cie) + Cie = cast<FDE>(this)->getLinkedCIE(); + if (Cie) { + CodeAlignmentFactor = Cie->getCodeAlignmentFactor(); + DataAlignmentFactor = Cie->getDataAlignmentFactor(); + } + + for (const auto &Instr : Instructions) { + uint8_t Opcode = Instr.Opcode; + if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) + Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK; + OS << " " << CallFrameString(Opcode) << ":"; + for (unsigned i = 0; i < Instr.Ops.size(); ++i) + printOperand(OS, Opcode, i, Instr.Ops[i], CodeAlignmentFactor, + DataAlignmentFactor); + OS << '\n'; + } +} + +DWARFDebugFrame::DWARFDebugFrame(bool IsEH) : IsEH(IsEH) {} + +DWARFDebugFrame::~DWARFDebugFrame() = default; + +static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, + uint32_t Offset, int Length) { + errs() << "DUMP: "; + for (int i = 0; i < Length; ++i) { + uint8_t c = Data.getU8(&Offset); + errs().write_hex(c); errs() << " "; + } + errs() << "\n"; +} + +static unsigned getSizeForEncoding(const DataExtractor &Data, + unsigned symbolEncoding) { + unsigned format = symbolEncoding & 0x0f; + switch (format) { + default: llvm_unreachable("Unknown Encoding"); + case DW_EH_PE_absptr: + case DW_EH_PE_signed: + return Data.getAddressSize(); + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + return 2; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + return 4; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + return 8; + } +} + +static uint64_t readPointer(const DataExtractor &Data, uint32_t &Offset, + unsigned Encoding) { + switch (getSizeForEncoding(Data, Encoding)) { + case 2: + return Data.getU16(&Offset); + case 4: + return Data.getU32(&Offset); + case 8: + return Data.getU64(&Offset); + default: + llvm_unreachable("Illegal data size"); + } +} + +void DWARFDebugFrame::parse(DataExtractor Data) { + uint32_t Offset = 0; + DenseMap<uint32_t, CIE *> CIEs; + + while (Data.isValidOffset(Offset)) { + uint32_t StartOffset = Offset; + + auto ReportError = [StartOffset](const char *ErrorMsg) { + std::string Str; + raw_string_ostream OS(Str); + OS << format(ErrorMsg, StartOffset); + OS.flush(); + report_fatal_error(Str); + }; + + bool IsDWARF64 = false; + uint64_t Length = Data.getU32(&Offset); + uint64_t Id; + + if (Length == UINT32_MAX) { + // DWARF-64 is distinguished by the first 32 bits of the initial length + // field being 0xffffffff. Then, the next 64 bits are the actual entry + // length. + IsDWARF64 = true; + Length = Data.getU64(&Offset); + } + + // At this point, Offset points to the next field after Length. + // Length is the structure size excluding itself. Compute an offset one + // past the end of the structure (needed to know how many instructions to + // read). + // TODO: For honest DWARF64 support, DataExtractor will have to treat + // offset_ptr as uint64_t* + uint32_t StartStructureOffset = Offset; + uint32_t EndStructureOffset = Offset + static_cast<uint32_t>(Length); + + // The Id field's size depends on the DWARF format + Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4); + bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) || + Id == DW_CIE_ID || + (IsEH && !Id)); + + if (IsCIE) { + uint8_t Version = Data.getU8(&Offset); + const char *Augmentation = Data.getCStr(&Offset); + StringRef AugmentationString(Augmentation ? Augmentation : ""); + uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : + Data.getU8(&Offset); + Data.setAddressSize(AddressSize); + uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(&Offset); + uint64_t CodeAlignmentFactor = Data.getULEB128(&Offset); + int64_t DataAlignmentFactor = Data.getSLEB128(&Offset); + uint64_t ReturnAddressRegister = Data.getULEB128(&Offset); + + // Parse the augmentation data for EH CIEs + StringRef AugmentationData(""); + uint32_t FDEPointerEncoding = DW_EH_PE_omit; + uint32_t LSDAPointerEncoding = DW_EH_PE_omit; + if (IsEH) { + Optional<uint32_t> PersonalityEncoding; + Optional<uint64_t> Personality; + + Optional<uint64_t> AugmentationLength; + uint32_t StartAugmentationOffset; + uint32_t EndAugmentationOffset; + + // Walk the augmentation string to get all the augmentation data. + for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { + switch (AugmentationString[i]) { + default: + ReportError("Unknown augmentation character in entry at %lx"); + case 'L': + LSDAPointerEncoding = Data.getU8(&Offset); + break; + case 'P': { + if (Personality) + ReportError("Duplicate personality in entry at %lx"); + PersonalityEncoding = Data.getU8(&Offset); + Personality = readPointer(Data, Offset, *PersonalityEncoding); + break; + } + case 'R': + FDEPointerEncoding = Data.getU8(&Offset); + break; + case 'z': + if (i) + ReportError("'z' must be the first character at %lx"); + // Parse the augmentation length first. We only parse it if + // the string contains a 'z'. + AugmentationLength = Data.getULEB128(&Offset); + StartAugmentationOffset = Offset; + EndAugmentationOffset = Offset + + static_cast<uint32_t>(*AugmentationLength); + } + } + + if (AugmentationLength.hasValue()) { + if (Offset != EndAugmentationOffset) + ReportError("Parsing augmentation data at %lx failed"); + + AugmentationData = Data.getData().slice(StartAugmentationOffset, + EndAugmentationOffset); + } + } + + auto Cie = llvm::make_unique<CIE>(StartOffset, Length, Version, + AugmentationString, AddressSize, + SegmentDescriptorSize, + CodeAlignmentFactor, + DataAlignmentFactor, + ReturnAddressRegister, + AugmentationData, FDEPointerEncoding, + LSDAPointerEncoding); + CIEs[StartOffset] = Cie.get(); + Entries.emplace_back(std::move(Cie)); + } else { + // FDE + uint64_t CIEPointer = Id; + uint64_t InitialLocation = 0; + uint64_t AddressRange = 0; + CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer]; + + if (IsEH) { + // The address size is encoded in the CIE we reference. + if (!Cie) + ReportError("Parsing FDE data at %lx failed due to missing CIE"); + + InitialLocation = readPointer(Data, Offset, + Cie->getFDEPointerEncoding()); + AddressRange = readPointer(Data, Offset, + Cie->getFDEPointerEncoding()); + + StringRef AugmentationString = Cie->getAugmentationString(); + if (!AugmentationString.empty()) { + // Parse the augmentation length and data for this FDE. + uint64_t AugmentationLength = Data.getULEB128(&Offset); + + uint32_t EndAugmentationOffset = + Offset + static_cast<uint32_t>(AugmentationLength); + + // Decode the LSDA if the CIE augmentation string said we should. + if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) + readPointer(Data, Offset, Cie->getLSDAPointerEncoding()); + + if (Offset != EndAugmentationOffset) + ReportError("Parsing augmentation data at %lx failed"); + } + } else { + InitialLocation = Data.getAddress(&Offset); + AddressRange = Data.getAddress(&Offset); + } + + Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, + InitialLocation, AddressRange, + Cie)); + } + + Entries.back()->parseInstructions(Data, &Offset, EndStructureOffset); + + if (Offset != EndStructureOffset) + ReportError("Parsing entry instructions at %lx failed"); + } +} + +void DWARFDebugFrame::dump(raw_ostream &OS) const { + OS << "\n"; + for (const auto &Entry : Entries) { + Entry->dumpHeader(OS); + Entry->dumpInstructions(OS); + OS << "\n"; + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 000000000000..35f673c7acc6 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,70 @@ +//===- DWARFDebugInfoEntry.cpp --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include <cstddef> +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, + uint32_t *OffsetPtr) { + DataExtractor DebugInfoData = U.getDebugInfoExtractor(); + const uint32_t UEndOffset = U.getNextUnitOffset(); + return extractFast(U, OffsetPtr, DebugInfoData, UEndOffset, 0); +} + +bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, uint32_t *OffsetPtr, + const DataExtractor &DebugInfoData, + uint32_t UEndOffset, uint32_t D) { + Offset = *OffsetPtr; + Depth = D; + if (Offset >= UEndOffset || !DebugInfoData.isValidOffset(Offset)) + return false; + uint64_t AbbrCode = DebugInfoData.getULEB128(OffsetPtr); + if (0 == AbbrCode) { + // NULL debug tag entry. + AbbrevDecl = nullptr; + return true; + } + AbbrevDecl = U.getAbbreviations()->getAbbreviationDeclaration(AbbrCode); + if (nullptr == AbbrevDecl) { + // Restore the original offset. + *OffsetPtr = Offset; + return false; + } + // See if all attributes in this DIE have fixed byte sizes. If so, we can + // just add this size to the offset to skip to the next DIE. + if (Optional<size_t> FixedSize = AbbrevDecl->getFixedAttributesByteSize(U)) { + *OffsetPtr += *FixedSize; + return true; + } + + // Skip all data in the .debug_info for the attributes + for (const auto &AttrSpec : AbbrevDecl->attributes()) { + // Check if this attribute has a fixed byte size. + if (auto FixedSize = AttrSpec.getByteSize(U)) { + // Attribute byte size if fixed, just add the size to the offset. + *OffsetPtr += *FixedSize; + } else if (!DWARFFormValue::skipValue(AttrSpec.Form, DebugInfoData, + OffsetPtr, &U)) { + // We failed to skip this attribute's value, restore the original offset + // and return the failure status. + *OffsetPtr = Offset; + return false; + } + } + return true; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp new file mode 100644 index 000000000000..f32e8fe76357 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,813 @@ +//===- DWARFDebugLine.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.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; + +typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind; +namespace { +struct ContentDescriptor { + dwarf::LineNumberEntryFormat Type; + dwarf::Form Form; +}; +typedef SmallVector<ContentDescriptor, 4> ContentDescriptors; +} // end anonmyous namespace + +DWARFDebugLine::Prologue::Prologue() { clear(); } + +void DWARFDebugLine::Prologue::clear() { + TotalLength = Version = PrologueLength = 0; + AddressSize = SegSelectorSize = 0; + MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0; + OpcodeBase = 0; + IsDWARF64 = false; + StandardOpcodeLengths.clear(); + IncludeDirectories.clear(); + FileNames.clear(); +} + +void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { + OS << "Line table prologue:\n" + << format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength) + << format(" version: %u\n", Version) + << format(Version >= 5 ? " address_size: %u\n" : "", AddressSize) + << format(Version >= 5 ? " seg_select_size: %u\n" : "", SegSelectorSize) + << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength) + << format(" min_inst_length: %u\n", MinInstLength) + << format(Version >= 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()) + for (uint32_t I = 0; I != IncludeDirectories.size(); ++I) + OS << format("include_directories[%3u] = '", I + 1) + << IncludeDirectories[I] << "'\n"; + + if (!FileNames.empty()) { + OS << " Dir Mod Time File Len File Name\n" + << " ---- ---------- ---------- -----------" + "----------------\n"; + for (uint32_t I = 0; I != FileNames.size(); ++I) { + const FileNameEntry &FileEntry = FileNames[I]; + OS << format("file_names[%3u] %4" PRIu64 " ", I + 1, FileEntry.DirIdx) + << format("0x%8.8" PRIx64 " 0x%8.8" PRIx64 " ", FileEntry.ModTime, + FileEntry.Length) + << FileEntry.Name << '\n'; + } + } +} + +// Parse v2-v4 directory and file tables. +static void +parseV2DirFileTables(DataExtractor DebugLineData, uint32_t *OffsetPtr, + uint64_t EndPrologueOffset, + std::vector<StringRef> &IncludeDirectories, + std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { + while (*OffsetPtr < EndPrologueOffset) { + StringRef S = DebugLineData.getCStrRef(OffsetPtr); + if (S.empty()) + break; + IncludeDirectories.push_back(S); + } + + while (*OffsetPtr < EndPrologueOffset) { + StringRef Name = DebugLineData.getCStrRef(OffsetPtr); + if (Name.empty()) + break; + DWARFDebugLine::FileNameEntry FileEntry; + FileEntry.Name = Name; + FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); + FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); + FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileNames.push_back(FileEntry); + } +} + +// Parse v5 directory/file entry content descriptions. +// Returns the descriptors, or an empty vector if we did not find a path or +// ran off the end of the prologue. +static ContentDescriptors +parseV5EntryFormat(DataExtractor DebugLineData, uint32_t *OffsetPtr, + uint64_t EndPrologueOffset) { + ContentDescriptors Descriptors; + int FormatCount = DebugLineData.getU8(OffsetPtr); + bool HasPath = false; + for (int I = 0; I != FormatCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return ContentDescriptors(); + ContentDescriptor Descriptor; + Descriptor.Type = + dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); + Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); + if (Descriptor.Type == dwarf::DW_LNCT_path) + HasPath = true; + Descriptors.push_back(Descriptor); + } + return HasPath ? Descriptors : ContentDescriptors(); +} + +static bool +parseV5DirFileTables(DataExtractor DebugLineData, uint32_t *OffsetPtr, + uint64_t EndPrologueOffset, + std::vector<StringRef> &IncludeDirectories, + std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { + // Get the directory entry description. + ContentDescriptors DirDescriptors = + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset); + if (DirDescriptors.empty()) + return false; + + // Get the directory entries, according to the format described above. + int DirEntryCount = DebugLineData.getU8(OffsetPtr); + for (int I = 0; I != DirEntryCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return false; + for (auto Descriptor : DirDescriptors) { + DWARFFormValue Value(Descriptor.Form); + switch (Descriptor.Type) { + case DW_LNCT_path: + if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr)) + return false; + IncludeDirectories.push_back(Value.getAsCString().getValue()); + break; + default: + if (!Value.skipValue(DebugLineData, OffsetPtr, nullptr)) + return false; + } + } + } + + // Get the file entry description. + ContentDescriptors FileDescriptors = + parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset); + if (FileDescriptors.empty()) + return false; + + // Get the file entries, according to the format described above. + int FileEntryCount = DebugLineData.getU8(OffsetPtr); + for (int I = 0; I != FileEntryCount; ++I) { + if (*OffsetPtr >= EndPrologueOffset) + return false; + DWARFDebugLine::FileNameEntry FileEntry; + for (auto Descriptor : FileDescriptors) { + DWARFFormValue Value(Descriptor.Form); + if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr)) + return false; + switch (Descriptor.Type) { + case DW_LNCT_path: + FileEntry.Name = Value.getAsCString().getValue(); + 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; + // FIXME: Add MD5 + default: + break; + } + } + FileNames.push_back(FileEntry); + } + return true; +} + +bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData, + uint32_t *OffsetPtr) { + const uint64_t PrologueOffset = *OffsetPtr; + + clear(); + TotalLength = DebugLineData.getU32(OffsetPtr); + if (TotalLength == UINT32_MAX) { + IsDWARF64 = true; + TotalLength = DebugLineData.getU64(OffsetPtr); + } else if (TotalLength > 0xffffff00) { + return false; + } + Version = DebugLineData.getU16(OffsetPtr); + if (Version < 2) + return false; + + if (Version >= 5) { + AddressSize = DebugLineData.getU8(OffsetPtr); + SegSelectorSize = DebugLineData.getU8(OffsetPtr); + } + + PrologueLength = DebugLineData.getUnsigned(OffsetPtr, sizeofPrologueLength()); + const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; + MinInstLength = DebugLineData.getU8(OffsetPtr); + if (Version >= 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 (Version >= 5) { + if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, + IncludeDirectories, FileNames)) { + fprintf(stderr, + "warning: parsing line table prologue at 0x%8.8" PRIx64 + " found an invalid directory or file table description at" + " 0x%8.8" PRIx64 "\n", PrologueOffset, (uint64_t)*OffsetPtr); + return false; + } + } else + parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, + IncludeDirectories, FileNames); + + if (*OffsetPtr != EndPrologueOffset) { + fprintf(stderr, + "warning: parsing line table prologue at 0x%8.8" PRIx64 + " should have ended at 0x%8.8" PRIx64 + " but it ended at 0x%8.8" PRIx64 "\n", + PrologueOffset, EndPrologueOffset, (uint64_t)*OffsetPtr); + return false; + } + return true; +} + +DWARFDebugLine::Row::Row(bool DefaultIsStmt) { reset(DefaultIsStmt); } + +void DWARFDebugLine::Row::postAppend() { + BasicBlock = false; + PrologueEnd = false; + EpilogueBegin = false; +} + +void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { + Address = 0; + Line = 1; + Column = 0; + File = 1; + Isa = 0; + Discriminator = 0; + IsStmt = DefaultIsStmt; + BasicBlock = false; + EndSequence = false; + PrologueEnd = false; + EpilogueBegin = false; +} + +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { + OS << "Address Line Column File ISA Discriminator Flags\n" + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; +} + +void DWARFDebugLine::Row::dump(raw_ostream &OS) const { + OS << format("0x%16.16" PRIx64 " %6u %6u", Address, Line, Column) + << format(" %6u %3u %13u ", File, Isa, Discriminator) + << (IsStmt ? " is_stmt" : "") << (BasicBlock ? " basic_block" : "") + << (PrologueEnd ? " prologue_end" : "") + << (EpilogueBegin ? " epilogue_begin" : "") + << (EndSequence ? " end_sequence" : "") << '\n'; +} + +DWARFDebugLine::Sequence::Sequence() { reset(); } + +void DWARFDebugLine::Sequence::reset() { + LowPC = 0; + HighPC = 0; + FirstRowIndex = 0; + LastRowIndex = 0; + Empty = true; +} + +DWARFDebugLine::LineTable::LineTable() { clear(); } + +void DWARFDebugLine::LineTable::dump(raw_ostream &OS) const { + Prologue.dump(OS); + 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), RowNumber(0) { + resetRowAndSequence(); +} + +void DWARFDebugLine::ParsingState::resetRowAndSequence() { + Row.reset(LineTable->Prologue.DefaultIsStmt); + Sequence.reset(); +} + +void DWARFDebugLine::ParsingState::appendRowToMatrix(uint32_t Offset) { + if (Sequence.Empty) { + // Record the beginning of instruction sequence. + Sequence.Empty = false; + Sequence.LowPC = Row.Address; + Sequence.FirstRowIndex = RowNumber; + } + ++RowNumber; + LineTable->appendRow(Row); + if (Row.EndSequence) { + // Record the end of instruction sequence. + Sequence.HighPC = Row.Address; + Sequence.LastRowIndex = RowNumber; + if (Sequence.isValid()) + LineTable->appendSequence(Sequence); + Sequence.reset(); + } + Row.postAppend(); +} + +const DWARFDebugLine::LineTable * +DWARFDebugLine::getLineTable(uint32_t Offset) const { + LineTableConstIter Pos = LineTableMap.find(Offset); + if (Pos != LineTableMap.end()) + return &Pos->second; + return nullptr; +} + +const DWARFDebugLine::LineTable * +DWARFDebugLine::getOrParseLineTable(DataExtractor DebugLineData, + uint32_t Offset) { + std::pair<LineTableIter, bool> Pos = + LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable())); + LineTable *LT = &Pos.first->second; + if (Pos.second) { + if (!LT->parse(DebugLineData, RelocMap, &Offset)) + return nullptr; + } + return LT; +} + +bool DWARFDebugLine::LineTable::parse(DataExtractor DebugLineData, + const RelocAddrMap *RMap, + uint32_t *OffsetPtr) { + const uint32_t DebugLineOffset = *OffsetPtr; + + clear(); + + if (!Prologue.parse(DebugLineData, OffsetPtr)) { + // Restore our offset and return false to indicate failure! + *OffsetPtr = DebugLineOffset; + return false; + } + + const uint32_t EndOffset = + DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength(); + + ParsingState State(this); + + while (*OffsetPtr < EndOffset) { + uint8_t Opcode = DebugLineData.getU8(OffsetPtr); + + if (Opcode == 0) { + // Extended Opcodes always start with a zero opcode followed by + // a uleb128 length so you can skip ones you don't know about + uint32_t ExtOffset = *OffsetPtr; + uint64_t Len = DebugLineData.getULEB128(OffsetPtr); + uint32_t ArgSize = Len - (*OffsetPtr - ExtOffset); + + uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); + switch (SubOpcode) { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the + // state-machine registers. Then reset the registers to the initial + // values specified above. Every statement program sequence must end + // with a DW_LNE_end_sequence instruction which creates a row whose + // address is that of the byte after the last target machine instruction + // of the sequence. + State.Row.EndSequence = true; + State.appendRowToMatrix(*OffsetPtr); + 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. + State.Row.Address = getRelocatedValue( + DebugLineData, DebugLineData.getAddressSize(), OffsetPtr, RMap); + 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; + FileEntry.Name = DebugLineData.getCStr(OffsetPtr); + FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); + FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); + FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + Prologue.FileNames.push_back(FileEntry); + } + break; + + case DW_LNE_set_discriminator: + State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); + break; + + default: + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + (*OffsetPtr) += ArgSize; + break; + } + } else if (Opcode < Prologue.OpcodeBase) { + switch (Opcode) { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the + // current values of the state-machine registers. Then set + // the basic_block register to false. + State.appendRowToMatrix(*OffsetPtr); + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the + // result to the address register of the state machine. + State.Row.Address += + DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; + 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); + 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); + 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); + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state + // machine to the logical negation of its current value. + State.Row.IsStmt = !State.Row.IsStmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the + // state machine to true + State.Row.BasicBlock = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state + // machine the address increment value corresponding to special + // opcode 255. The motivation for DW_LNS_const_add_pc is this: + // when the statement program needs to advance the address by a + // small amount, it can use a single special opcode, which occupies + // a single byte. When it needs to advance the address by up to + // twice the range of the last special opcode, it can use + // DW_LNS_const_add_pc followed by a special opcode, for a total + // of two bytes. Only if it needs to advance the address by more + // than twice that range will it need to use both DW_LNS_advance_pc + // and a special opcode, requiring three or more bytes. + { + uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; + uint64_t AddrOffset = + (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; + State.Row.Address += AddrOffset; + } + 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. + State.Row.Address += DebugLineData.getU16(OffsetPtr); + 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); + 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) + DebugLineData.getULEB128(OffsetPtr); + } + break; + } + } else { + // Special Opcodes + + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The "address + // advance" is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + + // (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * + // minimum_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; + uint64_t AddrOffset = + (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; + int32_t LineOffset = + Prologue.LineBase + (AdjustOpcode % Prologue.LineRange); + State.Row.Line += LineOffset; + State.Row.Address += AddrOffset; + State.appendRowToMatrix(*OffsetPtr); + // Reset discriminator to 0. + State.Row.Discriminator = 0; + } + } + + if (!State.Sequence.Empty) { + fprintf(stderr, "warning: last sequence in debug line table is not" + "terminated!\n"); + } + + // Sort all sequences so that address lookup will work faster. + if (!Sequences.empty()) { + std::sort(Sequences.begin(), Sequences.end(), Sequence::orderByLowPC); + // Note: actually, instruction address ranges of sequences should not + // overlap (in shared objects and executables). If they do, the address + // lookup would still work, though, but result would be ambiguous. + // 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 EndOffset; +} + +uint32_t +DWARFDebugLine::LineTable::findRowInSeq(const DWARFDebugLine::Sequence &Seq, + uint64_t Address) const { + if (!Seq.containsPC(Address)) + return UnknownRowIndex; + // Search for instruction address in the rows describing the sequence. + // Rows are stored in a vector, so we may use arithmetical operations with + // iterators. + DWARFDebugLine::Row Row; + Row.Address = Address; + RowIter FirstRow = Rows.begin() + Seq.FirstRowIndex; + RowIter LastRow = Rows.begin() + Seq.LastRowIndex; + LineTable::RowIter RowPos = std::lower_bound( + FirstRow, LastRow, Row, DWARFDebugLine::Row::orderByAddress); + if (RowPos == LastRow) { + return Seq.LastRowIndex - 1; + } + uint32_t Index = Seq.FirstRowIndex + (RowPos - FirstRow); + if (RowPos->Address > Address) { + if (RowPos == FirstRow) + return UnknownRowIndex; + else + Index--; + } + return Index; +} + +uint32_t DWARFDebugLine::LineTable::lookupAddress(uint64_t Address) const { + if (Sequences.empty()) + return UnknownRowIndex; + // First, find an instruction sequence containing the given address. + DWARFDebugLine::Sequence Sequence; + Sequence.LowPC = Address; + SequenceIter FirstSeq = Sequences.begin(); + SequenceIter LastSeq = Sequences.end(); + SequenceIter SeqPos = std::lower_bound( + FirstSeq, LastSeq, Sequence, DWARFDebugLine::Sequence::orderByLowPC); + DWARFDebugLine::Sequence FoundSeq; + if (SeqPos == LastSeq) { + FoundSeq = Sequences.back(); + } else if (SeqPos->LowPC == Address) { + FoundSeq = *SeqPos; + } else { + if (SeqPos == FirstSeq) + return UnknownRowIndex; + FoundSeq = *(SeqPos - 1); + } + return findRowInSeq(FoundSeq, Address); +} + +bool DWARFDebugLine::LineTable::lookupAddressRange( + uint64_t Address, uint64_t Size, std::vector<uint32_t> &Result) const { + if (Sequences.empty()) + return false; + uint64_t EndAddr = Address + Size; + // First, find an instruction sequence containing the given address. + DWARFDebugLine::Sequence Sequence; + Sequence.LowPC = Address; + SequenceIter FirstSeq = Sequences.begin(); + SequenceIter LastSeq = Sequences.end(); + SequenceIter SeqPos = std::lower_bound( + FirstSeq, LastSeq, Sequence, DWARFDebugLine::Sequence::orderByLowPC); + if (SeqPos == LastSeq || SeqPos->LowPC != Address) { + if (SeqPos == FirstSeq) + return false; + SeqPos--; + } + if (!SeqPos->containsPC(Address)) + return false; + + SequenceIter StartPos = SeqPos; + + // Add the rows from the first sequence to the vector, starting with the + // index we just calculated + + while (SeqPos != LastSeq && SeqPos->LowPC < EndAddr) { + const DWARFDebugLine::Sequence &CurSeq = *SeqPos; + // For the first sequence, we need to find which row in the sequence is the + // first in our range. + uint32_t FirstRowIndex = CurSeq.FirstRowIndex; + if (SeqPos == StartPos) + FirstRowIndex = findRowInSeq(CurSeq, Address); + + // Figure out the last row in the range. + uint32_t LastRowIndex = findRowInSeq(CurSeq, EndAddr - 1); + if (LastRowIndex == UnknownRowIndex) + LastRowIndex = CurSeq.LastRowIndex - 1; + + assert(FirstRowIndex != UnknownRowIndex); + assert(LastRowIndex != UnknownRowIndex); + + for (uint32_t I = FirstRowIndex; I <= LastRowIndex; ++I) { + Result.push_back(I); + } + + ++SeqPos; + } + + return true; +} + +bool DWARFDebugLine::LineTable::hasFileAtIndex(uint64_t FileIndex) const { + return FileIndex != 0 && FileIndex <= Prologue.FileNames.size(); +} + +bool DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, + const char *CompDir, + FileLineInfoKind Kind, + std::string &Result) const { + if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) + return false; + const FileNameEntry &Entry = Prologue.FileNames[FileIndex - 1]; + StringRef FileName = Entry.Name; + if (Kind != FileLineInfoKind::AbsoluteFilePath || + sys::path::is_absolute(FileName)) { + Result = FileName; + return true; + } + + SmallString<16> FilePath; + uint64_t IncludeDirIndex = Entry.DirIdx; + StringRef IncludeDir; + // Be defensive about the contents of Entry. + if (IncludeDirIndex > 0 && + IncludeDirIndex <= Prologue.IncludeDirectories.size()) + IncludeDir = Prologue.IncludeDirectories[IncludeDirIndex - 1]; + + // We may still need to append compilation directory of compile unit. + // We know that FileName is not absolute, the only way to have an + // absolute path at this point would be if IncludeDir is absolute. + if (CompDir && Kind == FileLineInfoKind::AbsoluteFilePath && + sys::path::is_relative(IncludeDir)) + sys::path::append(FilePath, CompDir); + + // sys::path::append skips empty strings. + sys::path::append(FilePath, IncludeDir, FileName); + Result = FilePath.str(); + return true; +} + +bool DWARFDebugLine::LineTable::getFileLineInfoForAddress( + uint64_t Address, const char *CompDir, FileLineInfoKind Kind, + DILineInfo &Result) const { + // Get the index of row we're looking for in the line table. + uint32_t RowIndex = lookupAddress(Address); + if (RowIndex == -1U) + return false; + // Take file number and line/column from the row. + const auto &Row = Rows[RowIndex]; + if (!getFileNameByIndex(Row.File, CompDir, Kind, Result.FileName)) + return false; + Result.Line = Row.Line; + Result.Column = Row.Column; + Result.Discriminator = Row.Discriminator; + return true; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp new file mode 100644 index 000000000000..d5c34216ed53 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -0,0 +1,123 @@ +//===- DWARFDebugLoc.cpp --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +void DWARFDebugLoc::dump(raw_ostream &OS) const { + for (const LocationList &L : Locations) { + OS << format("0x%8.8x: ", L.Offset); + const unsigned Indent = 12; + for (const Entry &E : L.Entries) { + if (&E != L.Entries.begin()) + OS.indent(Indent); + OS << "Beginning address offset: " << format("0x%016" PRIx64, E.Begin) + << '\n'; + OS.indent(Indent) << " Ending address offset: " + << format("0x%016" PRIx64, E.End) << '\n'; + OS.indent(Indent) << " Location description: "; + for (unsigned char Loc : E.Loc) { + OS << format("%2.2x ", Loc); + } + OS << "\n\n"; + } + } +} + +void DWARFDebugLoc::parse(DataExtractor data, unsigned AddressSize) { + uint32_t Offset = 0; + while (data.isValidOffset(Offset+AddressSize-1)) { + Locations.resize(Locations.size() + 1); + LocationList &Loc = Locations.back(); + Loc.Offset = Offset; + // 2.6.2 Location Lists + // A location list entry consists of: + while (true) { + // A beginning and ending address offsets. + Entry E; + E.Begin = getRelocatedValue(data, AddressSize, &Offset, &RelocMap); + E.End = getRelocatedValue(data, AddressSize, &Offset, &RelocMap); + + // The end of any given location list is marked by an end of list entry, + // which consists of a 0 for the beginning address offset and a 0 for the + // ending address offset. + if (E.Begin == 0 && E.End == 0) + break; + + unsigned Bytes = data.getU16(&Offset); + // A single location description describing the location of the object... + StringRef str = data.getData().substr(Offset, Bytes); + Offset += Bytes; + E.Loc.append(str.begin(), str.end()); + Loc.Entries.push_back(std::move(E)); + } + } + if (data.isValidOffset(Offset)) + errs() << "error: failed to consume entire .debug_loc section\n"; +} + +void DWARFDebugLocDWO::parse(DataExtractor data) { + uint32_t Offset = 0; + while (data.isValidOffset(Offset)) { + Locations.resize(Locations.size() + 1); + LocationList &Loc = Locations.back(); + Loc.Offset = Offset; + dwarf::LocationListEntry Kind; + while ((Kind = static_cast<dwarf::LocationListEntry>( + data.getU8(&Offset))) != dwarf::DW_LLE_end_of_list) { + + if (Kind != dwarf::DW_LLE_startx_length) { + errs() << "error: dumping support for LLE of kind " << (int)Kind + << " not implemented\n"; + return; + } + + Entry E; + + E.Start = data.getULEB128(&Offset); + E.Length = data.getU32(&Offset); + + unsigned Bytes = data.getU16(&Offset); + // A single location description describing the location of the object... + StringRef str = data.getData().substr(Offset, Bytes); + Offset += Bytes; + E.Loc.resize(str.size()); + std::copy(str.begin(), str.end(), E.Loc.begin()); + + Loc.Entries.push_back(std::move(E)); + } + } +} + +void DWARFDebugLocDWO::dump(raw_ostream &OS) const { + for (const LocationList &L : Locations) { + OS << format("0x%8.8x: ", L.Offset); + const unsigned Indent = 12; + for (const Entry &E : L.Entries) { + if (&E != L.Entries.begin()) + OS.indent(Indent); + OS << "Beginning address index: " << E.Start << '\n'; + OS.indent(Indent) << " Length: " << E.Length << '\n'; + OS.indent(Indent) << " Location description: "; + for (unsigned char Loc : E.Loc) + OS << format("%2.2x ", Loc); + OS << "\n\n"; + } + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp new file mode 100644 index 000000000000..e0a9adde8e58 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -0,0 +1,102 @@ +//===- DWARFDebugMacro.cpp ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SyntaxHighlighting.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> + +using namespace llvm; +using namespace dwarf; +using namespace syntax; + +void DWARFDebugMacro::dump(raw_ostream &OS) const { + unsigned IndLevel = 0; + 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, syntax::Macro).get() << MacinfoString(E.Type); + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + OS << " - lineno: " << E.Line; + OS << " macro: " << E.MacroStr; + break; + case DW_MACINFO_start_file: + OS << " - lineno: " << E.Line; + OS << " filenum: " << E.File; + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + OS << " - constant: " << E.ExtConstant; + OS << " string: " << E.ExtStr; + break; + } + OS << "\n"; + } +} + +void DWARFDebugMacro::parse(DataExtractor data) { + uint32_t Offset = 0; + while (data.isValidOffset(Offset)) { + // A macro list entry consists of: + Entry E; + // 1. Macinfo type + E.Type = data.getULEB128(&Offset); + + if (E.Type == 0) { + // Reached end of ".debug_macinfo" section. + return; + } + + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + // Push the corrupted entry to the list and halt parsing. + E.Type = DW_MACINFO_invalid; + Macros.push_back(E); + return; + case DW_MACINFO_define: + case DW_MACINFO_undef: + // 2. Source line + E.Line = data.getULEB128(&Offset); + // 3. Macro string + E.MacroStr = data.getCStr(&Offset); + break; + case DW_MACINFO_start_file: + // 2. Source line + E.Line = data.getULEB128(&Offset); + // 3. Source file id + E.File = data.getULEB128(&Offset); + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + // 2. Vendor extension constant + E.ExtConstant = data.getULEB128(&Offset); + // 3. Vendor extension string + E.ExtStr = data.getCStr(&Offset); + break; + } + + Macros.push_back(E); + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp new file mode 100644 index 000000000000..daded255f8c7 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -0,0 +1,69 @@ +//===- DWARFDebugPubTable.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> + +using namespace llvm; +using namespace dwarf; + +DWARFDebugPubTable::DWARFDebugPubTable(StringRef Data, bool LittleEndian, + bool GnuStyle) + : GnuStyle(GnuStyle) { + DataExtractor PubNames(Data, LittleEndian, 0); + uint32_t Offset = 0; + while (PubNames.isValidOffset(Offset)) { + Sets.push_back({}); + Set &SetData = Sets.back(); + + SetData.Length = PubNames.getU32(&Offset); + SetData.Version = PubNames.getU16(&Offset); + SetData.Offset = PubNames.getU32(&Offset); + SetData.Size = PubNames.getU32(&Offset); + + while (Offset < Data.size()) { + uint32_t DieRef = PubNames.getU32(&Offset); + if (DieRef == 0) + break; + uint8_t IndexEntryValue = GnuStyle ? PubNames.getU8(&Offset) : 0; + const char *Name = PubNames.getCStr(&Offset); + SetData.Entries.push_back( + {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + } + } +} + +void DWARFDebugPubTable::dump(StringRef Name, raw_ostream &OS) const { + OS << "\n." << Name << " contents:\n"; + for (const Set &S : Sets) { + OS << "length = " << format("0x%08x", S.Length); + OS << " version = " << format("0x%04x", S.Version); + OS << " unit_offset = " << format("0x%08x", S.Offset); + OS << " unit_size = " << format("0x%08x", S.Size) << '\n'; + OS << (GnuStyle ? "Offset Linkage Kind Name\n" + : "Offset Name\n"); + + for (const Entry &E : S.Entries) { + OS << format("0x%8.8x ", E.SecOffset); + if (GnuStyle) { + StringRef EntryLinkage = + GDBIndexEntryLinkageString(E.Descriptor.Linkage); + StringRef EntryKind = dwarf::GDBIndexEntryKindString(E.Descriptor.Kind); + OS << format("%-8s", EntryLinkage.data()) << ' ' + << format("%-8s", EntryKind.data()) << ' '; + } + OS << '\"' << E.Name << "\"\n"; + } + } +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp new file mode 100644 index 000000000000..6b5e1d3c931b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugRangeList.cpp @@ -0,0 +1,77 @@ +//===- DWARFDebugRangesList.cpp -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> +#include <utility> + +using namespace llvm; + +void DWARFDebugRangeList::clear() { + Offset = -1U; + AddressSize = 0; + Entries.clear(); +} + +bool DWARFDebugRangeList::extract(DataExtractor data, uint32_t *offset_ptr, + const RelocAddrMap &Relocs) { + clear(); + if (!data.isValidOffset(*offset_ptr)) + return false; + AddressSize = data.getAddressSize(); + if (AddressSize != 4 && AddressSize != 8) + return false; + Offset = *offset_ptr; + while (true) { + RangeListEntry entry; + uint32_t prev_offset = *offset_ptr; + entry.StartAddress = getRelocatedValue(data, AddressSize, offset_ptr, + &Relocs, &entry.SectionIndex); + entry.EndAddress = + getRelocatedValue(data, AddressSize, offset_ptr, &Relocs); + + // Check that both values were extracted correctly. + if (*offset_ptr != prev_offset + 2 * AddressSize) { + clear(); + return false; + } + if (entry.isEndOfListEntry()) + break; + Entries.push_back(entry); + } + return true; +} + +void DWARFDebugRangeList::dump(raw_ostream &OS) const { + for (const RangeListEntry &RLE : Entries) { + const char *format_str = (AddressSize == 4 + ? "%08x %08" PRIx64 " %08" PRIx64 "\n" + : "%08x %016" PRIx64 " %016" PRIx64 "\n"); + OS << format(format_str, Offset, RLE.StartAddress, RLE.EndAddress); + } + OS << format("%08x <End of list>\n", Offset); +} + +DWARFAddressRangesVector +DWARFDebugRangeList::getAbsoluteRanges(uint64_t BaseAddress) const { + DWARFAddressRangesVector Res; + for (const RangeListEntry &RLE : Entries) { + if (RLE.isBaseAddressSelectionEntry(AddressSize)) { + BaseAddress = RLE.EndAddress; + } else { + Res.push_back({BaseAddress + RLE.StartAddress, + BaseAddress + RLE.EndAddress, RLE.SectionIndex}); + } + } + return Res; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp new file mode 100644 index 000000000000..fd45c77d3745 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -0,0 +1,418 @@ +//===- DWARFDie.cpp -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SyntaxHighlighting.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.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 syntax; + +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(raw_ostream &OS, const DWARFAddressRangesVector& Ranges, + unsigned AddressSize, unsigned Indent) { + if (Ranges.empty()) + return; + + for (const auto &Range: Ranges) { + OS << '\n'; + OS.indent(Indent); + OS << format("[0x%0*" PRIx64 " - 0x%0*" PRIx64 ")", + AddressSize*2, Range.LowPC, + AddressSize*2, Range.HighPC); + } +} + +static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, + uint32_t *OffsetPtr, dwarf::Attribute Attr, + dwarf::Form Form, unsigned Indent) { + if (!Die.isValid()) + return; + const char BaseIndent[] = " "; + OS << BaseIndent; + OS.indent(Indent+2); + auto attrString = AttributeString(Attr); + if (!attrString.empty()) + WithColor(OS, syntax::Attribute) << attrString; + else + WithColor(OS, syntax::Attribute).get() << format("DW_AT_Unknown_%x", Attr); + + auto formString = FormEncodingString(Form); + if (!formString.empty()) + OS << " [" << formString << ']'; + else + OS << format(" [DW_FORM_Unknown_%x]", Form); + + DWARFUnit *U = Die.getDwarfUnit(); + DWARFFormValue formValue(Form); + + if (!formValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, U)) + return; + + OS << "\t("; + + StringRef Name; + std::string File; + auto Color = syntax::Enumerator; + if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { + Color = syntax::String; + if (const auto *LT = U->getContext().getLineTableForUnit(U)) + if (LT->getFileNameByIndex(formValue.getAsUnsignedConstant().getValue(), U->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { + 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 + formValue.dump(OS); + + // We have dumped the attribute raw value. For some attributes + // having both the raw value and the pretty-printed value is + // interesting. These attributes are handled below. + if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin) { + if (const char *Name = Die.getAttributeValueAsReferencedDie(Attr).getName(DINameKind::LinkageName)) + OS << " \"" << Name << '\"'; + } else if (Attr == DW_AT_APPLE_property_attribute) { + if (Optional<uint64_t> OptVal = formValue.getAsUnsignedConstant()) + dumpApplePropertyAttribute(OS, *OptVal); + } else if (Attr == DW_AT_ranges) { + dumpRanges(OS, Die.getAddressRanges(), U->getAddressByteSize(), + sizeof(BaseIndent)+Indent+4); + } + + 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 { + if (!isValid()) + return None; + auto Die = *this; + if (auto Value = Die.find(Attrs)) + return Value; + if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) + Die = D; + if (auto Value = Die.find(Attrs)) + return Value; + if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_specification)) + Die = D; + if (auto Value = Die.find(Attrs)) + return Value; + return None; +} + +DWARFDie +DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const { + auto SpecRef = toReference(find(Attr)); + if (SpecRef) { + auto SpecUnit = U->getUnitSection().getUnitForOffset(*SpecRef); + if (SpecUnit) + return SpecUnit->getDIEForOffset(*SpecRef); + } + return DWARFDie(); +} + +Optional<uint64_t> +DWARFDie::getRangesBaseAttribute() const { + return toSectionOffset(find({DW_AT_rnglists_base, DW_AT_GNU_ranges_base})); +} + +Optional<uint64_t> DWARFDie::getHighPC(uint64_t LowPC) const { + if (auto FormValue = find(DW_AT_high_pc)) { + if (auto Address = FormValue->getAsAddress()) { + // High PC is an address. + return Address; + } + if (auto Offset = FormValue->getAsUnsignedConstant()) { + // High PC is an offset from LowPC. + return LowPC + *Offset; + } + } + return None; +} + +bool DWARFDie::getLowAndHighPC(uint64_t &LowPC, uint64_t &HighPC, + uint64_t &SectionIndex) const { + auto F = find(DW_AT_low_pc); + auto LowPcAddr = toAddress(F); + if (!LowPcAddr) + return false; + if (auto HighPcAddr = getHighPC(*LowPcAddr)) { + LowPC = *LowPcAddr; + HighPC = *HighPcAddr; + SectionIndex = F->getSectionIndex(); + return true; + } + return false; +} + +DWARFAddressRangesVector +DWARFDie::getAddressRanges() const { + if (isNULL()) + return DWARFAddressRangesVector(); + // Single range specified by low/high PC. + uint64_t LowPC, HighPC, Index; + if (getLowAndHighPC(LowPC, HighPC, Index)) + return {{LowPC, HighPC, Index}}; + + // Multiple ranges from .debug_ranges section. + auto RangesOffset = toSectionOffset(find(DW_AT_ranges)); + if (RangesOffset) { + DWARFDebugRangeList RangeList; + if (U->extractRangeList(*RangesOffset, RangeList)) + return RangeList.getAbsoluteRanges(U->getBaseAddress()); + } + return DWARFAddressRangesVector(); +} + +void +DWARFDie::collectChildrenAddressRanges(DWARFAddressRangesVector& Ranges) const { + if (isNULL()) + return; + if (isSubprogramDIE()) { + const auto &DIERanges = getAddressRanges(); + Ranges.insert(Ranges.end(), DIERanges.begin(), DIERanges.end()); + } + + for (auto Child: children()) + Child.collectChildrenAddressRanges(Ranges); +} + +bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { + for (const auto& R : getAddressRanges()) { + 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); +} + +void DWARFDie::dump(raw_ostream &OS, unsigned RecurseDepth, + unsigned Indent) const { + if (!isValid()) + return; + DataExtractor debug_info_data = U->getDebugInfoExtractor(); + const uint32_t Offset = getOffset(); + uint32_t offset = Offset; + + if (debug_info_data.isValidOffset(offset)) { + uint32_t abbrCode = debug_info_data.getULEB128(&offset); + WithColor(OS, syntax::Address).get() << format("\n0x%8.8x: ", Offset); + + if (abbrCode) { + auto AbbrevDecl = getAbbreviationDeclarationPtr(); + if (AbbrevDecl) { + auto tagString = TagString(getTag()); + if (!tagString.empty()) + WithColor(OS, syntax::Tag).get().indent(Indent) << tagString; + else + WithColor(OS, syntax::Tag).get().indent(Indent) + << format("DW_TAG_Unknown_%x", getTag()); + + OS << format(" [%u] %c\n", abbrCode, + AbbrevDecl->hasChildren() ? '*' : ' '); + + // 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); + } + + DWARFDie child = getFirstChild(); + if (RecurseDepth > 0 && child) { + while (child) { + child.dump(OS, RecurseDepth-1, Indent+2); + child = child.getSibling(); + } + } + } else { + OS << "Abbreviation code not found in 'debug_abbrev' class for code: " + << abbrCode << '\n'; + } + } else { + OS.indent(Indent) << "NULL\n"; + } + } +} + +DWARFDie DWARFDie::getParent() const { + if (isValid()) + return U->getParent(Die); + return DWARFDie(); +} + +DWARFDie DWARFDie::getSibling() const { + if (isValid()) + return U->getSibling(Die); + return DWARFDie(); +} + +iterator_range<DWARFDie::attribute_iterator> +DWARFDie::attributes() const { + return make_range(attribute_iterator(*this, false), + attribute_iterator(*this, true)); +} + +DWARFDie::attribute_iterator::attribute_iterator(DWARFDie D, bool End) : + Die(D), AttrValue(0), Index(0) { + auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); + assert(AbbrDecl && "Must have abbreviation declaration"); + if (End) { + // This is the end iterator so we set the index to the attribute count. + Index = AbbrDecl->getNumAttributes(); + } else { + // This is the begin iterator so we extract the value for this->Index. + AttrValue.Offset = D.getOffset() + AbbrDecl->getCodeByteSize(); + updateForIndex(*AbbrDecl, 0); + } +} + +void DWARFDie::attribute_iterator::updateForIndex( + const DWARFAbbreviationDeclaration &AbbrDecl, uint32_t I) { + Index = I; + // AbbrDecl must be valid befor calling this function. + auto NumAttrs = AbbrDecl.getNumAttributes(); + if (Index < NumAttrs) { + AttrValue.Attr = AbbrDecl.getAttrByIndex(Index); + // Add the previous byte size of any previous attribute value. + AttrValue.Offset += AttrValue.ByteSize; + AttrValue.Value.setForm(AbbrDecl.getFormByIndex(Index)); + uint32_t ParseOffset = AttrValue.Offset; + auto U = Die.getDwarfUnit(); + assert(U && "Die must have valid DWARF unit"); + bool b = AttrValue.Value.extractValue(U->getDebugInfoExtractor(), + &ParseOffset, U); + (void)b; + assert(b && "extractValue cannot fail on fully parsed DWARF"); + AttrValue.ByteSize = ParseOffset - AttrValue.Offset; + } else { + assert(Index == NumAttrs && "Indexes should be [0, NumAttrs) only"); + AttrValue.clear(); + } +} + +DWARFDie::attribute_iterator &DWARFDie::attribute_iterator::operator++() { + if (auto AbbrDecl = Die.getAbbreviationDeclarationPtr()) + updateForIndex(*AbbrDecl, Index + 1); + return *this; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp new file mode 100644 index 000000000000..0963d7bfd713 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -0,0 +1,717 @@ +//===- DWARFFormValue.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "SyntaxHighlighting.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> +#include <limits> + +using namespace llvm; +using namespace dwarf; +using namespace syntax; + +static const DWARFFormValue::FormClass DWARF4FormClasses[] = { + 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 +}; + +namespace { + +/// A helper class that can be used in DWARFFormValue.cpp functions that need +/// to know the byte size of DW_FORM values that vary in size depending on the +/// DWARF version, address byte size, or DWARF32 or DWARF64. +class FormSizeHelper { + uint16_t Version; + uint8_t AddrSize; + llvm::dwarf::DwarfFormat Format; + +public: + FormSizeHelper(uint16_t V, uint8_t A, llvm::dwarf::DwarfFormat F) + : Version(V), AddrSize(A), Format(F) {} + + uint8_t getAddressByteSize() const { return AddrSize; } + + uint8_t getRefAddrByteSize() const { + if (Version == 2) + return AddrSize; + return getDwarfOffsetByteSize(); + } + + uint8_t getDwarfOffsetByteSize() const { + switch (Format) { + case dwarf::DwarfFormat::DWARF32: + return 4; + case dwarf::DwarfFormat::DWARF64: + return 8; + } + llvm_unreachable("Invalid Format value"); + } +}; + +} // end anonymous namespace + +template <class T> +static Optional<uint8_t> getFixedByteSize(dwarf::Form Form, const T *U) { + switch (Form) { + case DW_FORM_addr: + if (U) + return U->getAddressByteSize(); + return None; + + case DW_FORM_block: // ULEB128 length L followed by L bytes. + case DW_FORM_block1: // 1 byte length L followed by L bytes. + case DW_FORM_block2: // 2 byte length L followed by L bytes. + case DW_FORM_block4: // 4 byte length L followed by L bytes. + case DW_FORM_string: // C-string with null terminator. + case DW_FORM_sdata: // SLEB128. + case DW_FORM_udata: // ULEB128. + case DW_FORM_ref_udata: // ULEB128. + case DW_FORM_indirect: // ULEB128. + case DW_FORM_exprloc: // ULEB128 length L followed by L bytes. + case DW_FORM_strx: // ULEB128. + case DW_FORM_addrx: // ULEB128. + case DW_FORM_loclistx: // ULEB128. + case DW_FORM_rnglistx: // ULEB128. + case DW_FORM_GNU_addr_index: // ULEB128. + case DW_FORM_GNU_str_index: // ULEB128. + return None; + + case DW_FORM_ref_addr: + if (U) + return U->getRefAddrByteSize(); + return None; + + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_strx1: + case DW_FORM_addrx1: + return 1; + + case DW_FORM_data2: + case DW_FORM_ref2: + case DW_FORM_strx2: + case DW_FORM_addrx2: + return 2; + + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_ref_sup4: + case DW_FORM_strx4: + case DW_FORM_addrx4: + return 4; + + case DW_FORM_strp: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + case DW_FORM_line_strp: + case DW_FORM_sec_offset: + case DW_FORM_strp_sup: + if (U) + return U->getDwarfOffsetByteSize(); + return None; + + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + case DW_FORM_ref_sup8: + return 8; + + case DW_FORM_flag_present: + return 0; + + case DW_FORM_data16: + return 16; + + case DW_FORM_implicit_const: + // The implicit value is stored in the abbreviation as a SLEB128, and + // there no data in debug info. + return 0; + + default: + llvm_unreachable("Handle this form in this switch statement"); + } + return None; +} + +template <class T> +static bool skipFormValue(dwarf::Form Form, const DataExtractor &DebugInfoData, + uint32_t *OffsetPtr, const T *U) { + 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_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 = ::getFixedByteSize(Form, U)) { + *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; +} + +Optional<uint8_t> DWARFFormValue::getFixedByteSize(dwarf::Form Form, + const DWARFUnit *U) { + return ::getFixedByteSize(Form, U); +} + +Optional<uint8_t> +DWARFFormValue::getFixedByteSize(dwarf::Form Form, uint16_t Version, + uint8_t AddrSize, + llvm::dwarf::DwarfFormat Format) { + FormSizeHelper FSH(Version, AddrSize, Format); + return ::getFixedByteSize(Form, &FSH); +} + +bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { + // First, check DWARF4 form classes. + if (Form < makeArrayRef(DWARF4FormClasses).size() && + DWARF4FormClasses[Form] == FC) + return true; + // Check more forms from DWARF4 and DWARF5 proposals. + switch (Form) { + case DW_FORM_ref_sig8: + case DW_FORM_GNU_ref_alt: + return (FC == FC_Reference); + case DW_FORM_GNU_addr_index: + return (FC == FC_Address); + case DW_FORM_GNU_str_index: + case DW_FORM_GNU_strp_alt: + return (FC == FC_String); + case DW_FORM_implicit_const: + return (FC == FC_Constant); + default: + break; + } + // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset. + // Don't check for DWARF version here, as some producers may still do this + // by mistake. Also accept DW_FORM_strp since this is .debug_str section + // offset. + return (Form == DW_FORM_data4 || Form == DW_FORM_data8 || + Form == DW_FORM_strp) && + FC == FC_SectionOffset; +} + +bool DWARFFormValue::extractValue(const DataExtractor &Data, + uint32_t *OffsetPtr, const DWARFUnit *CU) { + 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: { + if (!U) + return false; + uint16_t AddrSize = (Form == DW_FORM_addr) ? U->getAddressByteSize() + : U->getRefAddrByteSize(); + Value.uval = getRelocatedValue(Data, AddrSize, OffsetPtr, + U->getRelocMap(), &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_data4: + case DW_FORM_ref4: + case DW_FORM_ref_sup4: + case DW_FORM_strx4: + case DW_FORM_addrx4: { + const RelocAddrMap *RelocMap = U ? U->getRelocMap() : nullptr; + Value.uval = getRelocatedValue(Data, 4, OffsetPtr, RelocMap); + break; + } + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sup8: + Value.uval = Data.getU64(OffsetPtr); + break; + case DW_FORM_sdata: + Value.sval = Data.getSLEB128(OffsetPtr); + break; + case DW_FORM_udata: + case DW_FORM_ref_udata: + 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: { + if (!U) + return false; + Value.uval = getRelocatedValue(Data, U->getDwarfOffsetByteSize(), + OffsetPtr, U->getRelocMap()); + 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: + Value.uval = Data.getULEB128(OffsetPtr); + break; + default: + // DWARFFormValue::skipValue() will have caught this and caused all + // DWARF DIEs to fail to be parsed, so this code is not be reachable. + llvm_unreachable("unsupported form"); + } + } while (Indirect); + + if (IsBlock) { + StringRef Str = Data.getData().substr(*OffsetPtr, Value.uval); + Value.data = nullptr; + if (!Str.empty()) { + Value.data = reinterpret_cast<const uint8_t *>(Str.data()); + *OffsetPtr += Value.uval; + } + } + + return true; +} + +bool DWARFFormValue::skipValue(DataExtractor DebugInfoData, uint32_t *OffsetPtr, + const DWARFUnit *U) const { + return DWARFFormValue::skipValue(Form, DebugInfoData, OffsetPtr, U); +} + +bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, + uint32_t *OffsetPtr, const DWARFUnit *U) { + return skipFormValue(Form, DebugInfoData, OffsetPtr, U); +} + +bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, + uint32_t *OffsetPtr, uint16_t Version, + uint8_t AddrSize, + llvm::dwarf::DwarfFormat Format) { + FormSizeHelper FSH(Version, AddrSize, Format); + return skipFormValue(Form, DebugInfoData, OffsetPtr, &FSH); +} + +void DWARFFormValue::dump(raw_ostream &OS) const { + uint64_t UValue = Value.uval; + bool CURelativeOffset = false; + + switch (Form) { + case DW_FORM_addr: + OS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_GNU_addr_index: { + OS << format(" indexed (%8.8x) address = ", (uint32_t)UValue); + uint64_t Address; + if (U == nullptr) + OS << "<invalid dwarf unit>"; + else if (U->getAddrOffsetSectionItem(UValue, Address)) + OS << format("0x%016" PRIx64, Address); + 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: + case DW_FORM_data8: + OS << format("0x%016" PRIx64, UValue); + 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: + OS << format("<0x%" PRIx64 "> ", UValue); + break; + case DW_FORM_block1: + OS << format("<0x%2.2x> ", (uint8_t)UValue); + break; + case DW_FORM_block2: + OS << format("<0x%4.4x> ", (uint16_t)UValue); + break; + case DW_FORM_block4: + OS << 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) { + OS << 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: + OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); + dumpString(OS); + break; + case DW_FORM_GNU_str_index: + OS << format(" indexed (%8.8x) string = ", (uint32_t)UValue); + dumpString(OS); + break; + case DW_FORM_GNU_strp_alt: + OS << format("alt indirect string, offset: 0x%" PRIx64 "", UValue); + dumpString(OS); + break; + case DW_FORM_ref_addr: + OS << format("0x%016" PRIx64, UValue); + break; + case DW_FORM_ref1: + CURelativeOffset = true; + OS << format("cu + 0x%2.2x", (uint8_t)UValue); + break; + case DW_FORM_ref2: + CURelativeOffset = true; + OS << format("cu + 0x%4.4x", (uint16_t)UValue); + break; + case DW_FORM_ref4: + CURelativeOffset = true; + OS << format("cu + 0x%4.4x", (uint32_t)UValue); + break; + case DW_FORM_ref8: + CURelativeOffset = true; + OS << format("cu + 0x%8.8" PRIx64, UValue); + break; + case DW_FORM_ref_udata: + CURelativeOffset = true; + OS << format("cu + 0x%" PRIx64, UValue); + break; + case DW_FORM_GNU_ref_alt: + OS << 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; + + // Should be formatted to 64-bit for DWARF64. + case DW_FORM_sec_offset: + OS << format("0x%08x", (uint32_t)UValue); + break; + + default: + OS << format("DW_FORM(0x%4.4x)", Form); + break; + } + + if (CURelativeOffset) { + OS << " => {"; + WithColor(OS, syntax::Address).get() + << format("0x%8.8" PRIx64, UValue + (U ? U->getOffset() : 0)); + OS << "}"; + } +} + +void DWARFFormValue::dumpString(raw_ostream &OS) const { + Optional<const char *> DbgStr = getAsCString(); + if (DbgStr.hasValue()) { + raw_ostream &COS = WithColor(OS, syntax::String); + COS << '"'; + COS.write_escaped(DbgStr.getValue()); + COS << '"'; + } +} + +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 || U == nullptr) + return None; + uint32_t Offset = Value.uval; + if (Form == DW_FORM_GNU_str_index) { + uint32_t StrOffset; + if (!U->getStringOffsetSectionItem(Offset, StrOffset)) + return None; + Offset = StrOffset; + } + if (const char *Str = U->getStringExtractor().getCStr(&Offset)) { + return Str; + } + return None; +} + +Optional<uint64_t> DWARFFormValue::getAsAddress() const { + if (!isFormClass(FC_Address)) + return None; + if (Form == DW_FORM_GNU_addr_index) { + uint32_t Index = Value.uval; + uint64_t Result; + if (!U || !U->getAddrOffsetSectionItem(Index, Result)) + return None; + return Result; + } + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsReference() const { + if (!isFormClass(FC_Reference)) + return None; + switch (Form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + if (!U) + return None; + return Value.uval + U->getOffset(); + case DW_FORM_ref_addr: + case DW_FORM_ref_sig8: + case DW_FORM_GNU_ref_alt: + return Value.uval; + default: + return None; + } +} + +Optional<uint64_t> DWARFFormValue::getAsSectionOffset() const { + if (!isFormClass(FC_SectionOffset)) + return None; + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsUnsignedConstant() const { + if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || + Form == DW_FORM_sdata) + return None; + return Value.uval; +} + +Optional<int64_t> DWARFFormValue::getAsSignedConstant() const { + if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || + (Form == DW_FORM_udata && + uint64_t(std::numeric_limits<int64_t>::max()) < Value.uval)) + return None; + switch (Form) { + case DW_FORM_data4: + return int32_t(Value.uval); + case DW_FORM_data2: + return int16_t(Value.uval); + case DW_FORM_data1: + return int8_t(Value.uval); + case DW_FORM_sdata: + case DW_FORM_data8: + default: + return Value.sval; + } +} + +Optional<ArrayRef<uint8_t>> DWARFFormValue::getAsBlock() const { + if (!isFormClass(FC_Block) && !isFormClass(FC_Exprloc)) + return None; + return makeArrayRef(Value.data, Value.uval); +} + +Optional<uint64_t> DWARFFormValue::getAsCStringOffset() const { + if (!isFormClass(FC_String) && Form == DW_FORM_string) + return None; + return Value.uval; +} + +Optional<uint64_t> DWARFFormValue::getAsReferenceUVal() const { + if (!isFormClass(FC_Reference)) + return None; + return Value.uval; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp new file mode 100644 index 000000000000..0625d01097c9 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFGdbIndex.cpp @@ -0,0 +1,183 @@ +//===- DWARFGdbIndex.cpp --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" +#include "llvm/Support/Format.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::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); + dumpAddressArea(OS); + dumpSymbolTable(OS); + dumpConstantPool(OS); + } +} + +bool DWARFGdbIndex::parseImpl(DataExtractor Data) { + uint32_t Offset = 0; + + // Only version 7 is supported at this moment. + Version = Data.getU32(&Offset); + if (Version != 7) + return false; + + CuListOffset = Data.getU32(&Offset); + uint32_t CuTypesOffset = Data.getU32(&Offset); + AddressAreaOffset = Data.getU32(&Offset); + SymbolTableOffset = Data.getU32(&Offset); + ConstantPoolOffset = Data.getU32(&Offset); + + if (Offset != CuListOffset) + return false; + + uint32_t CuListSize = (CuTypesOffset - CuListOffset) / 16; + CuList.reserve(CuListSize); + for (uint32_t i = 0; i < CuListSize; ++i) { + uint64_t CuOffset = Data.getU64(&Offset); + uint64_t CuLength = Data.getU64(&Offset); + CuList.push_back({CuOffset, CuLength}); + } + + // CU Types are no longer needed as DWARF skeleton type units never made it + // into the standard. + uint32_t CuTypesListSize = (AddressAreaOffset - CuTypesOffset) / 24; + if (CuTypesListSize != 0) + return false; + + uint32_t AddressAreaSize = (SymbolTableOffset - AddressAreaOffset) / 20; + AddressArea.reserve(AddressAreaSize); + for (uint32_t i = 0; i < AddressAreaSize; ++i) { + uint64_t LowAddress = Data.getU64(&Offset); + uint64_t HighAddress = Data.getU64(&Offset); + uint32_t CuIndex = Data.getU32(&Offset); + AddressArea.push_back({LowAddress, HighAddress, CuIndex}); + } + + // The symbol table. This is an open addressed hash table. The size of the + // hash table is always a power of 2. + // Each slot in the hash table consists of a pair of offset_type values. The + // first value is the offset of the symbol's name in the constant pool. The + // second value is the offset of the CU vector in the constant pool. + // If both values are 0, then this slot in the hash table is empty. This is ok + // because while 0 is a valid constant pool index, it cannot be a valid index + // for both a string and a CU vector. + uint32_t SymTableSize = (ConstantPoolOffset - SymbolTableOffset) / 8; + SymbolTable.reserve(SymTableSize); + uint32_t CuVectorsTotal = 0; + for (uint32_t i = 0; i < SymTableSize; ++i) { + uint32_t NameOffset = Data.getU32(&Offset); + uint32_t CuVecOffset = Data.getU32(&Offset); + SymbolTable.push_back({NameOffset, CuVecOffset}); + if (NameOffset || CuVecOffset) + ++CuVectorsTotal; + } + + // The constant pool. CU vectors are stored first, followed by strings. + // The first value is the number of CU indices in the vector. Each subsequent + // value is the index and symbol attributes of a CU in the CU list. + for (uint32_t i = 0; i < CuVectorsTotal; ++i) { + ConstantPoolVectors.emplace_back(0, SmallVector<uint32_t, 0>()); + auto &Vec = ConstantPoolVectors.back(); + Vec.first = Offset - ConstantPoolOffset; + + uint32_t Num = Data.getU32(&Offset); + for (uint32_t j = 0; j < Num; ++j) + Vec.second.push_back(Data.getU32(&Offset)); + } + + ConstantPoolStrings = Data.getData().drop_front(Offset); + StringPoolOffset = Offset; + return true; +} + +void DWARFGdbIndex::parse(DataExtractor Data) { + HasContent = !Data.getData().empty(); + HasError = HasContent && !parseImpl(Data); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp new file mode 100644 index 000000000000..25824f6eb83b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp @@ -0,0 +1,61 @@ +//===- DWARFTypeUnit.cpp --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> + +using namespace llvm; + +bool DWARFTypeUnit::extractImpl(DataExtractor debug_info, + uint32_t *offset_ptr) { + if (!DWARFUnit::extractImpl(debug_info, offset_ptr)) + return false; + TypeHash = debug_info.getU64(offset_ptr); + TypeOffset = debug_info.getU32(offset_ptr); + // TypeOffset is relative to the beginning of the header, + // so we have to account for the leading length field. + // FIXME: The size of the length field is 12 in DWARF64. + unsigned SizeOfLength = 4; + return TypeOffset < getLength() + SizeOfLength; +} + +void DWARFTypeUnit::dump(raw_ostream &OS, bool SummarizeTypes) { + DWARFDie TD = getDIEForOffset(TypeOffset + getOffset()); + const char *Name = TD.getName(DINameKind::ShortName); + + if (SummarizeTypes) { + OS << "name = '" << Name << "'" + << " type_signature = " << format("0x%016" PRIx64, TypeHash) + << " length = " << format("0x%08x", getLength()) << '\n'; + return; + } + + OS << format("0x%08x", getOffset()) << ": Type Unit:" + << " length = " << format("0x%08x", getLength()) + << " version = " << format("0x%04x", getVersion()); + if (getVersion() >= 5) + OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << " abbr_offset = " << format("0x%04x", getAbbreviations()->getOffset()) + << " addr_size = " << format("0x%02x", getAddressByteSize()) + << " name = '" << Name << "'" + << " type_signature = " << format("0x%016" PRIx64, TypeHash) + << " type_offset = " << format("0x%04x", TypeOffset) + << " (next unit at " << format("0x%08x", getNextUnitOffset()) << ")\n"; + + if (DWARFDie TU = getUnitDIE(false)) + TU.dump(OS, -1U); + else + OS << "<type unit can't be parsed!>\n\n"; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp new file mode 100644 index 000000000000..c5add6a478b3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -0,0 +1,442 @@ +//===-- DWARFUnit.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <vector> + +using namespace llvm; +using namespace dwarf; + +void DWARFUnitSectionBase::parse(DWARFContext &C, const DWARFSection &Section) { + parseImpl(C, Section, C.getDebugAbbrev(), &C.getRangeSection(), + C.getStringSection(), StringRef(), &C.getAddrSection(), + C.getLineSection().Data, C.isLittleEndian(), false); +} + +void DWARFUnitSectionBase::parseDWO(DWARFContext &C, + const DWARFSection &DWOSection, + DWARFUnitIndex *Index) { + parseImpl(C, DWOSection, C.getDebugAbbrevDWO(), &C.getRangeDWOSection(), + C.getStringDWOSection(), C.getStringOffsetDWOSection(), + &C.getAddrSection(), C.getLineDWOSection().Data, C.isLittleEndian(), + true); +} + +DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, + const DWARFDebugAbbrev *DA, const DWARFSection *RS, + StringRef SS, StringRef SOS, const DWARFSection *AOS, + StringRef LS, bool LE, bool IsDWO, + const DWARFUnitSectionBase &UnitSection, + const DWARFUnitIndex::Entry *IndexEntry) + : Context(DC), InfoSection(Section), Abbrev(DA), RangeSection(RS), + LineSection(LS), StringSection(SS), StringOffsetSection([&]() { + if (IndexEntry) + if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS)) + return SOS.slice(C->Offset, C->Offset + C->Length); + return SOS; + }()), + AddrOffsetSection(AOS), isLittleEndian(LE), isDWO(IsDWO), + UnitSection(UnitSection), IndexEntry(IndexEntry) { + clear(); +} + +DWARFUnit::~DWARFUnit() = default; + +bool DWARFUnit::getAddrOffsetSectionItem(uint32_t Index, + uint64_t &Result) const { + uint32_t Offset = AddrOffsetSectionBase + Index * AddrSize; + if (AddrOffsetSection->Data.size() < Offset + AddrSize) + return false; + DataExtractor DA(AddrOffsetSection->Data, isLittleEndian, AddrSize); + Result = getRelocatedValue(DA, AddrSize, &Offset, &AddrOffsetSection->Relocs); + return true; +} + +bool DWARFUnit::getStringOffsetSectionItem(uint32_t Index, + uint32_t &Result) const { + // FIXME: string offset section entries are 8-byte for DWARF64. + const uint32_t ItemSize = 4; + uint32_t Offset = Index * ItemSize; + if (StringOffsetSection.size() < Offset + ItemSize) + return false; + DataExtractor DA(StringOffsetSection, isLittleEndian, 0); + Result = DA.getU32(&Offset); + return true; +} + +bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) { + Length = debug_info.getU32(offset_ptr); + Version = debug_info.getU16(offset_ptr); + uint64_t AbbrOffset; + if (Version >= 5) { + UnitType = debug_info.getU8(offset_ptr); + AddrSize = debug_info.getU8(offset_ptr); + AbbrOffset = debug_info.getU32(offset_ptr); + } else { + AbbrOffset = debug_info.getU32(offset_ptr); + AddrSize = debug_info.getU8(offset_ptr); + } + 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; + } + + bool LengthOK = debug_info.isValidOffset(getNextUnitOffset() - 1); + bool VersionOK = DWARFContext::isSupportedVersion(Version); + bool AddrSizeOK = AddrSize == 4 || AddrSize == 8; + + if (!LengthOK || !VersionOK || !AddrSizeOK) + return false; + + Abbrevs = Abbrev->getAbbreviationDeclarationSet(AbbrOffset); + return Abbrevs != nullptr; +} + +bool DWARFUnit::extract(DataExtractor debug_info, uint32_t *offset_ptr) { + clear(); + + Offset = *offset_ptr; + + if (debug_info.isValidOffset(*offset_ptr)) { + if (extractImpl(debug_info, offset_ptr)) + return true; + + // reset the offset to where we tried to parse from if anything went wrong + *offset_ptr = Offset; + } + + return false; +} + +bool DWARFUnit::extractRangeList(uint32_t RangeListOffset, + DWARFDebugRangeList &RangeList) const { + // Require that compile unit is extracted. + assert(!DieArray.empty()); + DataExtractor RangesData(RangeSection->Data, isLittleEndian, AddrSize); + uint32_t ActualRangeListOffset = RangeSectionBase + RangeListOffset; + return RangeList.extract(RangesData, &ActualRangeListOffset, + RangeSection->Relocs); +} + +void DWARFUnit::clear() { + Offset = 0; + Length = 0; + Version = 0; + Abbrevs = nullptr; + AddrSize = 0; + BaseAddr = 0; + RangeSectionBase = 0; + AddrOffsetSectionBase = 0; + clearDIEs(false); + DWO.reset(); +} + +const char *DWARFUnit::getCompilationDir() { + return dwarf::toString(getUnitDIE().find(DW_AT_comp_dir), nullptr); +} + +Optional<uint64_t> DWARFUnit::getDWOId() { + return toUnsigned(getUnitDIE().find(DW_AT_GNU_dwo_id)); +} + +void DWARFUnit::extractDIEsToVector( + bool AppendCUDie, bool AppendNonCUDies, + std::vector<DWARFDebugInfoEntry> &Dies) const { + if (!AppendCUDie && !AppendNonCUDies) + return; + + // Set the offset to that of the first DIE and calculate the start of the + // next compilation unit header. + uint32_t DIEOffset = Offset + getHeaderSize(); + uint32_t NextCUOffset = getNextUnitOffset(); + DWARFDebugInfoEntry DIE; + DataExtractor 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) + fprintf(stderr, "warning: DWARF compile unit extends beyond its " + "bounds cu 0x%8.8x at 0x%8.8x'\n", getOffset(), DIEOffset); +} + +size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { + if ((CUDieOnly && !DieArray.empty()) || + DieArray.size() > 1) + return 0; // Already parsed. + + bool HasCUDie = !DieArray.empty(); + extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray); + + if (DieArray.empty()) + return 0; + + // If CU DIE was just parsed, copy several attribute values from it. + if (!HasCUDie) { + DWARFDie UnitDie = getUnitDIE(); + auto BaseAddr = toAddress(UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc})); + if (BaseAddr) + setBaseAddress(*BaseAddr); + AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); + RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); + // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for + // skeleton CU DIE, so that DWARF users not aware of it are not broken. + } + + return DieArray.size(); +} + +bool DWARFUnit::parseDWO() { + if (isDWO) + return false; + if (DWO.get()) + return false; + DWARFDie UnitDie = getUnitDIE(); + if (!UnitDie) + return false; + auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); + if (!DWOFileName) + return false; + auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); + SmallString<16> AbsolutePath; + if (sys::path::is_relative(*DWOFileName) && CompilationDir && + *CompilationDir) { + sys::path::append(AbsolutePath, *CompilationDir); + } + sys::path::append(AbsolutePath, *DWOFileName); + auto DWOId = getDWOId(); + if (!DWOId) + return false; + auto DWOContext = Context.getDWOContext(AbsolutePath); + if (!DWOContext) + return false; + + DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); + if (!DWOCU) + return false; + DWO = std::shared_ptr<DWARFCompileUnit>(std::move(DWOContext), DWOCU); + // Share .debug_addr and .debug_ranges section with compile unit in .dwo + DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); + auto DWORangesBase = UnitDie.getRangesBaseAttribute(); + DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); + return true; +} + +void DWARFUnit::clearDIEs(bool KeepCUDie) { + if (DieArray.size() > (unsigned)KeepCUDie) { + // std::vectors never get any smaller when resized to a smaller size, + // or when clear() or erase() are called, the size will report that it + // is smaller, but the memory allocated remains intact (call capacity() + // to see this). So we need to create a temporary vector and swap the + // contents which will cause just the internal pointers to be swapped + // so that when temporary vector goes out of scope, it will destroy the + // contents. + std::vector<DWARFDebugInfoEntry> TmpArray; + DieArray.swap(TmpArray); + // Save at least the compile unit DIE + if (KeepCUDie) + DieArray.push_back(TmpArray.front()); + } +} + +void DWARFUnit::collectAddressRanges(DWARFAddressRangesVector &CURanges) { + DWARFDie UnitDie = getUnitDIE(); + if (!UnitDie) + return; + // First, check if unit DIE describes address ranges for the whole unit. + const auto &CUDIERanges = UnitDie.getAddressRanges(); + if (!CUDIERanges.empty()) { + CURanges.insert(CURanges.end(), CUDIERanges.begin(), CUDIERanges.end()); + return; + } + + // This function is usually called if there in no .debug_aranges section + // in order to produce a compile unit level set of address ranges that + // is accurate. If the DIEs weren't parsed, then we don't want all dies for + // all compile units to stay loaded when they weren't needed. So we can end + // up parsing the DWARF and then throwing them all away to keep memory usage + // down. + const bool ClearDIEs = extractDIEsIfNeeded(false) > 1; + getUnitDIE().collectChildrenAddressRanges(CURanges); + + // Collect address ranges from DIEs in .dwo if necessary. + bool DWOCreated = parseDWO(); + if (DWO) + DWO->collectAddressRanges(CURanges); + if (DWOCreated) + DWO.reset(); + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed. + if (ClearDIEs) + clearDIEs(true); +} + +void DWARFUnit::updateAddressDieMap(DWARFDie Die) { + if (Die.isSubroutineDIE()) { + for (const auto &R : Die.getAddressRanges()) { + // 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); + } + } + // Parent DIEs are added to the AddrDieMap prior to the Children DIEs to + // simplify the logic to update AddrDieMap. The child's range will always + // be equal or smaller than the parent's range. With this assumption, when + // adding one range into the map, it will at most split a range into 3 + // sub-ranges. + for (DWARFDie Child = Die.getFirstChild(); Child; Child = Child.getSibling()) + updateAddressDieMap(Child); +} + +DWARFDie DWARFUnit::getSubroutineForAddress(uint64_t Address) { + extractDIEsIfNeeded(false); + if (AddrDieMap.empty()) + updateAddressDieMap(getUnitDIE()); + auto R = AddrDieMap.upper_bound(Address); + if (R == AddrDieMap.begin()) + return DWARFDie(); + // upper_bound's previous item contains Address. + --R; + if (Address >= R->second.first) + return DWARFDie(); + return R->second.second; +} + +void +DWARFUnit::getInlinedChainForAddress(uint64_t Address, + SmallVectorImpl<DWARFDie> &InlinedChain) { + assert(InlinedChain.empty()); + // Try to look for subprogram DIEs in the DWO file. + parseDWO(); + // First, find the subroutine that contains the given address (the leaf + // of inlined chain). + DWARFDie SubroutineDIE = + (DWO ? DWO.get() : this)->getSubroutineForAddress(Address); + + while (SubroutineDIE) { + if (SubroutineDIE.isSubroutineDIE()) + InlinedChain.push_back(SubroutineDIE); + SubroutineDIE = SubroutineDIE.getParent(); + } +} + +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(); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp new file mode 100644 index 000000000000..0981a4dfdfa5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp @@ -0,0 +1,172 @@ +//===- DWARFUnitIndex.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cinttypes> +#include <cstdint> + +using namespace llvm; + +bool DWARFUnitIndex::Header::parse(DataExtractor IndexData, + uint32_t *OffsetPtr) { + if (!IndexData.isValidOffsetForDataOfSize(*OffsetPtr, 16)) + return false; + Version = IndexData.getU32(OffsetPtr); + NumColumns = IndexData.getU32(OffsetPtr); + NumUnits = IndexData.getU32(OffsetPtr); + NumBuckets = IndexData.getU32(OffsetPtr); + return Version <= 2; +} + +void DWARFUnitIndex::Header::dump(raw_ostream &OS) const { + OS << format("version = %u slots = %u\n\n", Version, NumBuckets); +} + +bool DWARFUnitIndex::parse(DataExtractor IndexData) { + bool b = parseImpl(IndexData); + if (!b) { + // Make sure we don't try to dump anything + Header.NumBuckets = 0; + // Release any partially initialized data. + ColumnKinds.reset(); + Rows.reset(); + } + return b; +} + +bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { + uint32_t Offset = 0; + if (!Header.parse(IndexData, &Offset)) + return false; + + if (!IndexData.isValidOffsetForDataOfSize( + Offset, Header.NumBuckets * (8 + 4) + + (2 * Header.NumUnits + 1) * 4 * Header.NumColumns)) + return false; + + Rows = llvm::make_unique<Entry[]>(Header.NumBuckets); + auto Contribs = + llvm::make_unique<Entry::SectionContribution *[]>(Header.NumUnits); + ColumnKinds = llvm::make_unique<DWARFSectionKind[]>(Header.NumColumns); + + // Read Hash Table of Signatures + for (unsigned i = 0; i != Header.NumBuckets; ++i) + Rows[i].Signature = IndexData.getU64(&Offset); + + // Read Parallel Table of Indexes + for (unsigned i = 0; i != Header.NumBuckets; ++i) { + auto Index = IndexData.getU32(&Offset); + if (!Index) + continue; + Rows[i].Index = this; + Rows[i].Contributions = + llvm::make_unique<Entry::SectionContribution[]>(Header.NumColumns); + Contribs[Index - 1] = Rows[i].Contributions.get(); + } + + // Read the Column Headers + for (unsigned i = 0; i != Header.NumColumns; ++i) { + ColumnKinds[i] = static_cast<DWARFSectionKind>(IndexData.getU32(&Offset)); + if (ColumnKinds[i] == InfoColumnKind) { + if (InfoColumn != -1) + return false; + InfoColumn = i; + } + } + + if (InfoColumn == -1) + return false; + + // Read Table of Section Offsets + for (unsigned i = 0; i != Header.NumUnits; ++i) { + auto *Contrib = Contribs[i]; + for (unsigned i = 0; i != Header.NumColumns; ++i) + Contrib[i].Offset = IndexData.getU32(&Offset); + } + + // Read Table of Section Sizes + for (unsigned i = 0; i != Header.NumUnits; ++i) { + auto *Contrib = Contribs[i]; + for (unsigned i = 0; i != Header.NumColumns; ++i) + Contrib[i].Length = IndexData.getU32(&Offset); + } + + return true; +} + +StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { +#define CASE(DS) \ + case DW_SECT_##DS: \ + return #DS; + switch (DS) { + CASE(INFO); + CASE(TYPES); + CASE(ABBREV); + CASE(LINE); + CASE(LOC); + CASE(STR_OFFSETS); + CASE(MACINFO); + CASE(MACRO); + } + llvm_unreachable("unknown DWARFSectionKind"); +} + +void DWARFUnitIndex::dump(raw_ostream &OS) const { + if (!Header.NumBuckets) + 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 { + for (uint32_t i = 0; i != Header.NumBuckets; ++i) + if (const auto &Contribs = Rows[i].Contributions) + if (Contribs[InfoColumn].Offset == Offset) + return &Rows[i]; + return nullptr; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp new file mode 100644 index 000000000000..8a544296f65c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -0,0 +1,277 @@ +//===- DWARFVerifier.cpp --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/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/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/Support/raw_ostream.h" +#include <map> +#include <set> +#include <vector> + +using namespace llvm; +using namespace dwarf; +using namespace object; + +void DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, + DWARFAttribute &AttrValue) { + const auto Attr = AttrValue.Attr; + switch (Attr) { + case DW_AT_ranges: + // Make sure the offset in the DW_AT_ranges attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DCtx.getRangeSection().Data.size()) { + ++NumDebugInfoErrors; + OS << "error: DW_AT_ranges offset is beyond .debug_ranges " + "bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + } else { + ++NumDebugInfoErrors; + OS << "error: DIE has invalid DW_AT_ranges encoding:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + case DW_AT_stmt_list: + // Make sure the offset in the DW_AT_stmt_list attribute is valid. + if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { + if (*SectionOffset >= DCtx.getLineSection().Data.size()) { + ++NumDebugInfoErrors; + OS << "error: DW_AT_stmt_list offset is beyond .debug_line " + "bounds: " + << format("0x%08" PRIx32, *SectionOffset) << "\n"; + Die.dump(OS, 0); + OS << "\n"; + } + } else { + ++NumDebugInfoErrors; + OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + + default: + break; + } +} + +void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, + DWARFAttribute &AttrValue) { + 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 DieCU = Die.getDwarfUnit(); + auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset(); + auto CUOffset = AttrValue.Value.getRawUValue(); + if (CUOffset >= CUSize) { + ++NumDebugInfoErrors; + OS << "error: " << FormEncodingString(Form) << " CU offset " + << format("0x%08" PRIx32, CUOffset) + << " is invalid (must be less than CU size of " + << format("0x%08" PRIx32, CUSize) << "):\n"; + Die.dump(OS, 0); + OS << "\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 >= DCtx.getInfoSection().Data.size()) { + ++NumDebugInfoErrors; + OS << "error: DW_FORM_ref_addr offset beyond .debug_info " + "bounds:\n"; + Die.dump(OS, 0); + OS << "\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 >= DCtx.getStringSection().size()) { + ++NumDebugInfoErrors; + OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n"; + Die.dump(OS, 0); + OS << "\n"; + } + break; + } + default: + break; + } +} + +void 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"; + for (auto Pair : ReferenceToDIEOffsets) { + auto Die = DCtx.getDIEForOffset(Pair.first); + if (Die) + continue; + ++NumDebugInfoErrors; + OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first) + << ". Offset is in between DIEs:\n"; + for (auto Offset : Pair.second) { + auto ReferencingDie = DCtx.getDIEForOffset(Offset); + ReferencingDie.dump(OS, 0); + OS << "\n"; + } + OS << "\n"; + } +} + +bool DWARFVerifier::handleDebugInfo() { + NumDebugInfoErrors = 0; + OS << "Verifying .debug_info...\n"; + for (const auto &CU : DCtx.compile_units()) { + unsigned NumDies = CU->getNumDIEs(); + for (unsigned I = 0; I < NumDies; ++I) { + auto Die = CU->getDIEAtIndex(I); + const auto Tag = Die.getTag(); + if (Tag == DW_TAG_null) + continue; + for (auto AttrValue : Die.attributes()) { + verifyDebugInfoAttribute(Die, AttrValue); + verifyDebugInfoForm(Die, AttrValue); + } + } + } + verifyDebugInfoReferences(); + return NumDebugInfoErrors == 0; +} + +void DWARFVerifier::verifyDebugLineStmtOffsets() { + std::map<uint64_t, DWARFDie> StmtListToDie; + for (const auto &CU : DCtx.compile_units()) { + auto Die = CU->getUnitDIE(); + // Get the attribute value as a section offset. No need to produce an + // error here if the encoding isn't correct because we validate this in + // the .debug_info verifier. + auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list)); + if (!StmtSectionOffset) + continue; + const uint32_t LineTableOffset = *StmtSectionOffset; + auto LineTable = DCtx.getLineTableForUnit(CU.get()); + if (LineTableOffset < DCtx.getLineSection().Data.size()) { + if (!LineTable) { + ++NumDebugLineErrors; + OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + Die.dump(OS, 0); + OS << '\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; + OS << "error: two compile unit DIEs, " + << format("0x%08" PRIx32, Iter->second.getOffset()) << " and " + << format("0x%08" PRIx32, Die.getOffset()) + << ", have the same DW_AT_stmt_list section offset:\n"; + Iter->second.dump(OS, 0); + Die.dump(OS, 0); + OS << '\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; + uint32_t MaxFileIndex = LineTable->Prologue.FileNames.size(); + uint64_t PrevAddress = 0; + uint32_t RowIndex = 0; + for (const auto &Row : LineTable->Rows) { + if (Row.Address < PrevAddress) { + ++NumDebugLineErrors; + OS << "error: .debug_line[" + << format("0x%08" PRIx32, + *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'; + } + + if (Row.File > MaxFileIndex) { + ++NumDebugLineErrors; + OS << "error: .debug_line[" + << format("0x%08" PRIx32, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "][" << RowIndex << "] has invalid file index " << Row.File + << " (valid values are [1," << MaxFileIndex << "]):\n"; + DWARFDebugLine::Row::dumpTableHeader(OS); + Row.dump(OS); + OS << '\n'; + } + if (Row.EndSequence) + PrevAddress = 0; + else + PrevAddress = Row.Address; + ++RowIndex; + } + } +} + +bool DWARFVerifier::handleDebugLine() { + NumDebugLineErrors = 0; + OS << "Verifying .debug_line...\n"; + verifyDebugLineStmtOffsets(); + verifyDebugLineRows(); + return NumDebugLineErrors == 0; +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp b/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp new file mode 100644 index 000000000000..d4f44e446954 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.cpp @@ -0,0 +1,40 @@ +//===- SyntaxHighlighting.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SyntaxHighlighting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace dwarf; +using namespace syntax; + +static cl::opt<cl::boolOrDefault> + UseColor("color", + cl::desc("use colored syntax highlighting (default=autodetect)"), + cl::init(cl::BOU_UNSET)); + +WithColor::WithColor(raw_ostream &OS, enum HighlightColor Type) : OS(OS) { + // Detect color from terminal type unless the user passed the --color option. + if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE) { + switch (Type) { + case Address: OS.changeColor(raw_ostream::YELLOW); break; + case String: OS.changeColor(raw_ostream::GREEN); break; + case Tag: OS.changeColor(raw_ostream::BLUE); break; + case Attribute: OS.changeColor(raw_ostream::CYAN); break; + case Enumerator: OS.changeColor(raw_ostream::MAGENTA); break; + case Macro: OS.changeColor(raw_ostream::RED); break; + } + } +} + +WithColor::~WithColor() { + if (UseColor == cl::BOU_UNSET ? OS.has_colors() : UseColor == cl::BOU_TRUE) + OS.resetColor(); +} diff --git a/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.h b/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.h new file mode 100644 index 000000000000..277de973dbf0 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/DWARF/SyntaxHighlighting.h @@ -0,0 +1,42 @@ +//===- SyntaxHighlighting.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H +#define LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H + +namespace llvm { + +class raw_ostream; + +namespace dwarf { +namespace syntax { + +// Symbolic names for various syntax elements. +enum HighlightColor { Address, String, Tag, Attribute, Enumerator, Macro }; + +/// An RAII object that temporarily switches an output stream to a +/// specific color. +class WithColor { + raw_ostream &OS; + +public: + /// To be used like this: WithColor(OS, syntax::String) << "text"; + WithColor(raw_ostream &OS, enum HighlightColor Type); + ~WithColor(); + + raw_ostream& get() { return OS; } + operator raw_ostream& () { return OS; } +}; + +} // end namespace syntax +} // end namespace dwarf + +} // end namespace llvm + +#endif // LLVM_LIB_DEBUGINFO_SYNTAXHIGHLIGHTING_H diff --git a/contrib/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp b/contrib/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp new file mode 100644 index 000000000000..5b1b5d8dc4d5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp @@ -0,0 +1,282 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFError.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; + +namespace { +const uint32_t kSuperBlockBlock = 0; +const uint32_t kFreePageMap0Block = 1; +const uint32_t kFreePageMap1Block = 2; +const uint32_t kNumReservedPages = 3; + +const uint32_t kDefaultFreePageMap = kFreePageMap0Block; +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), + MininumBlocks(MinBlockCount), 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; + FreeBlocks.resize(AllocBlocks + FreeBlocks.size(), true); + } + + 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::build() { + 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.size() > 0) { + 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()); + } + } + + return L; +} diff --git a/contrib/llvm/lib/DebugInfo/MSF/MSFCommon.cpp b/contrib/llvm/lib/DebugInfo/MSF/MSFCommon.cpp new file mode 100644 index 000000000000..fdab7884646e --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/MSF/MSFCommon.cpp @@ -0,0 +1,57 @@ +//===- MSFCommon.cpp - Common types and functions for MSF files -*- 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/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MSFError.h" + +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(); +} diff --git a/contrib/llvm/lib/DebugInfo/MSF/MSFError.cpp b/contrib/llvm/lib/DebugInfo/MSF/MSFError.cpp new file mode 100644 index 000000000000..1b8294e47e75 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/MSF/MSFError.cpp @@ -0,0 +1,70 @@ +//===- MSFError.cpp - Error extensions for MSF files ------------*- 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/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"); + } +}; +} // end anonymous namespace + +static ManagedStatic<MSFErrorCategory> Category; + +char MSFError::ID = 0; + +MSFError::MSFError(msf_error_code C) : MSFError(C, "") {} + +MSFError::MSFError(const std::string &Context) + : MSFError(msf_error_code::unspecified, Context) {} + +MSFError::MSFError(msf_error_code C, const std::string &Context) : Code(C) { + ErrMsg = "MSF Error: "; + std::error_code EC = convertToErrorCode(); + if (Code != msf_error_code::unspecified) + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void MSFError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +const std::string &MSFError::getErrorMessage() const { return ErrMsg; } + +std::error_code MSFError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/contrib/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp b/contrib/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp new file mode 100644 index 000000000000..dfdeb8414212 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp @@ -0,0 +1,403 @@ +//===- MappedBlockStream.cpp - Reads stream data from an MSF file ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" + +#include "llvm/DebugInfo/MSF/IMSFFile.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MSFStreamLayout.h" +#include "llvm/Support/BinaryStreamError.h" + +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)...) {} +}; +} + +static void initializeFpmStreamLayout(const MSFLayout &Layout, + MSFStreamLayout &FpmLayout) { + uint32_t NumFpmIntervals = msf::getNumFpmIntervals(Layout); + support::ulittle32_t FpmBlock = Layout.SB->FreeBlockMapBlock; + assert(FpmBlock == 1 || FpmBlock == 2); + while (NumFpmIntervals > 0) { + FpmLayout.Blocks.push_back(FpmBlock); + FpmBlock += msf::getFpmIntervalLength(Layout); + --NumFpmIntervals; + } + FpmLayout.Length = msf::getFullFpmByteSize(Layout); +} + +typedef std::pair<uint32_t, uint32_t> Interval; +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) + : BlockSize(BlockSize), StreamLayout(Layout), MsfData(MsfData) {} + +std::unique_ptr<MappedBlockStream> +MappedBlockStream::createStream(uint32_t BlockSize, + const MSFStreamLayout &Layout, + BinaryStreamRef MsfData) { + return llvm::make_unique<MappedBlockStreamImpl<MappedBlockStream>>( + BlockSize, Layout, MsfData); +} + +std::unique_ptr<MappedBlockStream> MappedBlockStream::createIndexedStream( + const MSFLayout &Layout, BinaryStreamRef MsfData, uint32_t StreamIndex) { + assert(StreamIndex < Layout.StreamMap.size() && "Invalid stream index"); + MSFStreamLayout SL; + SL.Blocks = Layout.StreamMap[StreamIndex]; + SL.Length = Layout.StreamSizes[StreamIndex]; + return llvm::make_unique<MappedBlockStreamImpl<MappedBlockStream>>( + Layout.SB->BlockSize, SL, MsfData); +} + +std::unique_ptr<MappedBlockStream> +MappedBlockStream::createDirectoryStream(const MSFLayout &Layout, + BinaryStreamRef MsfData) { + MSFStreamLayout SL; + SL.Blocks = Layout.DirectoryBlocks; + SL.Length = Layout.SB->NumDirectoryBytes; + return createStream(Layout.SB->BlockSize, SL, MsfData); +} + +std::unique_ptr<MappedBlockStream> +MappedBlockStream::createFpmStream(const MSFLayout &Layout, + BinaryStreamRef MsfData) { + MSFStreamLayout SL; + initializeFpmStreamLayout(Layout, SL); + return createStream(Layout.SB->BlockSize, SL, MsfData); +} + +Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) { + // Make sure we aren't trying to read beyond the end of the stream. + if (auto EC = checkOffset(Offset, Size)) + 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 *>(Pool.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 = checkOffset(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 = + llvm::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 = checkOffset(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(); +} + +uint32_t MappedBlockStream::getNumBytesCopied() const { + return static_cast<uint32_t>(Pool.getBytesAllocated()); +} + +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) + : ReadInterface(BlockSize, Layout, MsfData), WriteInterface(MsfData) {} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createStream(uint32_t BlockSize, + const MSFStreamLayout &Layout, + WritableBinaryStreamRef MsfData) { + return llvm::make_unique<MappedBlockStreamImpl<WritableMappedBlockStream>>( + BlockSize, Layout, MsfData); +} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createIndexedStream(const MSFLayout &Layout, + WritableBinaryStreamRef MsfData, + uint32_t StreamIndex) { + 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); +} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createDirectoryStream( + const MSFLayout &Layout, WritableBinaryStreamRef MsfData) { + MSFStreamLayout SL; + SL.Blocks = Layout.DirectoryBlocks; + SL.Length = Layout.SB->NumDirectoryBytes; + return createStream(Layout.SB->BlockSize, SL, MsfData); +} + +std::unique_ptr<WritableMappedBlockStream> +WritableMappedBlockStream::createFpmStream(const MSFLayout &Layout, + WritableBinaryStreamRef MsfData) { + MSFStreamLayout SL; + initializeFpmStreamLayout(Layout, SL); + return createStream(Layout.SB->BlockSize, SL, MsfData); +} + +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 = checkOffset(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/contrib/llvm/lib/DebugInfo/PDB/DIA/DIADataStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIADataStream.cpp new file mode 100644 index 000000000000..7eabed8cad48 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIADataStream.cpp @@ -0,0 +1,75 @@ +//===- DIADataStream.cpp - DIA implementation of IPDBDataStream -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIADataStream.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/ConvertUTF.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 { + CComBSTR Name16; + if (S_OK != StreamData->get_name(&Name16)) + return std::string(); + + std::string Name8; + llvm::ArrayRef<char> Name16Bytes(reinterpret_cast<char *>(Name16.m_str), + Name16.ByteLength()); + if (!llvm::convertUTF16ToUTF8String(Name16Bytes, Name8)) + return std::string(); + return Name8; +} + +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(); } + +DIADataStream *DIADataStream::clone() const { + CComPtr<IDiaEnumDebugStreamData> EnumeratorClone; + if (S_OK != StreamData->Clone(&EnumeratorClone)) + return nullptr; + + return new DIADataStream(EnumeratorClone); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp new file mode 100644 index 000000000000..cae817c1b367 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp @@ -0,0 +1,54 @@ +//==- DIAEnumDebugStreams.cpp - DIA Debug Stream Enumerator impl -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIADataStream.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.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(); } + +DIAEnumDebugStreams *DIAEnumDebugStreams::clone() const { + CComPtr<IDiaEnumDebugStreams> EnumeratorClone; + if (S_OK != Enumerator->Clone(&EnumeratorClone)) + return nullptr; + return new DIAEnumDebugStreams(EnumeratorClone); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp new file mode 100644 index 000000000000..4741d9c9a849 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp @@ -0,0 +1,51 @@ +//==- DIAEnumLineNumbers.cpp - DIA Line Number Enumerator impl ---*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h" +#include "llvm/DebugInfo/PDB/DIA/DIALineNumber.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(); } + +DIAEnumLineNumbers *DIAEnumLineNumbers::clone() const { + CComPtr<IDiaEnumLineNumbers> EnumeratorClone; + if (S_OK != Enumerator->Clone(&EnumeratorClone)) + return nullptr; + return new DIAEnumLineNumbers(EnumeratorClone); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp new file mode 100644 index 000000000000..ccf8c4e622cc --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp @@ -0,0 +1,51 @@ +//==- DIAEnumSourceFiles.cpp - DIA Source File Enumerator impl ---*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSourceFiles.h" +#include "llvm/DebugInfo/PDB/DIA/DIASourceFile.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(); } + +DIAEnumSourceFiles *DIAEnumSourceFiles::clone() const { + CComPtr<IDiaEnumSourceFiles> EnumeratorClone; + if (S_OK != Enumerator->Clone(&EnumeratorClone)) + return nullptr; + return new DIAEnumSourceFiles(Session, EnumeratorClone); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp new file mode 100644 index 000000000000..3c211b569044 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp @@ -0,0 +1,55 @@ +//==- DIAEnumSymbols.cpp - DIA Symbol Enumerator impl ------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h" +#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.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(); } + +DIAEnumSymbols *DIAEnumSymbols::clone() const { + CComPtr<IDiaEnumSymbols> EnumeratorClone; + if (S_OK != Enumerator->Clone(&EnumeratorClone)) + return nullptr; + return new DIAEnumSymbols(Session, EnumeratorClone); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAError.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAError.cpp new file mode 100644 index 000000000000..0da877b0fbad --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIAError.cpp @@ -0,0 +1,58 @@ +#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 ManagedStatic<DIAErrorCategory> Category; + +char DIAError::ID = 0; + +DIAError::DIAError(dia_error_code C) : DIAError(C, "") {} + +DIAError::DIAError(StringRef Context) + : DIAError(dia_error_code::unspecified, Context) {} + +DIAError::DIAError(dia_error_code C, StringRef Context) : Code(C) { + ErrMsg = "DIA Error: "; + std::error_code EC = convertToErrorCode(); + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void DIAError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +StringRef DIAError::getErrorMessage() const { return ErrMsg; } + +std::error_code DIAError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp new file mode 100644 index 000000000000..b19be6b595ab --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp @@ -0,0 +1,76 @@ +//===- DIALineNumber.cpp - DIA implementation of IPDBLineNumber -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/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/contrib/llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp new file mode 100644 index 000000000000..4e2474c51cb1 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp @@ -0,0 +1,1126 @@ +//===- DIARawSymbol.cpp - DIA implementation of IPDBRawSymbol ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h" +#include "llvm/DebugInfo/PDB/DIA/DIASession.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 *)) { + CComBSTR Result16; + if (S_OK != (Symbol->*Method)(&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_UniqueId +PrivateGetDIAValue(IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(GUID *)) { + GUID Result; + if (S_OK != (Symbol->*Method)(&Result)) + return PDB_UniqueId(); + + static_assert(sizeof(PDB_UniqueId) == sizeof(GUID), + "PDB_UniqueId is the wrong size!"); + PDB_UniqueId IdResult; + ::memcpy(&IdResult, &Result, sizeof(GUID)); + return IdResult; +} + +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)) { + OS << "\n"; + OS.indent(Indent); + OS << Name << ": " << Value; + } +} + +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)) { + OS << "\n"; + OS.indent(Indent); + OS << Name << ": " << Result; + } + ::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; + OS << "\n"; + OS.indent(Indent); + Variant V = VariantFromVARIANT(Value); + OS << V; +} +} + +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; +} +} + +DIARawSymbol::DIARawSymbol(const DIASession &PDBSession, + CComPtr<IDiaSymbol> DiaSymbol) + : Session(PDBSession), Symbol(DiaSymbol) {} + +#define RAW_METHOD_DUMP(Stream, Method) \ + DumpDIAValue(Stream, Indent, StringRef(#Method), Symbol, &IDiaSymbol::Method); + +void DIARawSymbol::dump(raw_ostream &OS, int Indent) const { + RAW_METHOD_DUMP(OS, get_access) + RAW_METHOD_DUMP(OS, get_addressOffset) + RAW_METHOD_DUMP(OS, get_addressSection) + RAW_METHOD_DUMP(OS, get_age) + RAW_METHOD_DUMP(OS, get_arrayIndexTypeId) + RAW_METHOD_DUMP(OS, get_backEndMajor) + RAW_METHOD_DUMP(OS, get_backEndMinor) + RAW_METHOD_DUMP(OS, get_backEndBuild) + RAW_METHOD_DUMP(OS, get_backEndQFE) + RAW_METHOD_DUMP(OS, get_baseDataOffset) + RAW_METHOD_DUMP(OS, get_baseDataSlot) + RAW_METHOD_DUMP(OS, get_baseSymbolId) + RAW_METHOD_DUMP(OS, get_baseType) + RAW_METHOD_DUMP(OS, get_bitPosition) + RAW_METHOD_DUMP(OS, get_callingConvention) + RAW_METHOD_DUMP(OS, get_classParentId) + RAW_METHOD_DUMP(OS, get_compilerName) + RAW_METHOD_DUMP(OS, get_count) + RAW_METHOD_DUMP(OS, get_countLiveRanges) + RAW_METHOD_DUMP(OS, get_frontEndMajor) + RAW_METHOD_DUMP(OS, get_frontEndMinor) + RAW_METHOD_DUMP(OS, get_frontEndBuild) + RAW_METHOD_DUMP(OS, get_frontEndQFE) + RAW_METHOD_DUMP(OS, get_lexicalParentId) + RAW_METHOD_DUMP(OS, get_libraryName) + RAW_METHOD_DUMP(OS, get_liveRangeStartAddressOffset) + RAW_METHOD_DUMP(OS, get_liveRangeStartAddressSection) + RAW_METHOD_DUMP(OS, get_liveRangeStartRelativeVirtualAddress) + RAW_METHOD_DUMP(OS, get_localBasePointerRegisterId) + RAW_METHOD_DUMP(OS, get_lowerBoundId) + RAW_METHOD_DUMP(OS, get_memorySpaceKind) + RAW_METHOD_DUMP(OS, get_name) + RAW_METHOD_DUMP(OS, get_numberOfAcceleratorPointerTags) + RAW_METHOD_DUMP(OS, get_numberOfColumns) + RAW_METHOD_DUMP(OS, get_numberOfModifiers) + RAW_METHOD_DUMP(OS, get_numberOfRegisterIndices) + RAW_METHOD_DUMP(OS, get_numberOfRows) + RAW_METHOD_DUMP(OS, get_objectFileName) + RAW_METHOD_DUMP(OS, get_oemId) + RAW_METHOD_DUMP(OS, get_oemSymbolId) + RAW_METHOD_DUMP(OS, get_offsetInUdt) + RAW_METHOD_DUMP(OS, get_platform) + RAW_METHOD_DUMP(OS, get_rank) + RAW_METHOD_DUMP(OS, get_registerId) + RAW_METHOD_DUMP(OS, get_registerType) + RAW_METHOD_DUMP(OS, get_relativeVirtualAddress) + RAW_METHOD_DUMP(OS, get_samplerSlot) + RAW_METHOD_DUMP(OS, get_signature) + RAW_METHOD_DUMP(OS, get_sizeInUdt) + RAW_METHOD_DUMP(OS, get_slot) + RAW_METHOD_DUMP(OS, get_sourceFileName) + RAW_METHOD_DUMP(OS, get_stride) + RAW_METHOD_DUMP(OS, get_subTypeId) + RAW_METHOD_DUMP(OS, get_symbolsFileName) + RAW_METHOD_DUMP(OS, get_symIndexId) + RAW_METHOD_DUMP(OS, get_targetOffset) + RAW_METHOD_DUMP(OS, get_targetRelativeVirtualAddress) + RAW_METHOD_DUMP(OS, get_targetVirtualAddress) + RAW_METHOD_DUMP(OS, get_targetSection) + RAW_METHOD_DUMP(OS, get_textureSlot) + RAW_METHOD_DUMP(OS, get_timeStamp) + RAW_METHOD_DUMP(OS, get_token) + RAW_METHOD_DUMP(OS, get_typeId) + RAW_METHOD_DUMP(OS, get_uavSlot) + RAW_METHOD_DUMP(OS, get_undecoratedName) + RAW_METHOD_DUMP(OS, get_unmodifiedTypeId) + RAW_METHOD_DUMP(OS, get_upperBoundId) + RAW_METHOD_DUMP(OS, get_virtualBaseDispIndex) + RAW_METHOD_DUMP(OS, get_virtualBaseOffset) + RAW_METHOD_DUMP(OS, get_virtualTableShapeId) + RAW_METHOD_DUMP(OS, get_dataKind) + RAW_METHOD_DUMP(OS, get_symTag) + RAW_METHOD_DUMP(OS, get_guid) + RAW_METHOD_DUMP(OS, get_offset) + RAW_METHOD_DUMP(OS, get_thisAdjust) + RAW_METHOD_DUMP(OS, get_virtualBasePointerOffset) + RAW_METHOD_DUMP(OS, get_locationType) + RAW_METHOD_DUMP(OS, get_machineType) + RAW_METHOD_DUMP(OS, get_thunkOrdinal) + RAW_METHOD_DUMP(OS, get_length) + RAW_METHOD_DUMP(OS, get_liveRangeLength) + RAW_METHOD_DUMP(OS, get_virtualAddress) + RAW_METHOD_DUMP(OS, get_udtKind) + RAW_METHOD_DUMP(OS, get_constructor) + RAW_METHOD_DUMP(OS, get_customCallingConvention) + RAW_METHOD_DUMP(OS, get_farReturn) + RAW_METHOD_DUMP(OS, get_code) + RAW_METHOD_DUMP(OS, get_compilerGenerated) + RAW_METHOD_DUMP(OS, get_constType) + RAW_METHOD_DUMP(OS, get_editAndContinueEnabled) + RAW_METHOD_DUMP(OS, get_function) + RAW_METHOD_DUMP(OS, get_stride) + RAW_METHOD_DUMP(OS, get_noStackOrdering) + RAW_METHOD_DUMP(OS, get_hasAlloca) + RAW_METHOD_DUMP(OS, get_hasAssignmentOperator) + RAW_METHOD_DUMP(OS, get_isCTypes) + RAW_METHOD_DUMP(OS, get_hasCastOperator) + RAW_METHOD_DUMP(OS, get_hasDebugInfo) + RAW_METHOD_DUMP(OS, get_hasEH) + RAW_METHOD_DUMP(OS, get_hasEHa) + RAW_METHOD_DUMP(OS, get_hasInlAsm) + RAW_METHOD_DUMP(OS, get_framePointerPresent) + RAW_METHOD_DUMP(OS, get_inlSpec) + RAW_METHOD_DUMP(OS, get_interruptReturn) + RAW_METHOD_DUMP(OS, get_hasLongJump) + RAW_METHOD_DUMP(OS, get_hasManagedCode) + RAW_METHOD_DUMP(OS, get_hasNestedTypes) + RAW_METHOD_DUMP(OS, get_noInline) + RAW_METHOD_DUMP(OS, get_noReturn) + RAW_METHOD_DUMP(OS, get_optimizedCodeDebugInfo) + RAW_METHOD_DUMP(OS, get_overloadedOperator) + RAW_METHOD_DUMP(OS, get_hasSEH) + RAW_METHOD_DUMP(OS, get_hasSecurityChecks) + RAW_METHOD_DUMP(OS, get_hasSetJump) + RAW_METHOD_DUMP(OS, get_strictGSCheck) + RAW_METHOD_DUMP(OS, get_isAcceleratorGroupSharedLocal) + RAW_METHOD_DUMP(OS, get_isAcceleratorPointerTagLiveRange) + RAW_METHOD_DUMP(OS, get_isAcceleratorStubFunction) + RAW_METHOD_DUMP(OS, get_isAggregated) + RAW_METHOD_DUMP(OS, get_intro) + RAW_METHOD_DUMP(OS, get_isCVTCIL) + RAW_METHOD_DUMP(OS, get_isConstructorVirtualBase) + RAW_METHOD_DUMP(OS, get_isCxxReturnUdt) + RAW_METHOD_DUMP(OS, get_isDataAligned) + RAW_METHOD_DUMP(OS, get_isHLSLData) + RAW_METHOD_DUMP(OS, get_isHotpatchable) + RAW_METHOD_DUMP(OS, get_indirectVirtualBaseClass) + RAW_METHOD_DUMP(OS, get_isInterfaceUdt) + RAW_METHOD_DUMP(OS, get_intrinsic) + RAW_METHOD_DUMP(OS, get_isLTCG) + RAW_METHOD_DUMP(OS, get_isLocationControlFlowDependent) + RAW_METHOD_DUMP(OS, get_isMSILNetmodule) + RAW_METHOD_DUMP(OS, get_isMatrixRowMajor) + RAW_METHOD_DUMP(OS, get_managed) + RAW_METHOD_DUMP(OS, get_msil) + RAW_METHOD_DUMP(OS, get_isMultipleInheritance) + RAW_METHOD_DUMP(OS, get_isNaked) + RAW_METHOD_DUMP(OS, get_nested) + RAW_METHOD_DUMP(OS, get_isOptimizedAway) + RAW_METHOD_DUMP(OS, get_packed) + RAW_METHOD_DUMP(OS, get_isPointerBasedOnSymbolValue) + RAW_METHOD_DUMP(OS, get_isPointerToDataMember) + RAW_METHOD_DUMP(OS, get_isPointerToMemberFunction) + RAW_METHOD_DUMP(OS, get_pure) + RAW_METHOD_DUMP(OS, get_RValueReference) + RAW_METHOD_DUMP(OS, get_isRefUdt) + RAW_METHOD_DUMP(OS, get_reference) + RAW_METHOD_DUMP(OS, get_restrictedType) + RAW_METHOD_DUMP(OS, get_isReturnValue) + RAW_METHOD_DUMP(OS, get_isSafeBuffers) + RAW_METHOD_DUMP(OS, get_scoped) + RAW_METHOD_DUMP(OS, get_isSdl) + RAW_METHOD_DUMP(OS, get_isSingleInheritance) + RAW_METHOD_DUMP(OS, get_isSplitted) + RAW_METHOD_DUMP(OS, get_isStatic) + RAW_METHOD_DUMP(OS, get_isStripped) + RAW_METHOD_DUMP(OS, get_unalignedType) + RAW_METHOD_DUMP(OS, get_notReached) + RAW_METHOD_DUMP(OS, get_isValueUdt) + RAW_METHOD_DUMP(OS, get_virtual) + RAW_METHOD_DUMP(OS, get_virtualBaseClass) + RAW_METHOD_DUMP(OS, get_isVirtualInheritance) + RAW_METHOD_DUMP(OS, get_volatileType) + RAW_METHOD_DUMP(OS, get_wasInlined) + RAW_METHOD_DUMP(OS, get_unused) + RAW_METHOD_DUMP(OS, get_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)) + return nullptr; + + return llvm::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 llvm::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 llvm::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 llvm::make_unique<DIAEnumSymbols>(Session, 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()); +} + +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); +} + +uint32_t 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); +} + +uint32_t 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); +} + +uint32_t 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); +} + +uint32_t 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); +} + +uint32_t 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); +} + +uint32_t 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); +} + +uint32_t DIARawSymbol::getStride() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_stride); +} + +uint32_t DIARawSymbol::getSubTypeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_subTypeId); +} + +std::string DIARawSymbol::getSymbolsFileName() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_symbolsFileName); +} + +uint32_t 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); +} + +uint32_t 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); +} + +uint32_t DIARawSymbol::getUnmodifiedTypeId() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_unmodifiedTypeId); +} + +uint32_t 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); +} + +uint32_t 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 = llvm::make_unique<DIARawSymbol>(Session, TableType); + auto Pointer = + llvm::make_unique<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); +} + +PDB_UniqueId 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/contrib/llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp new file mode 100644 index 000000000000..ef47b92b4f2f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp @@ -0,0 +1,303 @@ +//===- DIASession.cpp - DIA implementation of IPDBSession -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h" +#include "llvm/DebugInfo/PDB/DIA/DIAEnumSourceFiles.h" +#include "llvm/DebugInfo/PDB/DIA/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 make_error<GenericError>(generic_error_code::invalid_path, Context); + 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<GenericError>( + "DIA is only supported when using MSVC."); +#else + const wchar_t *msdia_dll = nullptr; +#if _MSC_VER >= 1900 && _MSC_VER < 2000 + msdia_dll = L"msdia140.dll"; // VS2015 +#elif _MSC_VER >= 1800 + msdia_dll = L"msdia120.dll"; // VS2013 +#else +#error "Unknown Visual Studio version." +#endif + + 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<GenericError>(generic_error_code::invalid_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<GenericError>(generic_error_code::invalid_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; +} + +void DIASession::setLoadAddress(uint64_t Address) { + Session->put_loadAddress(Address); +} + +std::unique_ptr<PDBSymbolExe> DIASession::getGlobalScope() const { + CComPtr<IDiaSymbol> GlobalScope; + if (S_OK != Session->get_globalScope(&GlobalScope)) + return nullptr; + + auto RawSymbol = llvm::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; +} + +std::unique_ptr<PDBSymbol> DIASession::getSymbolById(uint32_t SymbolId) const { + CComPtr<IDiaSymbol> LocatedSymbol; + if (S_OK != Session->symbolById(SymbolId, &LocatedSymbol)) + return nullptr; + + auto RawSymbol = llvm::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 = llvm::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 llvm::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)) + return nullptr; + + return llvm::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 llvm::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 llvm::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 llvm::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 llvm::make_unique<DIASourceFile>(*this, LocatedFile); +} + +std::unique_ptr<IPDBEnumDataStreams> DIASession::getDebugStreams() const { + CComPtr<IDiaEnumDebugStreams> DiaEnumerator; + if (S_OK != Session->getEnumDebugStreams(&DiaEnumerator)) + return nullptr; + + return llvm::make_unique<DIAEnumDebugStreams>(DiaEnumerator); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp new file mode 100644 index 000000000000..8605f55b402c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp @@ -0,0 +1,74 @@ +//===- DIASourceFile.cpp - DIA implementation of IPDBSourceFile -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/DIA/DIASourceFile.h" +#include "llvm/ADT/ArrayRef.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/PDBSymbolCompiland.h" +#include "llvm/Support/ConvertUTF.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 { + CComBSTR FileName16; + HRESULT Result = SourceFile->get_fileName(&FileName16); + if (S_OK != Result) + return std::string(); + + std::string FileName8; + llvm::ArrayRef<char> FileNameBytes(reinterpret_cast<char *>(FileName16.m_str), + FileName16.ByteLength()); + llvm::convertUTF16ToUTF8String(FileNameBytes, FileName8); + return FileName8; +} + +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/contrib/llvm/lib/DebugInfo/PDB/GenericError.cpp b/contrib/llvm/lib/DebugInfo/PDB/GenericError.cpp new file mode 100644 index 000000000000..789f3b813170 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/GenericError.cpp @@ -0,0 +1,66 @@ +//===- Error.cpp - system_error extensions for PDB --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/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 GenericErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.pdb"; } + + std::string message(int Condition) const override { + switch (static_cast<generic_error_code>(Condition)) { + case generic_error_code::unspecified: + return "An unknown error has occurred."; + case generic_error_code::dia_sdk_not_present: + return "LLVM was not compiled with support for DIA. This usually means " + "that you are are not using MSVC, or your Visual Studio " + "installation " + "is corrupt."; + case generic_error_code::invalid_path: + return "Unable to load PDB. Make sure the file exists and is readable."; + } + llvm_unreachable("Unrecognized generic_error_code"); + } +}; +} // end anonymous namespace + +static ManagedStatic<GenericErrorCategory> Category; + +char GenericError::ID = 0; + +GenericError::GenericError(generic_error_code C) : GenericError(C, "") {} + +GenericError::GenericError(StringRef Context) + : GenericError(generic_error_code::unspecified, Context) {} + +GenericError::GenericError(generic_error_code C, StringRef Context) : Code(C) { + ErrMsg = "PDB Error: "; + std::error_code EC = convertToErrorCode(); + if (Code != generic_error_code::unspecified) + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void GenericError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +StringRef GenericError::getErrorMessage() const { return ErrMsg; } + +std::error_code GenericError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/IPDBSourceFile.cpp b/contrib/llvm/lib/DebugInfo/PDB/IPDBSourceFile.cpp new file mode 100644 index 000000000000..8cb1fbef51f4 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/IPDBSourceFile.cpp @@ -0,0 +1,35 @@ +//===- IPDBSourceFile.cpp - base interface for a PDB source file ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/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/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp new file mode 100644 index 000000000000..dabcc3447ee5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp @@ -0,0 +1,90 @@ +//===- DbiModuleDescriptor.cpp - PDB module information -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/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; +} + +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/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp new file mode 100644 index 000000000000..b28ec2ff33ac --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.cpp @@ -0,0 +1,210 @@ +//===- DbiModuleDescriptorBuilder.cpp - PDB Mod Info Creation ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" + +#include "llvm/ADT/ArrayRef.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/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/COFF.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace llvm { +template <> struct BinaryItemTraits<CVSymbol> { + static size_t length(const CVSymbol &Item) { return Item.RecordData.size(); } + + static ArrayRef<uint8_t> bytes(const CVSymbol &Item) { + return Item.RecordData; + } +}; +} + +static uint32_t calculateDiSymbolStreamSize(uint32_t SymbolByteSize, + uint32_t C13Size) { + uint32_t Size = sizeof(uint32_t); // Signature + Size += SymbolByteSize; // 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) { + Layout.Mod = ModIndex; +} + +DbiModuleDescriptorBuilder::~DbiModuleDescriptorBuilder() {} + +uint16_t DbiModuleDescriptorBuilder::getStreamIndex() const { + return Layout.ModDiStream; +} + +void DbiModuleDescriptorBuilder::setObjFileName(StringRef Name) { + ObjFileName = Name; +} + +void DbiModuleDescriptorBuilder::addSymbol(CVSymbol Symbol) { + Symbols.push_back(Symbol); + SymbolByteSize += Symbol.data().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)); +} + +template <typename T> struct Foo { + explicit Foo(T &&Answer) : Answer(Answer) {} + + T Answer; +}; + +template <typename T> Foo<T> makeFoo(T &&t) { return Foo<T>(std::move(t)); } + +void DbiModuleDescriptorBuilder::finalize() { + Layout.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 = 0; + Layout.SrcFileNameNI = 0; + + // This value includes both the signature field as well as the record bytes + // from the symbol stream. + Layout.SymBytes = SymbolByteSize + sizeof(uint32_t); +} + +Error DbiModuleDescriptorBuilder::finalizeMsfLayout() { + this->Layout.ModDiStream = kInvalidStreamIndex; + uint32_t C13Size = calculateC13DebugInfoSize(); + 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); + WritableBinaryStreamRef Ref(*NS); + BinaryStreamWriter SymbolWriter(Ref); + // Write the symbols. + if (auto EC = + SymbolWriter.writeInteger<uint32_t>(COFF::DEBUG_SECTION_MAGIC)) + return EC; + BinaryItemStream<CVSymbol> Records(llvm::support::endianness::little); + Records.setItems(Symbols); + BinaryStreamRef RecordsRef(Records); + if (auto EC = SymbolWriter.writeStreamRef(RecordsRef)) + return EC; + // 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::addC13Fragment( + std::unique_ptr<DebugLinesSubsection> Lines) { + DebugLinesSubsection &Frag = *Lines; + + // File Checksums have to come first, so push an empty entry on if this + // is the first. + if (C13Builders.empty()) + C13Builders.push_back(nullptr); + + this->LineInfo.push_back(std::move(Lines)); + C13Builders.push_back( + llvm::make_unique<DebugSubsectionRecordBuilder>(Frag.kind(), Frag)); +} + +void DbiModuleDescriptorBuilder::addC13Fragment( + std::unique_ptr<codeview::DebugInlineeLinesSubsection> Inlinees) { + DebugInlineeLinesSubsection &Frag = *Inlinees; + + // File Checksums have to come first, so push an empty entry on if this + // is the first. + if (C13Builders.empty()) + C13Builders.push_back(nullptr); + + this->Inlinees.push_back(std::move(Inlinees)); + C13Builders.push_back( + llvm::make_unique<DebugSubsectionRecordBuilder>(Frag.kind(), Frag)); +} + +void DbiModuleDescriptorBuilder::setC13FileChecksums( + std::unique_ptr<DebugChecksumsSubsection> Checksums) { + assert(!ChecksumInfo && "Can't have more than one checksum info!"); + + if (C13Builders.empty()) + C13Builders.push_back(nullptr); + + ChecksumInfo = std::move(Checksums); + C13Builders[0] = llvm::make_unique<DebugSubsectionRecordBuilder>( + ChecksumInfo->kind(), *ChecksumInfo); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp new file mode 100644 index 000000000000..434f775097e0 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp @@ -0,0 +1,273 @@ +//===- DbiModuleList.cpp - PDB module information list ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h" + +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/Error.h" + +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/contrib/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp new file mode 100644 index 000000000000..2f4fb6cc295d --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiStream.cpp @@ -0,0 +1,321 @@ +//===- DbiStream.cpp - PDB Dbi Stream (Stream 3) Access -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/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/InfoStream.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(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream) + : Pdb(File), Stream(std::move(Stream)), Header(nullptr) {} + +DbiStream::~DbiStream() = default; + +Error DbiStream::reload() { + 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."); + + BinaryStreamRef ModInfoSubstream; + BinaryStreamRef FileInfoSubstream; + if (auto EC = + Reader.readStreamRef(ModInfoSubstream, Header->ModiSubstreamSize)) + return EC; + + if (auto EC = Reader.readStreamRef(SecContrSubstream, + Header->SecContrSubstreamSize)) + return EC; + if (auto EC = Reader.readStreamRef(SecMapSubstream, Header->SectionMapSize)) + return EC; + if (auto EC = Reader.readStreamRef(FileInfoSubstream, Header->FileInfoSize)) + return EC; + if (auto EC = + Reader.readStreamRef(TypeServerMapSubstream, Header->TypeServerSize)) + return EC; + if (auto EC = Reader.readStreamRef(ECSubstream, Header->ECSubstreamSize)) + return EC; + if (auto EC = Reader.readArray( + DbgStreams, Header->OptionalDbgHdrSize / sizeof(ulittle16_t))) + return EC; + + if (auto EC = Modules.initialize(ModInfoSubstream, FileInfoSubstream)) + return EC; + + if (auto EC = initializeSectionContributionData()) + return EC; + if (auto EC = initializeSectionHeadersData()) + return EC; + if (auto EC = initializeSectionMapData()) + return EC; + if (auto EC = initializeFpoRecords()) + return EC; + + if (Reader.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Found unexpected bytes in DBI Stream."); + + if (ECSubstream.getLength() > 0) { + BinaryStreamReader ECReader(ECSubstream); + 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() { + return SectionHeaders; +} + +FixedStreamArray<object::FpoData> DbiStream::getFpoRecords() { + return FpoRecords; +} + +const DbiModuleList &DbiStream::modules() const { return Modules; } + +FixedStreamArray<SecMapEntry> DbiStream::getSectionMap() const { + return SectionMap; +} + +void DbiStream::visitSectionContributions( + ISectionContribVisitor &Visitor) const { + if (SectionContribVersion == DbiSecContribVer60) { + for (auto &SC : SectionContribs) + Visitor.visit(SC); + } else if (SectionContribVersion == DbiSecContribV2) { + for (auto &SC : SectionContribs2) + Visitor.visit(SC); + } +} + +Error DbiStream::initializeSectionContributionData() { + if (SecContrSubstream.getLength() == 0) + return Error::success(); + + BinaryStreamReader SCReader(SecContrSubstream); + 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() { + if (DbgStreams.size() == 0) + return Error::success(); + + uint32_t StreamNum = getDebugStreamIndex(DbgHeaderType::SectionHdr); + if (StreamNum >= Pdb.getNumStreams()) + return make_error<RawError>(raw_error_code::no_stream); + + auto SHS = MappedBlockStream::createIndexedStream( + Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum); + + 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::initializeFpoRecords() { + if (DbgStreams.size() == 0) + return Error::success(); + + uint32_t StreamNum = getDebugStreamIndex(DbgHeaderType::NewFPO); + + // This means there is no FPO data. + if (StreamNum == kInvalidStreamIndex) + return Error::success(); + + if (StreamNum >= Pdb.getNumStreams()) + return make_error<RawError>(raw_error_code::no_stream); + + auto FS = MappedBlockStream::createIndexedStream( + Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum); + + size_t StreamLen = FS->getLength(); + if (StreamLen % sizeof(object::FpoData)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupted New FPO stream."); + + size_t NumRecords = StreamLen / sizeof(object::FpoData); + BinaryStreamReader Reader(*FS); + if (auto EC = Reader.readArray(FpoRecords, NumRecords)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Corrupted New FPO stream."); + FpoStream = std::move(FS); + return Error::success(); +} + +Error DbiStream::initializeSectionMapData() { + if (SecMapSubstream.getLength() == 0) + return Error::success(); + + BinaryStreamReader SMReader(SecMapSubstream); + 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/contrib/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp new file mode 100644 index 000000000000..23c7456d7772 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -0,0 +1,409 @@ +//===- DbiStreamBuilder.cpp - PDB Dbi Stream Creation -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" + +#include "llvm/ADT/ArrayRef.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" +#include "llvm/Support/COFF.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), DbgStreams((int)DbgHeaderType::Max) {} + +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::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::setSectionContribs(ArrayRef<SectionContrib> Arr) { + SectionContribs = Arr; +} + +void DbiStreamBuilder::setSectionMap(ArrayRef<SecMapEntry> SecMap) { + SectionMap = SecMap; +} + +Error DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type, + ArrayRef<uint8_t> Data) { + if (DbgStreams[(int)Type].StreamNumber) + return make_error<RawError>(raw_error_code::duplicate_entry, + "The specified stream type already exists"); + auto ExpectedIndex = Msf.addStream(Data.size()); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + uint32_t Index = std::move(*ExpectedIndex); + DbgStreams[(int)Type].Data = Data; + DbgStreams[(int)Type].StreamNumber = Index; + return Error::success(); +} + +uint32_t DbiStreamBuilder::calculateSerializedLength() const { + // For now we only support serializing the header. + return sizeof(DbiStreamHeader) + calculateFileInfoSubstreamSize() + + calculateModiSubstreamSize() + calculateSectionContribsStreamSize() + + calculateSectionMapStreamSize() + calculateDbgStreamsSize(); +} + +Expected<DbiModuleDescriptorBuilder &> +DbiStreamBuilder::addModuleInfo(StringRef ModuleName) { + uint32_t Index = ModiList.size(); + auto MIB = + llvm::make_unique<DbiModuleDescriptorBuilder>(ModuleName, Index, Msf); + auto M = MIB.get(); + auto Result = ModiMap.insert(std::make_pair(ModuleName, std::move(MIB))); + + if (!Result.second) + return make_error<RawError>(raw_error_code::duplicate_entry, + "The specified module already exists"); + ModiList.push_back(M); + return *M; +} + +Error DbiStreamBuilder::addModuleSourceFile(StringRef Module, StringRef File) { + auto ModIter = ModiMap.find(Module); + if (ModIter == ModiMap.end()) + return make_error<RawError>(raw_error_code::no_entry, + "The specified module was not found"); + uint32_t Index = SourceFileNames.size(); + SourceFileNames.insert(std::make_pair(File, Index)); + auto &ModEntry = *ModIter; + ModEntry.second->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>(); + 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 = 0; + 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 = kInvalidStreamIndex; + H->PublicSymbolStreamIndex = kInvalidStreamIndex; + H->MFCTypeServerIndex = kInvalidStreamIndex; + H->GlobalSymbolStreamIndex = kInvalidStreamIndex; + + Header = H; + return Error::success(); +} + +Error DbiStreamBuilder::finalizeMsfLayout() { + 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 Section Contributions +// for a given input sections. +std::vector<SectionContrib> DbiStreamBuilder::createSectionContribs( + ArrayRef<object::coff_section> SecHdrs) { + std::vector<SectionContrib> Ret; + + // Create a SectionContrib for each input section. + for (auto &Sec : SecHdrs) { + Ret.emplace_back(); + auto &Entry = Ret.back(); + memset(&Entry, 0, sizeof(Entry)); + + Entry.Off = Sec.PointerToRawData; + Entry.Size = Sec.SizeOfRawData; + Entry.Characteristics = Sec.Characteristics; + } + 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); + + 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(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; + + for (auto &Stream : DbgStreams) + if (auto EC = Writer.writeInteger(Stream.StreamNumber)) + return EC; + + for (auto &Stream : DbgStreams) { + if (Stream.StreamNumber == kInvalidStreamIndex) + continue; + auto WritableStream = WritableMappedBlockStream::createIndexedStream( + Layout, MsfBuffer, Stream.StreamNumber); + BinaryStreamWriter DbgStreamWriter(*WritableStream); + if (auto EC = DbgStreamWriter.writeArray(Stream.Data)) + 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/contrib/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp new file mode 100644 index 000000000000..b3837dc72e5b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/EnumTables.cpp @@ -0,0 +1,38 @@ +//===- EnumTables.cpp - Enum to string conversion tables --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/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/contrib/llvm/lib/DebugInfo/PDB/Native/GSI.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/GSI.cpp new file mode 100644 index 000000000000..b219fe275f73 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/GSI.cpp @@ -0,0 +1,93 @@ +//===- GSI.cpp - Common Functions for GlobalsStream and PublicsStream ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GSI.h" + +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamReader.h" + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { + +static Error checkHashHdrVersion(const GSIHashHeader *HashHdr) { + if (HashHdr->VerHdr != GSIHashHeader::HdrVersion) + return make_error<RawError>( + raw_error_code::feature_unsupported, + "Encountered unsupported globals stream version."); + + return Error::success(); +} + +Error readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, + const GSIHashHeader *HashHdr, + BinaryStreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) + return EC; + + // Before the actual hash buckets, there is a bitmap of length determined by + // IPHR_HASH. + ArrayRef<uint8_t> Bitmap; + size_t BitmapSizeInBits = alignTo(IPHR_HASH + 1, 32); + uint32_t NumBitmapEntries = BitmapSizeInBits / 8; + if (auto EC = Reader.readBytes(Bitmap, NumBitmapEntries)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read a bitmap.")); + uint32_t NumBuckets = 0; + for (uint8_t B : Bitmap) + NumBuckets += countPopulation(B); + + // Hash buckets follow. + if (auto EC = Reader.readArray(HashBuckets, NumBuckets)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Hash buckets corrupted.")); + + return Error::success(); +} + +Error readGSIHashHeader(const GSIHashHeader *&HashHdr, + BinaryStreamReader &Reader) { + if (Reader.readObject(HashHdr)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Stream does not contain a GSIHashHeader."); + + if (HashHdr->VerSignature != GSIHashHeader::HdrSignature) + return make_error<RawError>( + raw_error_code::feature_unsupported, + "GSIHashHeader signature (0xffffffff) not found."); + + return Error::success(); +} + +Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, + const GSIHashHeader *HashHdr, + BinaryStreamReader &Reader) { + if (auto EC = checkHashHdrVersion(HashHdr)) + return EC; + + // HashHdr->HrSize specifies the number of bytes of PSHashRecords we have. + // Verify that we can read them all. + if (HashHdr->HrSize % sizeof(PSHashRecord)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid HR array size."); + uint32_t NumHashRecords = HashHdr->HrSize / sizeof(PSHashRecord); + if (auto EC = Reader.readArray(HashRecords, NumHashRecords)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Error reading hash records.")); + + return Error::success(); +} +} +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/GSI.h b/contrib/llvm/lib/DebugInfo/PDB/Native/GSI.h new file mode 100644 index 000000000000..9e63bc83548f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/GSI.h @@ -0,0 +1,68 @@ +//===- GSI.h - Common Declarations for GlobalsStream and PublicsStream ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The data structures defined in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +// - ppdb1->m_fMinimalDbgInfo seems to be always true. +// - SMALLBUCKETS macro is defined. +// +// The reference doesn't compile, so I learned just by reading code. +// It's not guaranteed to be correct. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H +#define LLVM_LIB_DEBUGINFO_PDB_RAW_GSI_H + +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamArray.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +class BinaryStreamReader; + +namespace pdb { + +/// From https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.cpp +static const unsigned IPHR_HASH = 4096; + +/// Header of the hash tables found in the globals and publics sections. +/// Based on GSIHashHeader in +/// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +struct GSIHashHeader { + enum : unsigned { + HdrSignature = ~0U, + HdrVersion = 0xeffe0000 + 19990810, + }; + support::ulittle32_t VerSignature; + support::ulittle32_t VerHdr; + support::ulittle32_t HrSize; + support::ulittle32_t NumBuckets; +}; + +Error readGSIHashBuckets(FixedStreamArray<support::ulittle32_t> &HashBuckets, + const GSIHashHeader *HashHdr, + BinaryStreamReader &Reader); +Error readGSIHashHeader(const GSIHashHeader *&HashHdr, + BinaryStreamReader &Reader); +Error readGSIHashRecords(FixedStreamArray<PSHashRecord> &HashRecords, + const GSIHashHeader *HashHdr, + BinaryStreamReader &Reader); +} +} + +#endif diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp new file mode 100644 index 000000000000..a2ee0f047c58 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/GlobalsStream.cpp @@ -0,0 +1,42 @@ +//===- GlobalsStream.cpp - PDB Index of Symbols by Name ---- ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "GSI.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); + + const GSIHashHeader *HashHdr; + if (auto EC = readGSIHashHeader(HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashBuckets(HashBuckets, HashHdr, Reader)) + return EC; + NumBuckets = HashBuckets.size(); + + return Error::success(); +} + +Error GlobalsStream::commit() { return Error::success(); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/Hash.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/Hash.cpp new file mode 100644 index 000000000000..2ad3f55dc5c3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/Hash.cpp @@ -0,0 +1,86 @@ +//===- Hash.cpp - PDB Hash Functions --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/Hash.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/JamCRC.h" + +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(makeArrayRef<char>(reinterpret_cast<const char *>(Buf.data()), + Buf.size())); + return JC.getCRC(); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp new file mode 100644 index 000000000000..ebf8c9c04db1 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/HashTable.cpp @@ -0,0 +1,302 @@ +//===- HashTable.cpp - PDB Hash Table ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/HashTable.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" + +#include <assert.h> + +using namespace llvm; +using namespace llvm::pdb; + +HashTable::HashTable() : HashTable(8) {} + +HashTable::HashTable(uint32_t Capacity) { Buckets.resize(Capacity); } + +Error HashTable::load(BinaryStreamReader &Stream) { + const Header *H; + if (auto EC = Stream.readObject(H)) + return EC; + if (H->Capacity == 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid Hash Table Capacity"); + if (H->Size > maxLoad(H->Capacity)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid Hash Table Size"); + + Buckets.resize(H->Capacity); + + if (auto EC = readSparseBitVector(Stream, Present)) + return EC; + if (Present.count() != H->Size) + return make_error<RawError>(raw_error_code::corrupt_file, + "Present bit vector does not match size!"); + + if (auto EC = readSparseBitVector(Stream, Deleted)) + return EC; + if (Present.intersects(Deleted)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Present bit vector interesects deleted!"); + + for (uint32_t P : Present) { + if (auto EC = Stream.readInteger(Buckets[P].first)) + return EC; + if (auto EC = Stream.readInteger(Buckets[P].second)) + return EC; + } + + return Error::success(); +} + +uint32_t HashTable::calculateSerializedLength() const { + uint32_t Size = sizeof(Header); + + int NumBitsP = Present.find_last() + 1; + int NumBitsD = Deleted.find_last() + 1; + + // Present bit set number of words, followed by that many actual words. + Size += sizeof(uint32_t); + Size += alignTo(NumBitsP, sizeof(uint32_t)); + + // Deleted bit set number of words, followed by that many actual words. + Size += sizeof(uint32_t); + Size += alignTo(NumBitsD, sizeof(uint32_t)); + + // One (Key, Value) pair for each entry Present. + Size += 2 * sizeof(uint32_t) * size(); + + return Size; +} + +Error HashTable::commit(BinaryStreamWriter &Writer) const { + Header H; + H.Size = size(); + H.Capacity = capacity(); + if (auto EC = Writer.writeObject(H)) + return EC; + + if (auto EC = writeSparseBitVector(Writer, Present)) + return EC; + + if (auto EC = writeSparseBitVector(Writer, Deleted)) + return EC; + + for (const auto &Entry : *this) { + if (auto EC = Writer.writeInteger(Entry.first)) + return EC; + if (auto EC = Writer.writeInteger(Entry.second)) + return EC; + } + return Error::success(); +} + +void HashTable::clear() { + Buckets.resize(8); + Present.clear(); + Deleted.clear(); +} + +uint32_t HashTable::capacity() const { return Buckets.size(); } +uint32_t HashTable::size() const { return Present.count(); } + +HashTableIterator HashTable::begin() const { return HashTableIterator(*this); } +HashTableIterator HashTable::end() const { + return HashTableIterator(*this, 0, true); +} + +HashTableIterator HashTable::find(uint32_t K) { + uint32_t H = K % capacity(); + uint32_t I = H; + Optional<uint32_t> FirstUnused; + do { + if (isPresent(I)) { + if (Buckets[I].first == K) + return HashTableIterator(*this, I, false); + } else { + if (!FirstUnused) + FirstUnused = I; + // Insertion occurs via linear probing from the slot hint, and will be + // inserted at the first empty / deleted location. Therefore, if we are + // probing and find a location that is neither present nor deleted, then + // nothing must have EVER been inserted at this location, and thus it is + // not possible for a matching value to occur later. + if (!isDeleted(I)) + break; + } + I = (I + 1) % capacity(); + } while (I != H); + + // The only way FirstUnused would not be set is if every single entry in the + // table were Present. But this would violate the load factor constraints + // that we impose, so it should never happen. + assert(FirstUnused); + return HashTableIterator(*this, *FirstUnused, true); +} + +void HashTable::set(uint32_t K, uint32_t V) { + auto Entry = find(K); + if (Entry != end()) { + assert(isPresent(Entry.index())); + assert(Buckets[Entry.index()].first == K); + // We're updating, no need to do anything special. + Buckets[Entry.index()].second = V; + return; + } + + auto &B = Buckets[Entry.index()]; + assert(!isPresent(Entry.index())); + assert(Entry.isEnd()); + B.first = K; + B.second = V; + Present.set(Entry.index()); + Deleted.reset(Entry.index()); + + grow(); + + assert(find(K) != end()); +} + +void HashTable::remove(uint32_t K) { + auto Iter = find(K); + // It wasn't here to begin with, just exit. + if (Iter == end()) + return; + + assert(Present.test(Iter.index())); + assert(!Deleted.test(Iter.index())); + Deleted.set(Iter.index()); + Present.reset(Iter.index()); +} + +uint32_t HashTable::get(uint32_t K) { + auto I = find(K); + assert(I != end()); + return (*I).second; +} + +uint32_t HashTable::maxLoad(uint32_t capacity) { return capacity * 2 / 3 + 1; } + +void HashTable::grow() { + uint32_t S = size(); + if (S < maxLoad(capacity())) + return; + assert(capacity() != UINT32_MAX && "Can't grow Hash table!"); + + uint32_t NewCapacity = + (capacity() <= INT32_MAX) ? capacity() * 2 : UINT32_MAX; + + // Growing requires rebuilding the table and re-hashing every item. Make a + // copy with a larger capacity, insert everything into the copy, then swap + // it in. + HashTable NewMap(NewCapacity); + for (auto I : Present) { + NewMap.set(Buckets[I].first, Buckets[I].second); + } + + Buckets.swap(NewMap.Buckets); + std::swap(Present, NewMap.Present); + std::swap(Deleted, NewMap.Deleted); + assert(capacity() == NewCapacity); + assert(size() == S); +} + +Error HashTable::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 HashTable::writeSparseBitVector(BinaryStreamWriter &Writer, + SparseBitVector<> &Vec) { + int ReqBits = Vec.find_last() + 1; + uint32_t NumWords = alignTo(ReqBits, sizeof(uint32_t)) / sizeof(uint32_t); + if (auto EC = Writer.writeInteger(NumWords)) + 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 != NumWords; ++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(); +} + +HashTableIterator::HashTableIterator(const HashTable &Map, uint32_t Index, + bool IsEnd) + : Map(&Map), Index(Index), IsEnd(IsEnd) {} + +HashTableIterator::HashTableIterator(const HashTable &Map) : Map(&Map) { + int I = Map.Present.find_first(); + if (I == -1) { + Index = 0; + IsEnd = true; + } else { + Index = static_cast<uint32_t>(I); + IsEnd = false; + } +} + +HashTableIterator &HashTableIterator::operator=(const HashTableIterator &R) { + Map = R.Map; + return *this; +} + +bool HashTableIterator::operator==(const HashTableIterator &R) const { + if (IsEnd && R.IsEnd) + return true; + if (IsEnd != R.IsEnd) + return false; + + return (Map == R.Map) && (Index == R.Index); +} + +const std::pair<uint32_t, uint32_t> &HashTableIterator::operator*() const { + assert(Map->Present.test(Index)); + return Map->Buckets[Index]; +} + +HashTableIterator &HashTableIterator::operator++() { + while (Index < Map->Buckets.size()) { + ++Index; + if (Map->Present.test(Index)) + return *this; + } + + IsEnd = true; + return *this; +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp new file mode 100644 index 000000000000..2a1d12e82390 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -0,0 +1,126 @@ +//===- InfoStream.cpp - PDB Info Stream (Stream 1) Access -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamWriter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +InfoStream::InfoStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +Error InfoStream::reload() { + BinaryStreamReader Reader(*Stream); + + const InfoStreamHeader *H; + if (auto EC = Reader.readObject(H)) + return joinErrors( + std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "PDB Stream does not contain a header.")); + + switch (H->Version) { + case PdbImplVC70: + case PdbImplVC80: + case PdbImplVC110: + case PdbImplVC140: + break; + default: + return make_error<RawError>(raw_error_code::corrupt_file, + "Unsupported PDB stream version."); + } + + Version = H->Version; + Signature = H->Signature; + Age = H->Age; + Guid = H->Guid; + + uint32_t Offset = Reader.getOffset(); + if (auto EC = NamedStreams.load(Reader)) + return EC; + uint32_t NewOffset = Reader.getOffset(); + NamedStreamMapByteSize = NewOffset - Offset; + + 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; + default: + continue; + } + FeatureSignatures.push_back(Sig); + } + return Error::success(); +} + +uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); } + +uint32_t InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { + uint32_t Result; + if (!NamedStreams.get(Name, Result)) + return 0; + return Result; +} + +iterator_range<StringMapConstIterator<uint32_t>> +InfoStream::named_streams() const { + return NamedStreams.entries(); +} + +PdbRaw_ImplVer InfoStream::getVersion() const { + return static_cast<PdbRaw_ImplVer>(Version); +} + +uint32_t InfoStream::getSignature() const { return Signature; } + +uint32_t InfoStream::getAge() const { return Age; } + +PDB_UniqueId InfoStream::getGuid() const { return 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; +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp new file mode 100644 index 000000000000..f019d410328a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp @@ -0,0 +1,74 @@ +//===- InfoStreamBuilder.cpp - PDB Info Stream Creation ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/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), Sig(-1), Age(0), + NamedStreams(NamedStreams) {} + +void InfoStreamBuilder::setVersion(PdbRaw_ImplVer V) { Ver = V; } + +void InfoStreamBuilder::setSignature(uint32_t S) { Sig = S; } + +void InfoStreamBuilder::setAge(uint32_t A) { Age = A; } + +void InfoStreamBuilder::setGuid(PDB_UniqueId G) { Guid = G; } + +void InfoStreamBuilder::addFeature(PdbRaw_FeatureSig Sig) { + Features.push_back(Sig); +} + +Error InfoStreamBuilder::finalizeMsfLayout() { + uint32_t Length = sizeof(InfoStreamHeader) + NamedStreams.finalize() + + (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); + BinaryStreamWriter Writer(*InfoS); + + InfoStreamHeader H; + H.Age = Age; + H.Signature = Sig; + H.Version = Ver; + H.Guid = Guid; + 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; + } + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp new file mode 100644 index 000000000000..d7a203746a0d --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp @@ -0,0 +1,89 @@ +//===- ModuleDebugStream.cpp - PDB Module Info Stream Access --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.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/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); + + 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; + if (auto EC = Reader.readArray(SymbolsSubstream, SymbolSize - 4)) + return EC; + + if (auto EC = Reader.readStreamRef(C11LinesSubstream, C11Size)) + return EC; + if (auto EC = Reader.readStreamRef(C13LinesSubstream, C13Size)) + return EC; + + BinaryStreamReader LineReader(C13LinesSubstream); + if (auto EC = + LineReader.readArray(LinesAndChecksums, LineReader.bytesRemaining())) + return EC; + + uint32_t GlobalRefsSize; + if (auto EC = Reader.readInteger(GlobalRefsSize)) + return EC; + if (auto EC = Reader.readStreamRef(GlobalRefsSubstream, GlobalRefsSize)) + return EC; + if (Reader.bytesRemaining() > 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Unexpected bytes in module stream."); + + return Error::success(); +} + +iterator_range<codeview::CVSymbolArray::Iterator> +ModuleDebugStreamRef::symbols(bool *HadError) const { + return make_range(SymbolsSubstream.begin(HadError), SymbolsSubstream.end()); +} + +llvm::iterator_range<ModuleDebugStreamRef::LinesAndChecksumsIterator> +ModuleDebugStreamRef::linesAndChecksums() const { + return make_range(LinesAndChecksums.begin(), LinesAndChecksums.end()); +} + +bool ModuleDebugStreamRef::hasLineInfo() const { + return C13LinesSubstream.getLength() > 0; +} + +Error ModuleDebugStreamRef::commit() { return Error::success(); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStreamBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStreamBuilder.cpp new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/ModuleDebugStreamBuilder.cpp diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp new file mode 100644 index 000000000000..c7ba32b82bc6 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp @@ -0,0 +1,135 @@ +//===- NamedStreamMap.cpp - PDB Named Stream Map ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" + +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::pdb; + +NamedStreamMap::NamedStreamMap() = default; + +Error NamedStreamMap::load(BinaryStreamReader &Stream) { + Mapping.clear(); + FinalizedHashTable.clear(); + FinalizedInfo.reset(); + + 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")); + + BinaryStreamRef StringsBuffer; + if (auto EC = Stream.readStreamRef(StringsBuffer, StringBufferSize)) + return EC; + + HashTable OffsetIndexMap; + if (auto EC = OffsetIndexMap.load(Stream)) + return EC; + + uint32_t NameOffset; + uint32_t NameIndex; + for (const auto &Entry : OffsetIndexMap) { + std::tie(NameOffset, NameIndex) = Entry; + + // Compute the offset of the start of the string relative to the stream. + BinaryStreamReader NameReader(StringsBuffer); + NameReader.setOffset(NameOffset); + // Pump out our c-string from the stream. + StringRef Str; + if (auto EC = NameReader.readCString(Str)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map name")); + + // Add this to a string-map from name to stream number. + Mapping.insert({Str, NameIndex}); + } + + return Error::success(); +} + +Error NamedStreamMap::commit(BinaryStreamWriter &Writer) const { + assert(FinalizedInfo.hasValue()); + + // The first field is the number of bytes of string data. + if (auto EC = Writer.writeInteger(FinalizedInfo->StringDataBytes)) + return EC; + + // Now all of the string data itself. + for (const auto &Item : Mapping) { + if (auto EC = Writer.writeCString(Item.getKey())) + return EC; + } + + // And finally the Offset Index map. + if (auto EC = FinalizedHashTable.commit(Writer)) + return EC; + + return Error::success(); +} + +uint32_t NamedStreamMap::finalize() { + if (FinalizedInfo.hasValue()) + return FinalizedInfo->SerializedLength; + + // Build the finalized hash table. + FinalizedHashTable.clear(); + FinalizedInfo.emplace(); + for (const auto &Item : Mapping) { + FinalizedHashTable.set(FinalizedInfo->StringDataBytes, Item.getValue()); + FinalizedInfo->StringDataBytes += Item.getKeyLength() + 1; + } + + // Number of bytes of string data. + FinalizedInfo->SerializedLength += sizeof(support::ulittle32_t); + // Followed by that many actual bytes of string data. + FinalizedInfo->SerializedLength += FinalizedInfo->StringDataBytes; + // Followed by the mapping from Offset to Index. + FinalizedInfo->SerializedLength += + FinalizedHashTable.calculateSerializedLength(); + return FinalizedInfo->SerializedLength; +} + +iterator_range<StringMapConstIterator<uint32_t>> +NamedStreamMap::entries() const { + return make_range<StringMapConstIterator<uint32_t>>(Mapping.begin(), + Mapping.end()); +} + +uint32_t NamedStreamMap::size() const { return Mapping.size(); } + +bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { + auto Iter = Mapping.find(Stream); + if (Iter == Mapping.end()) + return false; + StreamNo = Iter->second; + return true; +} + +void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) { + FinalizedInfo.reset(); + Mapping[Stream] = StreamNo; +} + +void NamedStreamMap::remove(StringRef Stream) { + FinalizedInfo.reset(); + Mapping.erase(Stream); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp new file mode 100644 index 000000000000..77f832582f82 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeCompilandSymbol.cpp @@ -0,0 +1,43 @@ +//===- NativeCompilandSymbol.cpp - Native impl for compilands ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" + +namespace llvm { +namespace pdb { + +NativeCompilandSymbol::NativeCompilandSymbol(NativeSession &Session, + DbiModuleDescriptor MI) + : NativeRawSymbol(Session), Module(MI) {} + +PDB_SymType NativeCompilandSymbol::getSymTag() const { + return PDB_SymType::Compiland; +} + +bool NativeCompilandSymbol::isEditAndContinueEnabled() const { + return Module.hasECInfo(); +} + +uint32_t 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/contrib/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp new file mode 100644 index 000000000000..97319fd77d11 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeEnumModules.cpp @@ -0,0 +1,53 @@ +//==- NativeEnumModules.cpp - Native Symbol Enumerator impl ------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h" +#include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" + +namespace llvm { +namespace pdb { + +NativeEnumModules::NativeEnumModules(NativeSession &PDBSession, + const DbiModuleList &Modules, + uint32_t Index) + : Session(PDBSession), Modules(Modules), Index(Index) {} + +uint32_t NativeEnumModules::getChildCount() const { + return static_cast<uint32_t>(Modules.getModuleCount()); +} + +std::unique_ptr<PDBSymbol> +NativeEnumModules::getChildAtIndex(uint32_t Index) const { + if (Index >= Modules.getModuleCount()) + return nullptr; + return std::unique_ptr<PDBSymbol>(new PDBSymbolCompiland( + Session, std::unique_ptr<IPDBRawSymbol>(new NativeCompilandSymbol( + Session, Modules.getModuleDescriptor(Index))))); +} + +std::unique_ptr<PDBSymbol> NativeEnumModules::getNext() { + if (Index >= Modules.getModuleCount()) + return nullptr; + return getChildAtIndex(Index++); +} + +void NativeEnumModules::reset() { Index = 0; } + +NativeEnumModules *NativeEnumModules::clone() const { + return new NativeEnumModules(Session, Modules, Index); +} + +} +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp new file mode 100644 index 000000000000..bb52560be167 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp @@ -0,0 +1,79 @@ +//===- NativeExeSymbol.cpp - native impl for PDBSymbolExe -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" + +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumModules.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" + +namespace llvm { +namespace pdb { + +NativeExeSymbol::NativeExeSymbol(NativeSession &Session) + : NativeRawSymbol(Session), File(Session.getPDBFile()) {} + +std::unique_ptr<IPDBEnumSymbols> +NativeExeSymbol::findChildren(PDB_SymType Type) const { + switch (Type) { + case PDB_SymType::Compiland: { + auto Dbi = File.getPDBDbiStream(); + if (Dbi) { + const DbiModuleList &Modules = Dbi->modules(); + return std::unique_ptr<IPDBEnumSymbols>( + new NativeEnumModules(Session, Modules)); + } + consumeError(Dbi.takeError()); + break; + } + default: + break; + } + return nullptr; +} + +uint32_t NativeExeSymbol::getAge() const { + auto IS = File.getPDBInfoStream(); + if (IS) + return IS->getAge(); + consumeError(IS.takeError()); + return 0; +} + +std::string NativeExeSymbol::getSymbolsFileName() const { + return File.getFilePath(); +} + +PDB_UniqueId NativeExeSymbol::getGuid() const { + auto IS = File.getPDBInfoStream(); + if (IS) + return IS->getGuid(); + consumeError(IS.takeError()); + return PDB_UniqueId{{0}}; +} + +bool NativeExeSymbol::hasCTypes() const { + auto Dbi = File.getPDBDbiStream(); + if (Dbi) + return Dbi->hasCTypes(); + consumeError(Dbi.takeError()); + return false; +} + +bool NativeExeSymbol::hasPrivateSymbols() const { + auto Dbi = File.getPDBDbiStream(); + if (Dbi) + return !Dbi->isStripped(); + consumeError(Dbi.takeError()); + return false; +} + +} // namespace pdb +} // namespace llvm diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp new file mode 100644 index 000000000000..70968d4330b0 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp @@ -0,0 +1,707 @@ +//===- NativeRawSymbol.cpp - Native implementation of IPDBRawSymbol -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.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; + +NativeRawSymbol::NativeRawSymbol(NativeSession &PDBSession) + : Session(PDBSession) {} + +void NativeRawSymbol::dump(raw_ostream &OS, int Indent) const {} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildren(PDB_SymType Type) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildren(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findChildrenByRVA(PDB_SymType Type, StringRef Name, + PDB_NameSearchFlags Flags, uint32_t RVA) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSymbols> +NativeRawSymbol::findInlineFramesByRVA(uint32_t RVA) const { + return nullptr; +} + +void NativeRawSymbol::getDataBytes(llvm::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; +} + +uint32_t 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; +} + +uint32_t 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; +} + +uint32_t NativeRawSymbol::getClassParentId() const { + return 0; +} + +std::string NativeRawSymbol::getCompilerName() const { + return 0; +} + +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; +} + +uint32_t 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; +} + +uint32_t NativeRawSymbol::getLowerBoundId() const { + return 0; +} + +uint32_t NativeRawSymbol::getMemorySpaceKind() const { + return 0; +} + +std::string NativeRawSymbol::getName() const { + return 0; +} + +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; +} + +uint32_t 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 0; +} + +uint32_t NativeRawSymbol::getStride() const { + return 0; +} + +uint32_t NativeRawSymbol::getSubTypeId() const { + return 0; +} + +std::string NativeRawSymbol::getSymbolsFileName() const { return ""; } + +uint32_t NativeRawSymbol::getSymIndexId() const { + return 0; +} + +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; +} + +uint32_t NativeRawSymbol::getTypeId() const { + return 0; +} + +uint32_t NativeRawSymbol::getUavSlot() const { + return 0; +} + +std::string NativeRawSymbol::getUndecoratedName() const { + return 0; +} + +uint32_t NativeRawSymbol::getUnmodifiedTypeId() const { + return 0; +} + +uint32_t 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; +} + +uint32_t 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 PDB_SymType::None; +} + +PDB_UniqueId NativeRawSymbol::getGuid() const { + return PDB_UniqueId{{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/contrib/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp new file mode 100644 index 000000000000..7e6843bceb7d --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -0,0 +1,146 @@ +//===- NativeSession.cpp - Native implementation of IPDBSession -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.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 <memory> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +NativeSession::NativeSession(std::unique_ptr<PDBFile> PdbFile, + std::unique_ptr<BumpPtrAllocator> Allocator) + : Pdb(std::move(PdbFile)), Allocator(std::move(Allocator)) {} + +NativeSession::~NativeSession() = default; + +Error NativeSession::createFromPdb(StringRef Path, + std::unique_ptr<IPDBSession> &Session) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return make_error<GenericError>(generic_error_code::invalid_path); + + std::unique_ptr<MemoryBuffer> Buffer = std::move(*ErrorOrBuffer); + auto Stream = llvm::make_unique<MemoryBufferByteStream>( + std::move(Buffer), llvm::support::little); + + auto Allocator = llvm::make_unique<BumpPtrAllocator>(); + auto File = llvm::make_unique<PDBFile>(Path, std::move(Stream), *Allocator); + if (auto EC = File->parseFileHeaders()) + return EC; + if (auto EC = File->parseStreamData()) + return EC; + + Session = + llvm::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; } + +void NativeSession::setLoadAddress(uint64_t Address) {} + +std::unique_ptr<PDBSymbolExe> NativeSession::getGlobalScope() const { + auto RawSymbol = + llvm::make_unique<NativeExeSymbol>(const_cast<NativeSession &>(*this)); + auto PdbSymbol(PDBSymbol::create(*this, std::move(RawSymbol))); + std::unique_ptr<PDBSymbolExe> ExeSymbol( + static_cast<PDBSymbolExe *>(PdbSymbol.release())); + return ExeSymbol; +} + +std::unique_ptr<PDBSymbol> +NativeSession::getSymbolById(uint32_t SymbolId) const { + return nullptr; +} + +std::unique_ptr<PDBSymbol> +NativeSession::findSymbolByAddress(uint64_t Address, 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<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; +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp new file mode 100644 index 000000000000..859295d2c7d3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -0,0 +1,411 @@ +//===- PDBFile.cpp - Low level interface to a PDB file ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/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/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::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, + "Does not contain superblock"); + } + + 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); + BinaryStreamReader FpmReader(*FpmStream); + ArrayRef<uint8_t> FpmBytes; + if (auto EC = FpmReader.readBytes(FpmBytes, + msf::getFullFpmByteSize(ContainerLayout))) + 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); + 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; +} + +Expected<GlobalsStream &> PDBFile::getPDBGlobalsStream() { + if (!Globals) { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto GlobalS = safelyCreateIndexedStream( + ContainerLayout, *Buffer, DbiS->getGlobalSymbolStreamIndex()); + if (!GlobalS) + return GlobalS.takeError(); + auto TempGlobals = llvm::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(ContainerLayout, *Buffer, StreamPDB); + if (!InfoS) + return InfoS.takeError(); + auto TempInfo = llvm::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(ContainerLayout, *Buffer, StreamDBI); + if (!DbiS) + return DbiS.takeError(); + auto TempDbi = llvm::make_unique<DbiStream>(*this, std::move(*DbiS)); + if (auto EC = TempDbi->reload()) + return std::move(EC); + Dbi = std::move(TempDbi); + } + return *Dbi; +} + +Expected<TpiStream &> PDBFile::getPDBTpiStream() { + if (!Tpi) { + auto TpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamTPI); + if (!TpiS) + return TpiS.takeError(); + auto TempTpi = llvm::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) { + auto IpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamIPI); + if (!IpiS) + return IpiS.takeError(); + auto TempIpi = llvm::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( + ContainerLayout, *Buffer, DbiS->getPublicSymbolStreamIndex()); + if (!PublicS) + return PublicS.takeError(); + auto TempPublics = + llvm::make_unique<PublicsStream>(*this, 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(ContainerLayout, *Buffer, SymbolStreamNum); + if (!SymbolS) + return SymbolS.takeError(); + + auto TempSymbols = llvm::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 IS = getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names"); + + auto NS = + safelyCreateIndexedStream(ContainerLayout, *Buffer, NameStreamIndex); + if (!NS) + return NS.takeError(); + + auto N = llvm::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; +} + +bool PDBFile::hasPDBDbiStream() const { return StreamDBI < getNumStreams(); } + +bool PDBFile::hasPDBGlobalsStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return false; + return DbiS->getGlobalSymbolStreamIndex() < getNumStreams(); +} + +bool PDBFile::hasPDBInfoStream() { return StreamPDB < getNumStreams(); } + +bool PDBFile::hasPDBIpiStream() const { return StreamIPI < getNumStreams(); } + +bool PDBFile::hasPDBPublicsStream() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + 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; + return IS->getNamedStreamIndex("/names") < getNumStreams(); +} + +/// 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(const MSFLayout &Layout, + BinaryStreamRef MsfData, + uint32_t StreamIndex) const { + if (StreamIndex >= getNumStreams()) + return make_error<RawError>(raw_error_code::no_stream); + return MappedBlockStream::createIndexedStream(Layout, MsfData, StreamIndex); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp new file mode 100644 index 000000000000..c6568029ec55 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -0,0 +1,188 @@ +//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/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" + +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) {} + +Error PDBFileBuilder::initialize(uint32_t BlockSize) { + auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize); + if (!ExpectedMsf) + return ExpectedMsf.takeError(); + Msf = llvm::make_unique<MSFBuilder>(std::move(*ExpectedMsf)); + return Error::success(); +} + +MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } + +InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { + if (!Info) + Info = llvm::make_unique<InfoStreamBuilder>(*Msf, NamedStreams); + return *Info; +} + +DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { + if (!Dbi) + Dbi = llvm::make_unique<DbiStreamBuilder>(*Msf); + return *Dbi; +} + +TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() { + if (!Tpi) + Tpi = llvm::make_unique<TpiStreamBuilder>(*Msf, StreamTPI); + return *Tpi; +} + +TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() { + if (!Ipi) + Ipi = llvm::make_unique<TpiStreamBuilder>(*Msf, StreamIPI); + return *Ipi; +} + +PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() { + return Strings; +} + +Error PDBFileBuilder::addNamedStream(StringRef Name, uint32_t Size) { + auto ExpectedStream = Msf->addStream(Size); + if (!ExpectedStream) + return ExpectedStream.takeError(); + NamedStreams.set(Name, *ExpectedStream); + return Error::success(); +} + +Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() { + uint32_t StringsLen = Strings.calculateSerializedSize(); + + if (auto EC = addNamedStream("/names", StringsLen)) + return std::move(EC); + if (auto EC = addNamedStream("/LinkInfo", 0)) + return std::move(EC); + if (auto EC = addNamedStream("/src/headerblock", 0)) + return std::move(EC); + + if (Info) { + if (auto EC = Info->finalizeMsfLayout()) + return std::move(EC); + } + if (Dbi) { + if (auto EC = Dbi->finalizeMsfLayout()) + return std::move(EC); + } + if (Tpi) { + if (auto EC = Tpi->finalizeMsfLayout()) + return std::move(EC); + } + if (Ipi) { + if (auto EC = Ipi->finalizeMsfLayout()) + return std::move(EC); + } + + return Msf->build(); +} + +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; +} + +Error PDBFileBuilder::commit(StringRef Filename) { + assert(!Filename.empty()); + auto ExpectedLayout = finalizeMsfLayout(); + if (!ExpectedLayout) + return ExpectedLayout.takeError(); + auto &Layout = *ExpectedLayout; + + uint64_t Filesize = Layout.SB->BlockSize * Layout.SB->NumBlocks; + auto OutFileOrError = FileOutputBuffer::create(Filename, Filesize); + if (OutFileOrError.getError()) + return llvm::make_error<pdb::GenericError>(generic_error_code::invalid_path, + Filename); + FileBufferByteStream Buffer(std::move(*OutFileOrError), + llvm::support::little); + BinaryStreamWriter Writer(Buffer); + + if (auto EC = Writer.writeObject(*Layout.SB)) + return EC; + uint32_t BlockMapOffset = + msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize); + Writer.setOffset(BlockMapOffset); + if (auto EC = Writer.writeArray(Layout.DirectoryBlocks)) + return EC; + + auto DirStream = + WritableMappedBlockStream::createDirectoryStream(Layout, Buffer); + BinaryStreamWriter DW(*DirStream); + if (auto EC = DW.writeInteger<uint32_t>(Layout.StreamSizes.size())) + return EC; + + if (auto EC = DW.writeArray(Layout.StreamSizes)) + return EC; + + for (const auto &Blocks : Layout.StreamMap) { + if (auto EC = DW.writeArray(Blocks)) + return EC; + } + + auto ExpectedSN = getNamedStreamIndex("/names"); + if (!ExpectedSN) + return ExpectedSN.takeError(); + + auto NS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, + *ExpectedSN); + BinaryStreamWriter NSWriter(*NS); + if (auto EC = Strings.commit(NSWriter)) + 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; + } + + return Buffer.commit(); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp new file mode 100644 index 000000000000..e84573fe07b8 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBStringTable.cpp @@ -0,0 +1,134 @@ +//===- PDBStringTable.cpp - PDB String Table ---------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#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 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(); +} + +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; + + uint32_t ID = IDs[Index]; + 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/contrib/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp new file mode 100644 index 000000000000..a472181a4895 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp @@ -0,0 +1,133 @@ +//===- PDBStringTableBuilder.cpp - PDB String Table -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::support::endian; +using namespace llvm::pdb; + +uint32_t PDBStringTableBuilder::insert(StringRef S) { + return Strings.insert(S); +} + +static uint32_t computeBucketCount(uint32_t NumStrings) { + // The /names stream is basically an on-disk open-addressing hash table. + // Hash collisions are resolved by linear probing. We cannot make + // utilization 100% because it will make the linear probing extremely + // slow. But lower utilization wastes disk space. As a reasonable + // load factor, we choose 80%. We need +1 because slot 0 is reserved. + return (NumStrings + 1) * 1.25; +} + +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; +} + +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 (Slot == 0) + continue; // Skip reserved slot + 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/contrib/llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp new file mode 100644 index 000000000000..9fd90102f72c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp @@ -0,0 +1,126 @@ +//===- PDBTypeServerHandler.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Handles CodeView LF_TYPESERVER2 records by attempting to locate a matching +// PDB file, then loading the PDB file and visiting all types from the +// referenced PDB using the original supplied visitor. +// +// The net effect of this is that when visiting a PDB containing a TypeServer +// record, the TypeServer record is "replaced" with all of the records in +// the referenced PDB file. If a single instance of PDBTypeServerHandler +// encounters the same TypeServer multiple times (for example reusing one +// PDBTypeServerHandler across multiple visitations of distinct object files or +// PDB files), PDBTypeServerHandler will optionally revisit all the records +// again, or simply consume the record and do nothing. +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static void ignoreErrors(Error EC) { + llvm::handleAllErrors(std::move(EC), [&](ErrorInfoBase &EIB) {}); +} + +PDBTypeServerHandler::PDBTypeServerHandler(bool RevisitAlways) + : RevisitAlways(RevisitAlways) {} + +void PDBTypeServerHandler::addSearchPath(StringRef Path) { + if (Path.empty() || !sys::fs::is_directory(Path)) + return; + + SearchPaths.insert(Path); +} + +Expected<bool> +PDBTypeServerHandler::handleInternal(PDBFile &File, + TypeVisitorCallbacks &Callbacks) { + auto ExpectedTpi = File.getPDBTpiStream(); + if (!ExpectedTpi) + return ExpectedTpi.takeError(); + + // For handling a type server, we should be using whatever the callback array + // was + // that is being used for the original file. We shouldn't allow the visitor + // to + // arbitrarily stick a deserializer in there. + if (auto EC = codeview::visitTypeStream(ExpectedTpi->typeArray(), Callbacks, + VDS_BytesExternal)) + return std::move(EC); + + return true; +} + +Expected<bool> PDBTypeServerHandler::handle(TypeServer2Record &TS, + TypeVisitorCallbacks &Callbacks) { + if (Session) { + // If we've already handled this TypeServer and we only want to handle each + // TypeServer once, consume the record without doing anything. + if (!RevisitAlways) + return true; + + return handleInternal(Session->getPDBFile(), Callbacks); + } + + StringRef File = sys::path::filename(TS.Name); + if (File.empty()) + return make_error<CodeViewError>( + cv_error_code::corrupt_record, + "TypeServer2Record does not contain filename!"); + + for (auto &Path : SearchPaths) { + SmallString<64> PathStr = Path.getKey(); + sys::path::append(PathStr, File); + if (!sys::fs::exists(PathStr)) + continue; + + std::unique_ptr<IPDBSession> ThisSession; + if (auto EC = loadDataForPDB(PDB_ReaderType::Native, PathStr, ThisSession)) { + // It is not an error if this PDB fails to load, it just means that it + // doesn't match and we should continue searching. + ignoreErrors(std::move(EC)); + continue; + } + + std::unique_ptr<NativeSession> NS( + static_cast<NativeSession *>(ThisSession.release())); + PDBFile &File = NS->getPDBFile(); + auto ExpectedInfo = File.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!ExpectedInfo) + return ExpectedInfo.takeError(); + + // Just because a file with a matching name was found and it was an actual + // PDB file doesn't mean it matches. For it to match the InfoStream's GUID + // must match the GUID specified in the TypeServer2 record. + ArrayRef<uint8_t> GuidBytes(ExpectedInfo->getGuid().Guid); + StringRef GuidStr(reinterpret_cast<const char *>(GuidBytes.begin()), + GuidBytes.size()); + if (GuidStr != TS.Guid) + continue; + + Session = std::move(NS); + return handleInternal(File, Callbacks); + } + + // We couldn't find a matching PDB, so let it be handled by someone else. + return false; +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp new file mode 100644 index 000000000000..58202577672a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp @@ -0,0 +1,131 @@ +//===- PublicsStream.cpp - PDB Public Symbol Stream -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The data structures defined in this file are based on the reference +// implementation which is available at +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +// +// When you are reading the reference source code, you'd find the +// information below useful. +// +// - ppdb1->m_fMinimalDbgInfo seems to be always true. +// - SMALLBUCKETS macro is defined. +// +// The reference doesn't compile, so I learned just by reading code. +// It's not guaranteed to be correct. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "GSI.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <algorithm> +#include <cstdint> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +// This is PSGSIHDR struct defined in +// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/dbi/gsi.h +struct PublicsStream::HeaderInfo { + ulittle32_t SymHash; + ulittle32_t AddrMap; + ulittle32_t NumThunks; + ulittle32_t SizeOfThunk; + ulittle16_t ISectThunkTable; + char Padding[2]; + ulittle32_t OffThunkTable; + ulittle32_t NumSections; +}; + +PublicsStream::PublicsStream(PDBFile &File, + std::unique_ptr<MappedBlockStream> Stream) + : Pdb(File), Stream(std::move(Stream)) {} + +PublicsStream::~PublicsStream() = default; + +uint32_t PublicsStream::getSymHash() const { return Header->SymHash; } +uint32_t PublicsStream::getAddrMap() const { return Header->AddrMap; } + +// 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(HeaderInfo) + sizeof(GSIHashHeader)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + // Read PSGSIHDR and GSIHashHdr structs. + if (Reader.readObject(Header)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + if (auto EC = readGSIHashHeader(HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashRecords(HashRecords, HashHdr, Reader)) + return EC; + + if (auto EC = readGSIHashBuckets(HashBuckets, HashHdr, Reader)) + return EC; + NumBuckets = HashBuckets.size(); + + // 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 (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(); +} + +iterator_range<codeview::CVSymbolArray::Iterator> +PublicsStream::getSymbols(bool *HadError) const { + auto SymbolS = Pdb.getPDBSymbolStream(); + if (SymbolS.takeError()) { + codeview::CVSymbolArray::Iterator Iter; + return make_range(Iter, Iter); + } + SymbolStream &SS = SymbolS.get(); + + return SS.getSymbols(HadError); +} + +Error PublicsStream::commit() { return Error::success(); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/RawError.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/RawError.cpp new file mode 100644 index 000000000000..548289fff3df --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/RawError.cpp @@ -0,0 +1,75 @@ +#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"); + } +}; +} // end anonymous namespace + +static ManagedStatic<RawErrorCategory> Category; + +char RawError::ID = 0; + +RawError::RawError(raw_error_code C) : RawError(C, "") {} + +RawError::RawError(const std::string &Context) + : RawError(raw_error_code::unspecified, Context) {} + +RawError::RawError(raw_error_code C, const std::string &Context) : Code(C) { + ErrMsg = "Native PDB Error: "; + std::error_code EC = convertToErrorCode(); + if (Code != raw_error_code::unspecified) + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void RawError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +const std::string &RawError::getErrorMessage() const { return ErrMsg; } + +std::error_code RawError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp new file mode 100644 index 000000000000..9e9ebd11495b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp @@ -0,0 +1,45 @@ +//===- SymbolStream.cpp - PDB Symbol Stream Access ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +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(); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp new file mode 100644 index 000000000000..16904a5a27ed --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -0,0 +1,110 @@ +//===- TpiHashing.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/PDB/Native/TpiHashing.h" + +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// Corresponds to `fUDTAnon`. +template <typename T> static bool isAnonymous(T &Rec) { + StringRef Name = Rec.getName(); + return Name == "<unnamed-tag>" || Name == "__unnamed" || + Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed"); +} + +// Computes a hash for a given TPI record. +template <typename T> +static uint32_t getTpiHash(T &Rec, ArrayRef<uint8_t> FullRecord) { + auto Opts = static_cast<uint16_t>(Rec.getOptions()); + + bool ForwardRef = + Opts & static_cast<uint16_t>(ClassOptions::ForwardReference); + bool Scoped = Opts & static_cast<uint16_t>(ClassOptions::Scoped); + bool UniqueName = Opts & static_cast<uint16_t>(ClassOptions::HasUniqueName); + bool IsAnon = UniqueName && isAnonymous(Rec); + + if (!ForwardRef && !Scoped && !IsAnon) + return hashStringV1(Rec.getName()); + if (!ForwardRef && UniqueName && !IsAnon) + return hashStringV1(Rec.getUniqueName()); + return hashBufferV8(FullRecord); +} + +template <typename T> static uint32_t getSourceLineHash(T &Rec) { + char Buf[4]; + support::endian::write32le(Buf, Rec.getUDT().getIndex()); + return hashStringV1(StringRef(Buf, 4)); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, + UdtSourceLineRecord &Rec) { + CVR.Hash = getSourceLineHash(Rec); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, + UdtModSourceLineRecord &Rec) { + CVR.Hash = getSourceLineHash(Rec); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, ClassRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.data()); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, EnumRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.data()); +} + +void TpiHashUpdater::visitKnownRecordImpl(CVType &CVR, UnionRecord &Rec) { + CVR.Hash = getTpiHash(Rec, CVR.data()); +} + +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, UdtSourceLineRecord &Rec) { + return verifySourceLine(Rec.getUDT()); +} + +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &Rec) { + return verifySourceLine(Rec.getUDT()); +} + +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, ClassRecord &Rec) { + if (getTpiHash(Rec, CVR.data()) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, EnumRecord &Rec) { + if (getTpiHash(Rec, CVR.data()) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} +Error TpiHashVerifier::visitKnownRecord(CVType &CVR, UnionRecord &Rec) { + if (getTpiHash(Rec, CVR.data()) % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} + +Error TpiHashVerifier::verifySourceLine(codeview::TypeIndex TI) { + char Buf[4]; + support::endian::write32le(Buf, TI.getIndex()); + uint32_t Hash = hashStringV1(StringRef(Buf, 4)); + if (Hash % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); +} + +Error TpiHashVerifier::visitTypeBegin(CVType &Rec) { + ++Index; + RawRecord = Rec; + return Error::success(); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp new file mode 100644 index 000000000000..623afb371b50 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -0,0 +1,152 @@ +//===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.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(const 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.readArray(TypeRecords, Header->TypeRecordBytes)) + return EC; + + // Hash indices, hash values, etc come from the hash stream. + if (Header->HashStreamIndex != kInvalidStreamIndex) { + if (Header->HashStreamIndex >= Pdb.getNumStreams()) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid TPI hash stream index."); + + auto HS = MappedBlockStream::createIndexedStream( + Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex); + 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 = llvm::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; } + +FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const { + return HashValues; +} + +FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const { + return TypeIndexOffsets; +} + +HashTable &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/contrib/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp b/contrib/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp new file mode 100644 index 000000000000..20456cc97823 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -0,0 +1,177 @@ +//===- TpiStreamBuilder.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/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/DebugInfo/PDB/Native/TpiStream.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 = MinTpiHashBuckets; + + // 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] % MinTpiHashBuckets; + } + ArrayRef<uint8_t> Bytes( + reinterpret_cast<const uint8_t *>(HashBuffer.data()), + calculateHashBufferSize()); + HashValueStream = + llvm::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); + + BinaryStreamWriter Writer(*InfoS); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + for (auto Rec : TypeRecords) + if (auto EC = Writer.writeBytes(Rec)) + return EC; + + if (HashStreamIndex != kInvalidStreamIndex) { + auto HVS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, + HashStreamIndex); + 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/contrib/llvm/lib/DebugInfo/PDB/PDB.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDB.cpp new file mode 100644 index 000000000000..7e3acc1165f3 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDB.cpp @@ -0,0 +1,51 @@ +//===- PDB.cpp - base header file for creating a PDB reader -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDB.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#if LLVM_ENABLE_DIA_SDK +#include "llvm/DebugInfo/PDB/DIA/DIASession.h" +#endif +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.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) + return NativeSession::createFromPdb(Path, Session); + +#if LLVM_ENABLE_DIA_SDK + return DIASession::createFromPdb(Path, Session); +#else + return llvm::make_error<GenericError>("DIA is not installed on the system"); +#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 llvm::make_error<GenericError>("DIA is not installed on the system"); +#endif +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBContext.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBContext.cpp new file mode 100644 index 000000000000..94b81ecf561e --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBContext.cpp @@ -0,0 +1,120 @@ +//===-- PDBContext.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/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/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.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, DIDumpType DumpType, bool DumpEH, + bool SummarizeTypes) {} + +DILineInfo PDBContext::getLineInfoForAddress(uint64_t Address, + DILineInfoSpecifier Specifier) { + DILineInfo Result; + Result.FunctionName = getFunctionName(Address, Specifier.FNKind); + + uint32_t Length = 1; + std::unique_ptr<PDBSymbol> Symbol = + Session->findSymbolByAddress(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, 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(uint64_t Address, uint64_t Size, + DILineInfoSpecifier Specifier) { + if (Size == 0) + return DILineInfoTable(); + + DILineInfoTable Table; + auto LineNumbers = Session->findLineNumbersByAddress(Address, Size); + if (!LineNumbers || LineNumbers->getChildCount() == 0) + return Table; + + while (auto LineInfo = LineNumbers->getNext()) { + DILineInfo LineEntry = + getLineInfoForAddress(LineInfo->getVirtualAddress(), Specifier); + Table.push_back(std::make_pair(LineInfo->getVirtualAddress(), LineEntry)); + } + return Table; +} + +DIInliningInfo +PDBContext::getInliningInfoForAddress(uint64_t Address, + DILineInfoSpecifier Specifier) { + DIInliningInfo InlineInfo; + DILineInfo Frame = getLineInfoForAddress(Address, Specifier); + InlineInfo.addFrame(Frame); + return InlineInfo; +} + +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/contrib/llvm/lib/DebugInfo/PDB/PDBExtras.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBExtras.cpp new file mode 100644 index 000000000000..dc22a30facab --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBExtras.cpp @@ -0,0 +1,363 @@ +//===- PDBExtras.cpp - helper functions and classes for PDBs -----*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBExtras.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/Formatters.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_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 codeview::RegisterId &Reg) { + switch (Reg) { + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, AL, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CL, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DL, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BL, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, AH, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CH, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DH, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BH, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, AX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, SP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, BP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, SI, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DI, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EAX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ECX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EDX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EBX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ESP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EBP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ESI, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, EDI, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, ES, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, CS, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, SS, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, DS, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, FS, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, GS, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, IP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RAX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RBX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RCX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RDX, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RSI, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RDI, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RBP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, RSP, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R8, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R9, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R10, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R11, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R12, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R13, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R14, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(codeview::RegisterId, R15, OS) + default: + OS << static_cast<int>(Reg); + } + 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) + 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) + } + 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) + } + 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) + default: + OS << "Unknown"; + } + 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_UniqueId &Guid) { + codeview::detail::GuidAdapter A(Guid.Guid); + A.format(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::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.Double; + 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/contrib/llvm/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp new file mode 100644 index 000000000000..541fcda45177 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp @@ -0,0 +1,28 @@ +//===- PDBInterfaceAnchors.h - defines class anchor funcions ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// 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/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" + +using namespace llvm; +using namespace llvm::pdb; + +IPDBSession::~IPDBSession() = default; + +IPDBDataStream::~IPDBDataStream() = default; + +IPDBRawSymbol::~IPDBRawSymbol() = default; + +IPDBLineNumber::~IPDBLineNumber() = default; diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymDumper.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymDumper.cpp new file mode 100644 index 000000000000..2f819312e54e --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymDumper.cpp @@ -0,0 +1,147 @@ +//===- PDBSymDumper.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/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/contrib/llvm/lib/DebugInfo/PDB/PDBSymbol.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbol.cpp new file mode 100644 index 000000000000..74010c2dd7dd --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbol.cpp @@ -0,0 +1,174 @@ +//===- PDBSymbol.cpp - base class for user-facing symbol types --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/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, + std::unique_ptr<IPDBRawSymbol> Symbol) + : Session(PDBSession), RawSymbol(std::move(Symbol)) {} + +PDBSymbol::PDBSymbol(PDBSymbol &Symbol) + : Session(Symbol.Session), RawSymbol(std::move(Symbol.RawSymbol)) {} + +PDBSymbol::~PDBSymbol() = default; + +#define FACTORY_SYMTAG_CASE(Tag, Type) \ + case PDB_SymType::Tag: \ + return std::unique_ptr<PDBSymbol>(new Type(PDBSession, std::move(Symbol))); + +std::unique_ptr<PDBSymbol> +PDBSymbol::create(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) { + switch (Symbol->getSymTag()) { + 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::move(Symbol))); + } +} + +void PDBSymbol::defaultDump(raw_ostream &OS, int Indent) const { + RawSymbol->dump(OS, Indent); +} + +void PDBSymbol::dumpProperties() const { + outs() << "\n"; + defaultDump(outs(), 0); + outs().flush(); +} + +void PDBSymbol::dumpChildStats() const { + TagStats Stats; + getChildStats(Stats); + outs() << "\n"; + for (auto &Stat : Stats) { + outs() << Stat.first << ": " << Stat.second << "\n"; + } + outs().flush(); +} + +std::unique_ptr<PDBSymbol> PDBSymbol::clone() const { + return Session.getSymbolById(getSymIndexId()); +} + +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); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp new file mode 100644 index 000000000000..3648272e1d0e --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp @@ -0,0 +1,27 @@ +//===- PDBSymbolAnnotation.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/PDB/PDBSymbolAnnotation.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolAnnotation::PDBSymbolAnnotation(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Annotation); +} + +void PDBSymbolAnnotation::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolBlock.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolBlock.cpp new file mode 100644 index 000000000000..7385d3ba1489 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolBlock.cpp @@ -0,0 +1,26 @@ +//===- PDBSymbolBlock.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/PDB/PDBSymbolBlock.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolBlock::PDBSymbolBlock(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Block); +} + +void PDBSymbolBlock::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp new file mode 100644 index 000000000000..854cf42d1bae --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp @@ -0,0 +1,46 @@ +//===- PDBSymbolCompiland.cpp - compiland details --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolCompiland::PDBSymbolCompiland(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Compiland); +} + +void PDBSymbolCompiland::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +std::string PDBSymbolCompiland::getSourceFileName() const +{ + std::string Result = RawSymbol->getSourceFileName(); + if (!Result.empty()) + return Result; + auto Envs = findAllChildren<PDBSymbolCompilandEnv>(); + if (!Envs) + return std::string(); + while (auto Env = Envs->getNext()) { + std::string Var = Env->getName(); + if (Var != "src") + continue; + std::string Value = Env->getValue(); + return Value; + } + return std::string(); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp new file mode 100644 index 000000000000..e08450e0ad0c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolCompilandDetails.cpp - compiland details --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolCompilandDetails::PDBSymbolCompilandDetails( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::CompilandDetails); +} + +void PDBSymbolCompilandDetails::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp new file mode 100644 index 000000000000..2f1c43666ae5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp @@ -0,0 +1,36 @@ +//===- PDBSymbolCompilandEnv.cpp - compiland env variables ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h" + +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolCompilandEnv::PDBSymbolCompilandEnv( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::CompilandEnv); +} + +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/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCustom.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCustom.cpp new file mode 100644 index 000000000000..9ec20bb62d75 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolCustom.cpp @@ -0,0 +1,31 @@ +//===- PDBSymbolCustom.cpp - compiler-specific types ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolCustom.h" + +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolCustom::PDBSymbolCustom(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> CustomSymbol) + : PDBSymbol(PDBSession, std::move(CustomSymbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Custom); +} + +void PDBSymbolCustom::getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) { + RawSymbol->getDataBytes(bytes); +} + +void PDBSymbolCustom::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolData.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolData.cpp new file mode 100644 index 000000000000..60026689c6f1 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolData.cpp @@ -0,0 +1,26 @@ +//===- PDBSymbolData.cpp - PDB data (e.g. variable) accessors ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolData::PDBSymbolData(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> DataSymbol) + : PDBSymbol(PDBSession, std::move(DataSymbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Data); +} + +void PDBSymbolData::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp new file mode 100644 index 000000000000..7417167b61ad --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp @@ -0,0 +1,36 @@ +//===- PDBSymbolExe.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/PDB/PDBSymbolExe.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolExe::PDBSymbolExe(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Exe); +} + +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/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFunc.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFunc.cpp new file mode 100644 index 000000000000..0734a1f8314a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFunc.cpp @@ -0,0 +1,108 @@ +//===- PDBSymbolFunc.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/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/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.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(); } + + FunctionArgEnumerator *clone() const override { + return new FunctionArgEnumerator(Session, Func); + } + +private: + typedef std::vector<std::unique_ptr<PDBSymbolData>> ArgListType; + const IPDBSession &Session; + const PDBSymbolFunc &Func; + ArgListType Args; + ArgListType::const_iterator CurIter; +}; +} + +PDBSymbolFunc::PDBSymbolFunc(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Function); +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolData>> +PDBSymbolFunc::getArguments() const { + return llvm::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; +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp new file mode 100644 index 000000000000..482c95e3a850 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolFuncDebugEnd.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/PDB/PDBSymbolFuncDebugEnd.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolFuncDebugEnd::PDBSymbolFuncDebugEnd( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::FuncDebugEnd); +} + +void PDBSymbolFuncDebugEnd::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp new file mode 100644 index 000000000000..ae23c7619e2a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolFuncDebugStart.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/PDB/PDBSymbolFuncDebugStart.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolFuncDebugStart::PDBSymbolFuncDebugStart( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::FuncDebugStart); +} + +void PDBSymbolFuncDebugStart::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolLabel.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolLabel.cpp new file mode 100644 index 000000000000..a67a20d8e352 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolLabel.cpp @@ -0,0 +1,25 @@ +//===- PDBSymbolLabel.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/PDB/PDBSymbolLabel.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolLabel::PDBSymbolLabel(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Label); +} + +void PDBSymbolLabel::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp new file mode 100644 index 000000000000..87bb4044216b --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolPublicSymbol.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/PDB/PDBSymbolPublicSymbol.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolPublicSymbol::PDBSymbolPublicSymbol( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::PublicSymbol); +} + +void PDBSymbolPublicSymbol::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolThunk.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolThunk.cpp new file mode 100644 index 000000000000..b2648197f9cc --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolThunk.cpp @@ -0,0 +1,25 @@ +//===- PDBSymbolThunk.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/PDB/PDBSymbolThunk.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolThunk::PDBSymbolThunk(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Thunk); +} + +void PDBSymbolThunk::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp new file mode 100644 index 000000000000..a8054a42d866 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp @@ -0,0 +1,32 @@ +//===- PDBSymbolTypeArray.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/PDB/PDBSymbolTypeArray.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeArray::PDBSymbolTypeArray(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::ArrayType); +} + +void PDBSymbolTypeArray::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +void PDBSymbolTypeArray::dumpRight(PDBSymDumper &Dumper) const { + Dumper.dumpRight(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp new file mode 100644 index 000000000000..0ee18d471624 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolTypeBaseClass.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/PDB/PDBSymbolTypeBaseClass.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeBaseClass::PDBSymbolTypeBaseClass( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::BaseClass); +} + +void PDBSymbolTypeBaseClass::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp new file mode 100644 index 000000000000..0bf563af7df5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp @@ -0,0 +1,27 @@ +//===- PDBSymbolTypeBuiltin.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/PDB/PDBSymbolTypeBuiltin.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeBuiltin::PDBSymbolTypeBuiltin( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::BuiltinType); +} + +void PDBSymbolTypeBuiltin::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp new file mode 100644 index 000000000000..f617d8d0c2df --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolTypeCustom.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/PDB/PDBSymbolTypeCustom.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeCustom::PDBSymbolTypeCustom(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::CustomType); +} + +void PDBSymbolTypeCustom::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp new file mode 100644 index 000000000000..68ba87c1cdf8 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp @@ -0,0 +1,29 @@ +//===- PDBSymbolTypeDimension.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/PDB/PDBSymbolTypeDimension.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeDimension::PDBSymbolTypeDimension( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Dimension); +} + +void PDBSymbolTypeDimension::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp new file mode 100644 index 000000000000..2addea072c88 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolTypeEnum.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/PDB/PDBSymbolTypeEnum.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeEnum::PDBSymbolTypeEnum(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Enum); +} + +void PDBSymbolTypeEnum::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp new file mode 100644 index 000000000000..ec27985e91d1 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolTypeFriend.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/PDB/PDBSymbolTypeFriend.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeFriend::PDBSymbolTypeFriend(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Friend); +} + +void PDBSymbolTypeFriend::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp new file mode 100644 index 000000000000..4d5cd63f6857 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp @@ -0,0 +1,27 @@ +//===- PDBSymbolTypeFunctionArg.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/PDB/PDBSymbolTypeFunctionArg.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeFunctionArg::PDBSymbolTypeFunctionArg( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::FunctionArg); +} + +void PDBSymbolTypeFunctionArg::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp new file mode 100644 index 000000000000..473529d1b043 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp @@ -0,0 +1,86 @@ +//===- PDBSymbolTypeFunctionSig.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/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/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.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(); } + + MyType *clone() const override { + std::unique_ptr<ArgEnumeratorType> Clone(Enumerator->clone()); + return new FunctionArgEnumerator(Session, std::move(Clone)); + } + +private: + const IPDBSession &Session; + std::unique_ptr<ArgEnumeratorType> Enumerator; +}; +} + +PDBSymbolTypeFunctionSig::PDBSymbolTypeFunctionSig( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::FunctionSig); +} + +std::unique_ptr<IPDBEnumSymbols> +PDBSymbolTypeFunctionSig::getArguments() const { + return llvm::make_unique<FunctionArgEnumerator>(Session, *this); +} + +void PDBSymbolTypeFunctionSig::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +void PDBSymbolTypeFunctionSig::dumpRight(PDBSymDumper &Dumper) const { + Dumper.dumpRight(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp new file mode 100644 index 000000000000..86e0ec4f8565 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp @@ -0,0 +1,28 @@ +//===- PDBSymboTypelManaged.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/PDB/PDBSymbolTypeManaged.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeManaged::PDBSymbolTypeManaged( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::ManagedType); +} + +void PDBSymbolTypeManaged::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp new file mode 100644 index 000000000000..69819811d61f --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp @@ -0,0 +1,32 @@ +//===- PDBSymbolTypePointer.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/PDB/PDBSymbolTypePointer.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypePointer::PDBSymbolTypePointer( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::PointerType); +} + +void PDBSymbolTypePointer::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} + +void PDBSymbolTypePointer::dumpRight(PDBSymDumper &Dumper) const { + Dumper.dumpRight(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp new file mode 100644 index 000000000000..102b540e0fef --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp @@ -0,0 +1,27 @@ +//===- PDBSymbolTypeTypedef.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/PDB/PDBSymbolTypeTypedef.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeTypedef::PDBSymbolTypeTypedef( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::Typedef); +} + +void PDBSymbolTypeTypedef::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp new file mode 100644 index 000000000000..15dc15352165 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp @@ -0,0 +1,33 @@ +//===- PDBSymbolTypeUDT.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/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 "llvm/DebugInfo/PDB/UDTLayout.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeUDT::PDBSymbolTypeUDT(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::UDT); +} + +void PDBSymbolTypeUDT::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp new file mode 100644 index 000000000000..9a21855f57f0 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp @@ -0,0 +1,27 @@ +//===- PDBSymbolTypeVTable.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/PDB/PDBSymbolTypeVTable.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeVTable::PDBSymbolTypeVTable(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::VTable); +} + +void PDBSymbolTypeVTable::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp new file mode 100644 index 000000000000..a516a4d2c429 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolTypeVTableShape.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/PDB/PDBSymbolTypeVTableShape.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolTypeVTableShape::PDBSymbolTypeVTableShape( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::VTableShape); +} + +void PDBSymbolTypeVTableShape::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp new file mode 100644 index 000000000000..dbbea9c93e20 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp @@ -0,0 +1,24 @@ +//===- PDBSymbolUnknown.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/PDB/PDBSymbolUnknown.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolUnknown::PDBSymbolUnknown(const IPDBSession &PDBSession, + std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) {} + +void PDBSymbolUnknown::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } diff --git a/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp new file mode 100644 index 000000000000..020aec9e98a8 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp @@ -0,0 +1,28 @@ +//===- PDBSymbolUsingNamespace.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/PDB/PDBSymbolUsingNamespace.h" + +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +PDBSymbolUsingNamespace::PDBSymbolUsingNamespace( + const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) + : PDBSymbol(PDBSession, std::move(Symbol)) { + assert(RawSymbol->getSymTag() == PDB_SymType::UsingNamespace); +} + +void PDBSymbolUsingNamespace::dump(PDBSymDumper &Dumper) const { + Dumper.dump(*this); +} diff --git a/contrib/llvm/lib/DebugInfo/PDB/UDTLayout.cpp b/contrib/llvm/lib/DebugInfo/PDB/UDTLayout.cpp new file mode 100644 index 000000000000..aacefae80c3a --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/PDB/UDTLayout.cpp @@ -0,0 +1,298 @@ +//===- UDTLayout.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/PDB/UDTLayout.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.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 <utility> + +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 = llvm::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 *dyn_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(Child)); + } 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 = llvm::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 = + llvm::make_unique<VTableLayoutItem>(*this, std::move(VTables[0])); + + VTable = VTLayout.get(); + + addChildToLayout(std::move(VTLayout)); + } + + for (auto &Data : Members) { + auto DM = llvm::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 = llvm::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 = + llvm::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)); +}
\ No newline at end of file diff --git a/contrib/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/contrib/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp new file mode 100644 index 000000000000..c1e2536d6e20 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -0,0 +1,121 @@ +//===- lib/DebugInfo/Symbolize/DIPrinter.cpp ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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/raw_ostream.h" +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <string> + +namespace llvm { +namespace symbolize { + +// By default, DILineInfo contains "<invalid>" for function/filename it +// cannot fetch. We replace it to "??" to make our output closer to addr2line. +static const char kDILineInfoBadString[] = "<invalid>"; +static const char kBadString[] = "??"; + +// 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 == kDILineInfoBadString) + FunctionName = kBadString; + + StringRef Delimiter = PrintPretty ? " at " : "\n"; + StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; + OS << Prefix << FunctionName << Delimiter; + } + std::string Filename = Info.FileName; + if (Filename == kDILineInfoBadString) + Filename = kBadString; + if (!Verbose) { + OS << Filename << ":" << Info.Line << ":" << Info.Column << "\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 == kDILineInfoBadString) + Name = kBadString; + OS << Name << "\n"; + OS << Global.Start << " " << Global.Size << "\n"; + return *this; +} + +} // end namespace symbolize +} // end namespace llvm diff --git a/contrib/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/contrib/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp new file mode 100644 index 000000000000..f672680cb9ea --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp @@ -0,0 +1,271 @@ +//===- SymbolizableObjectFile.cpp -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of SymbolizableObjectFile class. +// +//===----------------------------------------------------------------------===// + +#include "SymbolizableObjectFile.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.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/COFF.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(object::ObjectFile *Obj, + std::unique_ptr<DIContext> DICtx) { + std::unique_ptr<SymbolizableObjectFile> res( + new SymbolizableObjectFile(Obj, std::move(DICtx))); + 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()) { + StringRef Name; + StringRef Data; + if (auto EC = Section->getName(Name)) + return EC; + if (Name == ".opd") { + if (auto EC = Section->getContents(Data)) + return EC; + OpdExtractor.reset(new DataExtractor(Data, 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; + } + return std::move(res); +} + +SymbolizableObjectFile::SymbolizableObjectFile(ObjectFile *Obj, + std::unique_ptr<DIContext> DICtx) + : Module(Obj), DebugInfoContext(std::move(DICtx)) {} + +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.insert(std::make_pair(SD, Export.Name)); + } + return std::error_code(); +} + +std::error_code SymbolizableObjectFile::addSymbol(const SymbolRef &Symbol, + uint64_t SymbolSize, + DataExtractor *OpdExtractor, + uint64_t OpdAddress) { + 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 (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; + uint32_t OpdOffset32 = OpdOffset; + if (OpdOffset == OpdOffset32 && + OpdExtractor->isValidOffsetForAddress(OpdOffset32)) + SymbolAddress = OpdExtractor->getAddress(&OpdOffset32); + } + 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.insert(std::make_pair(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 &SymbolMap = Type == SymbolRef::ST_Function ? Functions : Objects; + if (SymbolMap.empty()) + return false; + SymbolDesc SD = { Address, Address }; + auto SymbolIterator = SymbolMap.upper_bound(SD); + if (SymbolIterator == SymbolMap.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(uint64_t ModuleOffset, + FunctionNameKind FNKind, + bool UseSymbolTable) const { + DILineInfo LineInfo; + if (DebugInfoContext) { + 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, + FunctionName, Start, Size)) { + LineInfo.FunctionName = FunctionName; + } + } + return LineInfo; +} + +DIInliningInfo SymbolizableObjectFile::symbolizeInlinedCode( + uint64_t ModuleOffset, FunctionNameKind FNKind, bool UseSymbolTable) const { + DIInliningInfo InlinedContext; + + if (DebugInfoContext) + 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, + FunctionName, Start, Size)) { + InlinedContext.getMutableFrame(InlinedContext.getNumberOfFrames() - 1) + ->FunctionName = FunctionName; + } + } + + return InlinedContext; +} + +DIGlobal SymbolizableObjectFile::symbolizeData(uint64_t ModuleOffset) const { + DIGlobal Res; + getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Res.Name, Res.Start, + Res.Size); + return Res; +} diff --git a/contrib/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h b/contrib/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h new file mode 100644 index 000000000000..216cca8de4f5 --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h @@ -0,0 +1,90 @@ +//===- SymbolizableObjectFile.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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(object::ObjectFile *Obj, std::unique_ptr<DIContext> DICtx); + + DILineInfo symbolizeCode(uint64_t ModuleOffset, FunctionNameKind FNKind, + bool UseSymbolTable) const override; + DIInliningInfo symbolizeInlinedCode(uint64_t ModuleOffset, + FunctionNameKind FNKind, + bool UseSymbolTable) const override; + DIGlobal symbolizeData(uint64_t 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); + + object::ObjectFile *Module; + std::unique_ptr<DIContext> DebugInfoContext; + + 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; + + friend bool operator<(const SymbolDesc &s1, const SymbolDesc &s2) { + return s1.Addr < s2.Addr; + } + }; + std::map<SymbolDesc, StringRef> Functions; + std::map<SymbolDesc, StringRef> Objects; + + SymbolizableObjectFile(object::ObjectFile *Obj, + std::unique_ptr<DIContext> DICtx); +}; + +} // end namespace symbolize + +} // end namespace llvm + +#endif // LLVM_LIB_DEBUGINFO_SYMBOLIZE_SYMBOLIZABLEOBJECTFILE_H diff --git a/contrib/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/contrib/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp new file mode 100644 index 000000000000..a41a065a983c --- /dev/null +++ b/contrib/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -0,0 +1,506 @@ +//===-- LLVMSymbolize.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation for LLVM symbolization library. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/Symbolize/Symbolize.h" + +#include "SymbolizableObjectFile.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBContext.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Support/COFF.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 <cstdlib> +#include <cstring> + +#if defined(_MSC_VER) +#include <Windows.h> +#include <DbgHelp.h> +#pragma comment(lib, "dbghelp.lib") + +// Windows.h conflicts with our COFF header definitions. +#ifdef IMAGE_FILE_MACHINE_I386 +#undef IMAGE_FILE_MACHINE_I386 +#endif +#endif + +namespace llvm { +namespace symbolize { + +Expected<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, + uint64_t 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 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 += Info->getModulePreferredBase(); + + DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts.PrintFunctions, + Opts.UseSymbolTable); + if (Opts.Demangle) + LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info); + return LineInfo; +} + +Expected<DIInliningInfo> +LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, + uint64_t 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 += 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, + uint64_t 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 += Info->getModulePreferredBase(); + + DIGlobal Global = Info->symbolizeData(ModuleOffset); + if (Opts.Demangle) + Global.Name = DemangleName(Global.Name, Info); + return Global; +} + +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 !zlib::isAvailable() || CRCHash == zlib::crc32(MB.get()->getBuffer()); +} + +bool findDebugBinary(const std::string &OrigPath, + const std::string &DebuglinkName, uint32_t CRCHash, + std::string &Result) { + std::string OrigRealPath = OrigPath; +#if defined(HAVE_REALPATH) + if (char *RP = realpath(OrigPath.c_str(), nullptr)) { + OrigRealPath = RP; + free(RP); + } +#endif + SmallString<16> OrigDir(OrigRealPath); + llvm::sys::path::remove_filename(OrigDir); + SmallString<16> DebugPath = OrigDir; + // Try /path/to/original_binary/debuglink_name + llvm::sys::path::append(DebugPath, DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = DebugPath.str(); + return true; + } + // Try /path/to/original_binary/.debug/debuglink_name + DebugPath = OrigRealPath; + llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = DebugPath.str(); + return true; + } + // Try /usr/lib/debug/path/to/original_binary/debuglink_name + DebugPath = "/usr/lib/debug"; + 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; + Section.getName(Name); + Name = Name.substr(Name.find_first_not_of("._")); + if (Name == "gnu_debuglink") { + StringRef Data; + Section.getContents(Data); + DataExtractor DE(Data, Obj->isLittleEndian(), 0); + uint32_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, 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) { + const auto &I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectPairForPathArch.end()) { + return I->second; + } + + auto ObjOrErr = getOrCreateObject(Path, ArchName); + if (!ObjOrErr) { + ObjectPairForPathArch.insert(std::make_pair(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.insert( + std::make_pair(std::make_pair(Path, ArchName), Res)); + return Res; +} + +Expected<ObjectFile *> +LLVMSymbolizer::getOrCreateObject(const std::string &Path, + const std::string &ArchName) { + const auto &I = BinaryForPath.find(Path); + Binary *Bin = nullptr; + if (I == BinaryForPath.end()) { + Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path); + if (!BinOrErr) { + BinaryForPath.insert(std::make_pair(Path, OwningBinary<Binary>())); + return BinOrErr.takeError(); + } + Bin = BinOrErr->getBinary(); + BinaryForPath.insert(std::make_pair(Path, std::move(BinOrErr.get()))); + } else { + Bin = I->second.getBinary(); + } + + if (!Bin) + return static_cast<ObjectFile *>(nullptr); + + if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) { + const auto &I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectForUBPathAndArch.end()) { + return I->second.get(); + } + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = + UB->getObjectForArch(ArchName); + if (!ObjOrErr) { + ObjectForUBPathAndArch.insert(std::make_pair( + std::make_pair(Path, ArchName), std::unique_ptr<ObjectFile>())); + return ObjOrErr.takeError(); + } + ObjectFile *Res = ObjOrErr->get(); + ObjectForUBPathAndArch.insert(std::make_pair(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::getOrCreateModuleInfo(const std::string &ModuleName) { + const 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.insert( + std::make_pair(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()) { +#if 0 + using namespace pdb; + std::unique_ptr<IPDBSession> Session; + if (auto Err = loadDataForEXE(PDB_ReaderType::DIA, + Objects.first->getFileName(), Session)) { + Modules.insert( + std::make_pair(ModuleName, std::unique_ptr<SymbolizableModule>())); + return std::move(Err); + } + Context.reset(new PDBContext(*CoffObject, std::move(Session))); +#else + return make_error<StringError>( + "PDB support not compiled in", + std::make_error_code(std::errc::not_supported)); +#endif + } + } + if (!Context) + Context.reset(new DWARFContextInMemory(*Objects.second)); + assert(Context); + auto InfoOrErr = + SymbolizableObjectFile::create(Objects.first, std::move(Context)); + std::unique_ptr<SymbolizableModule> SymMod; + if (InfoOrErr) + SymMod = std::move(InfoOrErr.get()); + auto InsertResult = + Modules.insert(std::make_pair(ModuleName, std::move(SymMod))); + assert(InsertResult.second); + if (auto EC = InfoOrErr.getError()) + return errorCodeToError(EC); + return InsertResult.first->second.get(); +} + +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 + +#if !defined(_MSC_VER) +// Assume that __cxa_demangle is provided by libcxxabi (except for Windows). +extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer, + size_t *length, int *status); +#endif + +std::string +LLVMSymbolizer::DemangleName(const std::string &Name, + const SymbolizableModule *DbiModuleDescriptor) { +#if !defined(_MSC_VER) + // 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 = __cxa_demangle(Name.c_str(), nullptr, nullptr, &status); + if (status != 0) + return Name; + std::string Result = DemangledName; + free(DemangledName); + return Result; + } +#else + if (!Name.empty() && Name.front() == '?') { + // Only do MSVC C++ demangling on symbols starting with '?'. + char DemangledName[1024] = {0}; + DWORD result = ::UnDecorateSymbolName( + Name.c_str(), DemangledName, 1023, + UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected + UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc + UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications + UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers + UNDNAME_NO_MS_KEYWORDS | // Strip all MS extension keywords + UNDNAME_NO_FUNCTION_RETURNS); // Strip function return types + return (result == 0) ? Name : std::string(DemangledName); + } +#endif + if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) + return std::string(demanglePE32ExternCFunc(Name)); + return Name; +} + +} // namespace symbolize +} // namespace llvm |
