diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:41:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:41:05 +0000 |
commit | 01095a5d43bbfde13731688ddcf6048ebb8b7721 (patch) | |
tree | 4def12e759965de927d963ac65840d663ef9d1ea /lib/DebugInfo | |
parent | f0f4822ed4b66e3579e92a89f368f8fb860e218e (diff) | |
download | src-vendor/llvm/llvm-release_39-r276489.tar.gz src-vendor/llvm/llvm-release_39-r276489.zip |
Vendor import of llvm release_39 branch r276489:vendor/llvm/llvm-release_39-r276489
Diffstat (limited to 'lib/DebugInfo')
108 files changed, 7860 insertions, 674 deletions
diff --git a/lib/DebugInfo/CodeView/ByteStream.cpp b/lib/DebugInfo/CodeView/ByteStream.cpp new file mode 100644 index 000000000000..2c43bc6958d2 --- /dev/null +++ b/lib/DebugInfo/CodeView/ByteStream.cpp @@ -0,0 +1,79 @@ +//===- ByteStream.cpp - Reads stream data from a byte sequence ------------===// +// +// 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/ByteStream.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include <cstring> + +using namespace llvm; +using namespace llvm::codeview; + +static Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Src, + ArrayRef<uint8_t> Dest) { + return make_error<CodeViewError>(cv_error_code::operation_unsupported, + "ByteStream is immutable."); +} + +static Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Src, + MutableArrayRef<uint8_t> Dest) { + if (Dest.size() < Src.size()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + if (Offset > Src.size() - Dest.size()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + + ::memcpy(Dest.data() + Offset, Src.data(), Src.size()); + return Error::success(); +} + +template <bool Writable> +Error ByteStream<Writable>::readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const { + if (Offset > Data.size()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + if (Data.size() < Size + Offset) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + Buffer = Data.slice(Offset, Size); + return Error::success(); +} + +template <bool Writable> +Error ByteStream<Writable>::readLongestContiguousChunk( + uint32_t Offset, ArrayRef<uint8_t> &Buffer) const { + if (Offset >= Data.size()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + Buffer = Data.slice(Offset); + return Error::success(); +} + +template <bool Writable> +Error ByteStream<Writable>::writeBytes(uint32_t Offset, + ArrayRef<uint8_t> Buffer) const { + return ::writeBytes(Offset, Buffer, Data); +} + +template <bool Writable> uint32_t ByteStream<Writable>::getLength() const { + return Data.size(); +} + +template <bool Writable> Error ByteStream<Writable>::commit() const { + return Error::success(); +} + +template <bool Writable> StringRef ByteStream<Writable>::str() const { + const char *CharData = reinterpret_cast<const char *>(Data.data()); + return StringRef(CharData, Data.size()); +} + +namespace llvm { +namespace codeview { +template class ByteStream<true>; +template class ByteStream<false>; +} +} diff --git a/lib/DebugInfo/CodeView/CMakeLists.txt b/lib/DebugInfo/CodeView/CMakeLists.txt index cfa0e4d8b401..47297a9131ee 100644 --- a/lib/DebugInfo/CodeView/CMakeLists.txt +++ b/lib/DebugInfo/CodeView/CMakeLists.txt @@ -1,10 +1,23 @@ add_llvm_library(LLVMDebugInfoCodeView + ByteStream.cpp + CodeViewError.cpp + CVTypeVisitor.cpp + EnumTables.cpp FieldListRecordBuilder.cpp Line.cpp ListRecordBuilder.cpp MemoryTypeTableBuilder.cpp MethodListRecordBuilder.cpp + ModuleSubstream.cpp + ModuleSubstreamVisitor.cpp + RecordSerialization.cpp + StreamReader.cpp + StreamWriter.cpp + SymbolDumper.cpp + TypeDumper.cpp + TypeRecord.cpp TypeRecordBuilder.cpp + TypeStreamMerger.cpp TypeTableBuilder.cpp ADDITIONAL_HEADER_DIRS diff --git a/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/lib/DebugInfo/CodeView/CVTypeVisitor.cpp new file mode 100644 index 000000000000..09f72214c52b --- /dev/null +++ b/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -0,0 +1,123 @@ +//===- 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" + +using namespace llvm; +using namespace llvm::codeview; + +template <typename T> +static Error takeObject(ArrayRef<uint8_t> &Data, const T *&Res) { + if (Data.size() < sizeof(*Res)) + return llvm::make_error<CodeViewError>(cv_error_code::insufficient_buffer); + Res = reinterpret_cast<const T *>(Data.data()); + Data = Data.drop_front(sizeof(*Res)); + return Error::success(); +} + +CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks) + : Callbacks(Callbacks) {} + +Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) { + ArrayRef<uint8_t> LeafData = Record.Data; + if (auto EC = Callbacks.visitTypeBegin(Record)) + return EC; + switch (Record.Type) { + default: + if (auto EC = Callbacks.visitUnknownType(Record)) + return EC; + break; + case LF_FIELDLIST: + if (auto EC = Callbacks.visitFieldListBegin(Record)) + return EC; + if (auto EC = visitFieldList(Record)) + return EC; + if (auto EC = Callbacks.visitFieldListEnd(Record)) + return EC; + break; +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \ + auto Result = Name##Record::deserialize(RK, LeafData); \ + if (Result.getError()) \ + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); \ + if (auto EC = Callbacks.visit##Name(*Result)) \ + return EC; \ + break; \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + TYPE_RECORD(EnumVal, EnumVal, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + } + if (auto EC = Callbacks.visitTypeEnd(Record)) + return EC; + return Error::success(); +} + +/// Visits the type records in Data. Sets the error flag on parse failures. +Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) { + for (const auto &I : Types) { + if (auto EC = visitTypeRecord(I)) + return EC; + } + return Error::success(); +} + +Error CVTypeVisitor::skipPadding(ArrayRef<uint8_t> &Data) { + if (Data.empty()) + return Error::success(); + uint8_t Leaf = Data.front(); + 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; + if (Data.size() < BytesToAdvance) { + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid padding bytes!"); + } + Data = Data.drop_front(BytesToAdvance); + return Error::success(); +} + +/// Visits individual member records of a field list record. Member records do +/// not describe their own length, and need special handling. +Error CVTypeVisitor::visitFieldList(const CVRecord<TypeLeafKind> &Record) { + ArrayRef<uint8_t> RecordData = Record.Data; + while (!RecordData.empty()) { + const ulittle16_t *LeafPtr; + if (auto EC = takeObject(RecordData, LeafPtr)) + return EC; + TypeLeafKind Leaf = TypeLeafKind(unsigned(*LeafPtr)); + switch (Leaf) { + default: + // Field list records do not describe their own length, so we cannot + // continue parsing past an unknown member type. + if (auto EC = Callbacks.visitUnknownMember(Record)) + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \ + auto Result = Name##Record::deserialize(RK, RecordData); \ + if (Result.getError()) \ + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); \ + if (auto EC = Callbacks.visit##Name(*Result)) \ + return EC; \ + break; \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + MEMBER_RECORD(EnumVal, EnumVal, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + } + if (auto EC = skipPadding(RecordData)) + return EC; + } + return Error::success(); +} diff --git a/lib/DebugInfo/CodeView/CodeViewError.cpp b/lib/DebugInfo/CodeView/CodeViewError.cpp new file mode 100644 index 000000000000..aad1d8b25cd0 --- /dev/null +++ b/lib/DebugInfo/CodeView/CodeViewError.cpp @@ -0,0 +1,67 @@ +//===- 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 LLVM_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::operation_unsupported: + return "The requested operation is not supported."; + } + 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/lib/DebugInfo/CodeView/EnumTables.cpp b/lib/DebugInfo/CodeView/EnumTables.cpp new file mode 100644 index 000000000000..d59271b2367e --- /dev/null +++ b/lib/DebugInfo/CodeView/EnumTables.cpp @@ -0,0 +1,375 @@ +//===- 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<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(ModuleSubstreamKind, None), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, Symbols), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, Lines), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, StringTable), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, FileChecksums), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, FrameData), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, InlineeLines), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeImports), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeExports), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, ILLines), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, FuncMDTokenMap), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, TypeMDTokenMap), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, MergedAssemblyInput), + CV_ENUM_CLASS_ENT(ModuleSubstreamKind, 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<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/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp b/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp index 91b71cc4b119..5f229e3d9f94 100644 --- a/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp +++ b/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp @@ -15,151 +15,118 @@ using namespace codeview; FieldListRecordBuilder::FieldListRecordBuilder() : ListRecordBuilder(TypeRecordKind::FieldList) {} -void FieldListRecordBuilder::writeBaseClass(MemberAccess Access, TypeIndex Type, - uint64_t Offset) { +void FieldListRecordBuilder::writeBaseClass(const BaseClassRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); Builder.writeTypeRecordKind(TypeRecordKind::BaseClass); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeEncodedUnsignedInteger(Offset); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getBaseType()); + Builder.writeEncodedUnsignedInteger(Record.getBaseOffset()); finishSubRecord(); } -void FieldListRecordBuilder::writeEnumerate(MemberAccess Access, uint64_t Value, - StringRef Name) { +void FieldListRecordBuilder::writeEnumerator(const EnumeratorRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::Enumerate); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeEncodedUnsignedInteger(Value); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeRecordKind(TypeRecordKind::Enumerator); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + // FIXME: Handle full APInt such as __int128. + Builder.writeEncodedUnsignedInteger(Record.getValue().getZExtValue()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeMember(MemberAccess Access, TypeIndex Type, - uint64_t Offset, StringRef Name) { +void FieldListRecordBuilder::writeDataMember(const DataMemberRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::Member); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeEncodedUnsignedInteger(Offset); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeRecordKind(Record.getKind()); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getType()); + Builder.writeEncodedUnsignedInteger(Record.getFieldOffset()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeMethod(uint16_t OverloadCount, - TypeIndex MethodList, StringRef Name) { +void FieldListRecordBuilder::writeOverloadedMethod( + const OverloadedMethodRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::Method); - Builder.writeUInt16(OverloadCount); - Builder.writeTypeIndex(MethodList); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeRecordKind(TypeRecordKind::OverloadedMethod); + Builder.writeUInt16(Record.getNumOverloads()); + Builder.writeTypeIndex(Record.getMethodList()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeOneMethod( - MemberAccess Access, MethodKind Kind, MethodOptions Options, TypeIndex Type, - int32_t VTableSlotOffset, StringRef Name) { +void FieldListRecordBuilder::writeOneMethod(const OneMethodRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - uint16_t Flags = static_cast<uint16_t>(Access); - Flags |= static_cast<uint16_t>(Kind) << MethodKindShift; - Flags |= static_cast<uint16_t>(Options); + uint16_t Flags = static_cast<uint16_t>(Record.getAccess()); + Flags |= static_cast<uint16_t>(Record.getKind()) << MethodKindShift; + Flags |= static_cast<uint16_t>(Record.getOptions()); Builder.writeTypeRecordKind(TypeRecordKind::OneMethod); Builder.writeUInt16(Flags); - Builder.writeTypeIndex(Type); - switch (Kind) { - case MethodKind::IntroducingVirtual: - case MethodKind::PureIntroducingVirtual: - assert(VTableSlotOffset >= 0); - Builder.writeInt32(VTableSlotOffset); - break; - - default: - assert(VTableSlotOffset == -1); - break; + Builder.writeTypeIndex(Record.getType()); + if (Record.isIntroducingVirtual()) { + assert(Record.getVFTableOffset() >= 0); + Builder.writeInt32(Record.getVFTableOffset()); + } else { + assert(Record.getVFTableOffset() == -1); } - Builder.writeNullTerminatedString(Name); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeOneMethod(const MethodInfo &Method, - StringRef Name) { - writeOneMethod(Method.getAccess(), Method.getKind(), Method.getOptions(), - Method.getType(), Method.getVTableSlotOffset(), Name); -} - -void FieldListRecordBuilder::writeNestedType(TypeIndex Type, StringRef Name) { +void FieldListRecordBuilder::writeNestedType(const NestedTypeRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::NestedType); + Builder.writeTypeRecordKind(Record.getKind()); Builder.writeUInt16(0); - Builder.writeTypeIndex(Type); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeIndex(Record.getNestedType()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeStaticMember(MemberAccess Access, - TypeIndex Type, StringRef Name) { +void FieldListRecordBuilder::writeStaticDataMember( + const StaticDataMemberRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::StaticMember); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeRecordKind(Record.getKind()); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getType()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeIndirectVirtualBaseClass( - MemberAccess Access, TypeIndex Type, TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, uint64_t SlotIndex) { - writeVirtualBaseClass(TypeRecordKind::IndirectVirtualBaseClass, Access, Type, - VirtualBasePointerType, VirtualBasePointerOffset, - SlotIndex); -} - void FieldListRecordBuilder::writeVirtualBaseClass( - MemberAccess Access, TypeIndex Type, TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, uint64_t SlotIndex) { - writeVirtualBaseClass(TypeRecordKind::VirtualBaseClass, Access, Type, - VirtualBasePointerType, VirtualBasePointerOffset, - SlotIndex); -} - -void FieldListRecordBuilder::writeVirtualBaseClass( - TypeRecordKind Kind, MemberAccess Access, TypeIndex Type, - TypeIndex VirtualBasePointerType, int64_t VirtualBasePointerOffset, - uint64_t SlotIndex) { + const VirtualBaseClassRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(Kind); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeTypeIndex(VirtualBasePointerType); - Builder.writeEncodedInteger(VirtualBasePointerOffset); - Builder.writeEncodedUnsignedInteger(SlotIndex); + Builder.writeTypeRecordKind(Record.getKind()); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getBaseType()); + Builder.writeTypeIndex(Record.getVBPtrType()); + Builder.writeEncodedInteger(Record.getVBPtrOffset()); + Builder.writeEncodedUnsignedInteger(Record.getVTableIndex()); finishSubRecord(); } -void FieldListRecordBuilder::writeVirtualFunctionTablePointer(TypeIndex Type) { +void FieldListRecordBuilder::writeVFPtr(const VFPtrRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::VirtualFunctionTablePointer); + Builder.writeTypeRecordKind(TypeRecordKind::VFPtr); Builder.writeUInt16(0); - Builder.writeTypeIndex(Type); + Builder.writeTypeIndex(Record.getType()); finishSubRecord(); -}
\ No newline at end of file +} diff --git a/lib/DebugInfo/CodeView/ListRecordBuilder.cpp b/lib/DebugInfo/CodeView/ListRecordBuilder.cpp index 69c7e87330e6..eb79e8ac9a3f 100644 --- a/lib/DebugInfo/CodeView/ListRecordBuilder.cpp +++ b/lib/DebugInfo/CodeView/ListRecordBuilder.cpp @@ -7,25 +7,96 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallString.h" #include "llvm/DebugInfo/CodeView/ListRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" using namespace llvm; using namespace codeview; -ListRecordBuilder::ListRecordBuilder(TypeRecordKind Kind) : Builder(Kind) {} +ListRecordBuilder::ListRecordBuilder(TypeRecordKind Kind) + : Kind(Kind), Builder(Kind) {} + +void ListRecordBuilder::writeListContinuation(const ListContinuationRecord &R) { + TypeRecordBuilder &Builder = getBuilder(); + + assert(getLastContinuationSize() < 65535 - 8 && "continuation won't fit"); + + Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation); + Builder.writeUInt16(0); + Builder.writeTypeIndex(R.getContinuationIndex()); + + // End the current segment manually so that nothing comes after the + // continuation. + ContinuationOffsets.push_back(Builder.size()); + SubrecordStart = Builder.size(); +} void ListRecordBuilder::finishSubRecord() { - // The builder starts at offset 2 in the actual CodeView buffer, so add an - // additional offset of 2 before computing the alignment. - uint32_t Remainder = (Builder.size() + 2) % 4; + // The type table inserts a 16 bit size field before each list, so factor that + // into our alignment padding. + uint32_t Remainder = + (Builder.size() + 2 * (ContinuationOffsets.size() + 1)) % 4; if (Remainder != 0) { for (int32_t PaddingBytesLeft = 4 - Remainder; PaddingBytesLeft > 0; --PaddingBytesLeft) { - Builder.writeUInt8(0xf0 + PaddingBytesLeft); + Builder.writeUInt8(LF_PAD0 + PaddingBytesLeft); } } - // TODO: Split the list into multiple records if it's longer than 64KB, using - // a subrecord of TypeRecordKind::Index to chain the records together. - assert(Builder.size() < 65536); + // 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, + // back up and insert a continuation record, sliding the current subrecord + // down. + if (getLastContinuationSize() > 65535 - 8) { + assert(SubrecordStart != 0 && "can't slide from the start!"); + SmallString<128> SubrecordCopy( + Builder.str().slice(SubrecordStart, Builder.size())); + assert(SubrecordCopy.size() < 65530 && "subrecord is too large to slide!"); + Builder.truncate(SubrecordStart); + + // Write a placeholder continuation record. + Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation); + Builder.writeUInt16(0); + Builder.writeUInt32(0); + ContinuationOffsets.push_back(Builder.size()); + assert(Builder.size() == SubrecordStart + 8 && "wrong continuation size"); + assert(getLastContinuationSize() < 65535 && "segment too big"); + + // Start a new list record of the appropriate kind, and copy the previous + // subrecord into place. + Builder.writeTypeRecordKind(Kind); + Builder.writeBytes(SubrecordCopy); + } + + SubrecordStart = Builder.size(); +} + +TypeIndex ListRecordBuilder::writeListRecord(TypeTableBuilder &Table) { + // Get the continuation segments as a reversed vector of StringRefs for + // convenience. + SmallVector<StringRef, 1> Segments; + StringRef Data = str(); + size_t LastEnd = 0; + for (size_t SegEnd : ContinuationOffsets) { + Segments.push_back(Data.slice(LastEnd, SegEnd)); + LastEnd = SegEnd; + } + Segments.push_back(Data.slice(LastEnd, Builder.size())); + + // Pop the last record off and emit it directly. + StringRef LastRec = Segments.pop_back_val(); + TypeIndex ContinuationIndex = Table.writeRecord(LastRec); + + // Emit each record with a continuation in reverse order, so that each one + // references the previous record. + for (StringRef Rec : reverse(Segments)) { + assert(*reinterpret_cast<const ulittle16_t *>(Rec.data()) == + unsigned(Kind)); + ulittle32_t *ContinuationPtr = + reinterpret_cast<ulittle32_t *>(const_cast<char *>(Rec.end())) - 1; + *ContinuationPtr = ContinuationIndex.getIndex(); + ContinuationIndex = Table.writeRecord(Rec); + } + return ContinuationIndex; } diff --git a/lib/DebugInfo/CodeView/Makefile b/lib/DebugInfo/CodeView/Makefile deleted file mode 100644 index 535bc10b7442..000000000000 --- a/lib/DebugInfo/CodeView/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lib/DebugInfo/CodeView/Makefile ---------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL = ../../..
-LIBRARYNAME = LLVMDebugInfoCodeView
-BUILD_ARCHIVE := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/lib/DebugInfo/CodeView/MemoryTypeTableBuilder.cpp b/lib/DebugInfo/CodeView/MemoryTypeTableBuilder.cpp index 9afce92eeb1d..8b9e73b94ff5 100644 --- a/lib/DebugInfo/CodeView/MemoryTypeTableBuilder.cpp +++ b/lib/DebugInfo/CodeView/MemoryTypeTableBuilder.cpp @@ -13,23 +13,34 @@ using namespace llvm; using namespace codeview; -MemoryTypeTableBuilder::Record::Record(StringRef RData) - : Size(RData.size()), Data(new char[RData.size()]) { - memcpy(Data.get(), RData.data(), RData.size()); -} - TypeIndex MemoryTypeTableBuilder::writeRecord(StringRef Data) { + assert(Data.size() <= UINT16_MAX); auto I = HashedRecords.find(Data); if (I != HashedRecords.end()) { return I->second; } - std::unique_ptr<Record> R(new Record(Data)); + // The record provided by the user lacks the 2 byte size field prefix and is + // not padded to 4 bytes. Ultimately, that is what gets emitted in the object + // file, so pad it out now. + const int SizeOfRecLen = 2; + const int Align = 4; + int TotalSize = alignTo(Data.size() + SizeOfRecLen, Align); + assert(TotalSize - SizeOfRecLen <= UINT16_MAX); + char *Mem = + reinterpret_cast<char *>(RecordStorage.Allocate(TotalSize, Align)); + *reinterpret_cast<ulittle16_t *>(Mem) = uint16_t(TotalSize - SizeOfRecLen); + memcpy(Mem + SizeOfRecLen, Data.data(), Data.size()); + for (int I = Data.size() + SizeOfRecLen; I < TotalSize; ++I) + Mem[I] = LF_PAD0 + (TotalSize - I); TypeIndex TI(static_cast<uint32_t>(Records.size()) + TypeIndex::FirstNonSimpleIndex); - HashedRecords.insert(std::make_pair(StringRef(R->data(), R->size()), TI)); - Records.push_back(std::move(R)); + + // Use only the data supplied by the user as a key to the hash table, so that + // future lookups will succeed. + HashedRecords.insert(std::make_pair(StringRef(Mem + SizeOfRecLen, Data.size()), TI)); + Records.push_back(StringRef(Mem, TotalSize)); return TI; } diff --git a/lib/DebugInfo/CodeView/MethodListRecordBuilder.cpp b/lib/DebugInfo/CodeView/MethodListRecordBuilder.cpp index 889302556b2d..ae089a352081 100644 --- a/lib/DebugInfo/CodeView/MethodListRecordBuilder.cpp +++ b/lib/DebugInfo/CodeView/MethodListRecordBuilder.cpp @@ -14,7 +14,7 @@ using namespace llvm; using namespace codeview; MethodListRecordBuilder::MethodListRecordBuilder() - : ListRecordBuilder(TypeRecordKind::MethodList) {} + : ListRecordBuilder(TypeRecordKind::MethodOverloadList) {} void MethodListRecordBuilder::writeMethod(MemberAccess Access, MethodKind Kind, MethodOptions Options, TypeIndex Type, diff --git a/lib/DebugInfo/CodeView/ModuleSubstream.cpp b/lib/DebugInfo/CodeView/ModuleSubstream.cpp new file mode 100644 index 000000000000..2e31ed6b5b7f --- /dev/null +++ b/lib/DebugInfo/CodeView/ModuleSubstream.cpp @@ -0,0 +1,42 @@ +//===- ModuleSubstream.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/ModuleSubstream.h" + +#include "llvm/DebugInfo/CodeView/StreamReader.h" + +using namespace llvm; +using namespace llvm::codeview; + +ModuleSubstream::ModuleSubstream() : Kind(ModuleSubstreamKind::None) {} + +ModuleSubstream::ModuleSubstream(ModuleSubstreamKind Kind, StreamRef Data) + : Kind(Kind), Data(Data) {} + +Error ModuleSubstream::initialize(StreamRef Stream, ModuleSubstream &Info) { + const ModuleSubsectionHeader *Header; + StreamReader Reader(Stream); + if (auto EC = Reader.readObject(Header)) + return EC; + + ModuleSubstreamKind Kind = + static_cast<ModuleSubstreamKind>(uint32_t(Header->Kind)); + if (auto EC = Reader.readStreamRef(Info.Data, Header->Length)) + return EC; + Info.Kind = Kind; + return Error::success(); +} + +uint32_t ModuleSubstream::getRecordLength() const { + return sizeof(ModuleSubsectionHeader) + Data.getLength(); +} + +ModuleSubstreamKind ModuleSubstream::getSubstreamKind() const { return Kind; } + +StreamRef ModuleSubstream::getRecordData() const { return Data; } diff --git a/lib/DebugInfo/CodeView/ModuleSubstreamVisitor.cpp b/lib/DebugInfo/CodeView/ModuleSubstreamVisitor.cpp new file mode 100644 index 000000000000..6f237ee67fe4 --- /dev/null +++ b/lib/DebugInfo/CodeView/ModuleSubstreamVisitor.cpp @@ -0,0 +1,104 @@ +//===- ModuleSubstreamVisitor.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/ModuleSubstreamVisitor.h" + +using namespace llvm; +using namespace llvm::codeview; + +Error IModuleSubstreamVisitor::visitSymbols(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::Symbols, Data); +} +Error IModuleSubstreamVisitor::visitLines(StreamRef Data, + const LineSubstreamHeader *Header, + const LineInfoArray &Lines) { + return visitUnknown(ModuleSubstreamKind::Lines, Data); +} +Error IModuleSubstreamVisitor::visitStringTable(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::StringTable, Data); +} +Error IModuleSubstreamVisitor::visitFileChecksums( + StreamRef Data, const FileChecksumArray &Checksums) { + return visitUnknown(ModuleSubstreamKind::FileChecksums, Data); +} +Error IModuleSubstreamVisitor::visitFrameData(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::FrameData, Data); +} +Error IModuleSubstreamVisitor::visitInlineeLines(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::InlineeLines, Data); +} +Error IModuleSubstreamVisitor::visitCrossScopeImports(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::CrossScopeExports, Data); +} +Error IModuleSubstreamVisitor::visitCrossScopeExports(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::CrossScopeImports, Data); +} +Error IModuleSubstreamVisitor::visitILLines(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::ILLines, Data); +} +Error IModuleSubstreamVisitor::visitFuncMDTokenMap(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::FuncMDTokenMap, Data); +} +Error IModuleSubstreamVisitor::visitTypeMDTokenMap(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::TypeMDTokenMap, Data); +} +Error IModuleSubstreamVisitor::visitMergedAssemblyInput(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::MergedAssemblyInput, Data); +} +Error IModuleSubstreamVisitor::visitCoffSymbolRVA(StreamRef Data) { + return visitUnknown(ModuleSubstreamKind::CoffSymbolRVA, Data); +} + +Error llvm::codeview::visitModuleSubstream(const ModuleSubstream &R, + IModuleSubstreamVisitor &V) { + switch (R.getSubstreamKind()) { + case ModuleSubstreamKind::Symbols: + return V.visitSymbols(R.getRecordData()); + case ModuleSubstreamKind::Lines: { + StreamReader Reader(R.getRecordData()); + const LineSubstreamHeader *Header; + if (auto EC = Reader.readObject(Header)) + return EC; + VarStreamArrayExtractor<LineColumnEntry> E(Header); + LineInfoArray LineInfos(E); + if (auto EC = Reader.readArray(LineInfos, Reader.bytesRemaining())) + return EC; + return V.visitLines(R.getRecordData(), Header, LineInfos); + } + case ModuleSubstreamKind::StringTable: + return V.visitStringTable(R.getRecordData()); + case ModuleSubstreamKind::FileChecksums: { + StreamReader Reader(R.getRecordData()); + FileChecksumArray Checksums; + if (auto EC = Reader.readArray(Checksums, Reader.bytesRemaining())) + return EC; + return V.visitFileChecksums(R.getRecordData(), Checksums); + } + case ModuleSubstreamKind::FrameData: + return V.visitFrameData(R.getRecordData()); + case ModuleSubstreamKind::InlineeLines: + return V.visitInlineeLines(R.getRecordData()); + case ModuleSubstreamKind::CrossScopeImports: + return V.visitCrossScopeImports(R.getRecordData()); + case ModuleSubstreamKind::CrossScopeExports: + return V.visitCrossScopeExports(R.getRecordData()); + case ModuleSubstreamKind::ILLines: + return V.visitILLines(R.getRecordData()); + case ModuleSubstreamKind::FuncMDTokenMap: + return V.visitFuncMDTokenMap(R.getRecordData()); + case ModuleSubstreamKind::TypeMDTokenMap: + return V.visitTypeMDTokenMap(R.getRecordData()); + case ModuleSubstreamKind::MergedAssemblyInput: + return V.visitMergedAssemblyInput(R.getRecordData()); + case ModuleSubstreamKind::CoffSymbolRVA: + return V.visitCoffSymbolRVA(R.getRecordData()); + default: + return V.visitUnknown(R.getSubstreamKind(), R.getRecordData()); + } +} diff --git a/lib/DebugInfo/CodeView/RecordSerialization.cpp b/lib/DebugInfo/CodeView/RecordSerialization.cpp new file mode 100644 index 000000000000..ab9206a33ec0 --- /dev/null +++ b/lib/DebugInfo/CodeView/RecordSerialization.cpp @@ -0,0 +1,171 @@ +//===-- 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/TypeRecord.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; +} + +std::error_code llvm::codeview::consume(ArrayRef<uint8_t> &Data, APSInt &Num) { + // Used to avoid overload ambiguity on APInt construtor. + bool FalseVal = false; + if (Data.size() < 2) + return std::make_error_code(std::errc::illegal_byte_sequence); + uint16_t Short = *reinterpret_cast<const ulittle16_t *>(Data.data()); + Data = Data.drop_front(2); + if (Short < LF_NUMERIC) { + Num = APSInt(APInt(/*numBits=*/16, Short, /*isSigned=*/false), + /*isUnsigned=*/true); + return std::error_code(); + } + switch (Short) { + case LF_CHAR: + if (Data.size() < 1) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/8, + *reinterpret_cast<const int8_t *>(Data.data()), + /*isSigned=*/true), + /*isUnsigned=*/false); + Data = Data.drop_front(1); + return std::error_code(); + case LF_SHORT: + if (Data.size() < 2) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/16, + *reinterpret_cast<const little16_t *>(Data.data()), + /*isSigned=*/true), + /*isUnsigned=*/false); + Data = Data.drop_front(2); + return std::error_code(); + case LF_USHORT: + if (Data.size() < 2) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/16, + *reinterpret_cast<const ulittle16_t *>(Data.data()), + /*isSigned=*/false), + /*isUnsigned=*/true); + Data = Data.drop_front(2); + return std::error_code(); + case LF_LONG: + if (Data.size() < 4) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/32, + *reinterpret_cast<const little32_t *>(Data.data()), + /*isSigned=*/true), + /*isUnsigned=*/false); + Data = Data.drop_front(4); + return std::error_code(); + case LF_ULONG: + if (Data.size() < 4) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/32, + *reinterpret_cast<const ulittle32_t *>(Data.data()), + /*isSigned=*/FalseVal), + /*isUnsigned=*/true); + Data = Data.drop_front(4); + return std::error_code(); + case LF_QUADWORD: + if (Data.size() < 8) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/64, + *reinterpret_cast<const little64_t *>(Data.data()), + /*isSigned=*/true), + /*isUnsigned=*/false); + Data = Data.drop_front(8); + return std::error_code(); + case LF_UQUADWORD: + if (Data.size() < 8) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = APSInt(APInt(/*numBits=*/64, + *reinterpret_cast<const ulittle64_t *>(Data.data()), + /*isSigned=*/false), + /*isUnsigned=*/true); + Data = Data.drop_front(8); + return std::error_code(); + } + return std::make_error_code(std::errc::illegal_byte_sequence); +} + +std::error_code llvm::codeview::consume(StringRef &Data, APSInt &Num) { + ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end()); + auto EC = consume(Bytes, Num); + Data = StringRef(reinterpret_cast<const char *>(Bytes.data()), Bytes.size()); + return EC; +} + +/// Decode a numeric leaf value that is known to be a uint64_t. +std::error_code llvm::codeview::consume_numeric(ArrayRef<uint8_t> &Data, + uint64_t &Num) { + APSInt N; + if (auto EC = consume(Data, N)) + return EC; + if (N.isSigned() || !N.isIntN(64)) + return std::make_error_code(std::errc::illegal_byte_sequence); + Num = N.getLimitedValue(); + return std::error_code(); +} + +std::error_code llvm::codeview::consume(ArrayRef<uint8_t> &Data, + uint32_t &Item) { + const support::ulittle32_t *IntPtr; + if (auto EC = consumeObject(Data, IntPtr)) + return EC; + Item = *IntPtr; + return std::error_code(); +} + +std::error_code llvm::codeview::consume(StringRef &Data, uint32_t &Item) { + ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end()); + auto EC = consume(Bytes, Item); + Data = StringRef(reinterpret_cast<const char *>(Bytes.data()), Bytes.size()); + return EC; +} + +std::error_code llvm::codeview::consume(ArrayRef<uint8_t> &Data, + int32_t &Item) { + const support::little32_t *IntPtr; + if (auto EC = consumeObject(Data, IntPtr)) + return EC; + Item = *IntPtr; + return std::error_code(); +} + +std::error_code llvm::codeview::consume(ArrayRef<uint8_t> &Data, + StringRef &Item) { + if (Data.empty()) + return std::make_error_code(std::errc::illegal_byte_sequence); + + StringRef Rest; + std::tie(Item, Rest) = getBytesAsCharacters(Data).split('\0'); + // We expect this to be null terminated. If it was not, it is an error. + if (Data.size() == Item.size()) + return std::make_error_code(std::errc::illegal_byte_sequence); + + Data = ArrayRef<uint8_t>(Rest.bytes_begin(), Rest.bytes_end()); + return std::error_code(); +} diff --git a/lib/DebugInfo/CodeView/StreamReader.cpp b/lib/DebugInfo/CodeView/StreamReader.cpp new file mode 100644 index 000000000000..64e45487322e --- /dev/null +++ b/lib/DebugInfo/CodeView/StreamReader.cpp @@ -0,0 +1,93 @@ +//===- StreamReader.cpp - Reads bytes and objects from a stream -----------===// +// +// 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/StreamReader.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/StreamRef.h" + +using namespace llvm; +using namespace llvm::codeview; + +StreamReader::StreamReader(StreamRef S) : Stream(S), Offset(0) {} + +Error StreamReader::readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer) { + if (auto EC = Stream.readLongestContiguousChunk(Offset, Buffer)) + return EC; + Offset += Buffer.size(); + return Error::success(); +} + +Error StreamReader::readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size) { + if (auto EC = Stream.readBytes(Offset, Size, Buffer)) + return EC; + Offset += Size; + return Error::success(); +} + +Error StreamReader::readInteger(uint16_t &Dest) { + const support::ulittle16_t *P; + if (auto EC = readObject(P)) + return EC; + Dest = *P; + return Error::success(); +} + +Error StreamReader::readInteger(uint32_t &Dest) { + const support::ulittle32_t *P; + if (auto EC = readObject(P)) + return EC; + Dest = *P; + return Error::success(); +} + +Error StreamReader::readZeroString(StringRef &Dest) { + uint32_t Length = 0; + // First compute the length of the string by reading 1 byte at a time. + uint32_t OriginalOffset = getOffset(); + const char *C; + do { + if (auto EC = readObject(C)) + return EC; + if (*C != '\0') + ++Length; + } while (*C != '\0'); + // Now go back and request a reference for that many bytes. + uint32_t NewOffset = getOffset(); + setOffset(OriginalOffset); + + ArrayRef<uint8_t> Data; + if (auto EC = readBytes(Data, Length)) + return EC; + Dest = StringRef(reinterpret_cast<const char *>(Data.begin()), Data.size()); + + // Now set the offset back to where it was after we calculated the length. + setOffset(NewOffset); + return Error::success(); +} + +Error StreamReader::readFixedString(StringRef &Dest, uint32_t Length) { + ArrayRef<uint8_t> Bytes; + if (auto EC = readBytes(Bytes, Length)) + return EC; + Dest = StringRef(reinterpret_cast<const char *>(Bytes.begin()), Bytes.size()); + return Error::success(); +} + +Error StreamReader::readStreamRef(StreamRef &Ref) { + return readStreamRef(Ref, bytesRemaining()); +} + +Error StreamReader::readStreamRef(StreamRef &Ref, uint32_t Length) { + if (bytesRemaining() < Length) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + Ref = Stream.slice(Offset, Length); + Offset += Length; + return Error::success(); +} diff --git a/lib/DebugInfo/CodeView/StreamWriter.cpp b/lib/DebugInfo/CodeView/StreamWriter.cpp new file mode 100644 index 000000000000..f61c6b522f57 --- /dev/null +++ b/lib/DebugInfo/CodeView/StreamWriter.cpp @@ -0,0 +1,77 @@ +//===- StreamWrite.cpp - Writes bytes and objects to a stream -------------===// +// +// 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/StreamWriter.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamRef.h" + +using namespace llvm; +using namespace llvm::codeview; + +StreamWriter::StreamWriter(StreamRef S) : Stream(S), Offset(0) {} + +Error StreamWriter::writeBytes(ArrayRef<uint8_t> Buffer) { + if (auto EC = Stream.writeBytes(Offset, Buffer)) + return EC; + Offset += Buffer.size(); + return Error::success(); +} + +Error StreamWriter::writeInteger(uint16_t Int) { + return writeObject(support::ulittle16_t(Int)); +} + +Error StreamWriter::writeInteger(uint32_t Int) { + return writeObject(support::ulittle32_t(Int)); +} + +Error StreamWriter::writeZeroString(StringRef Str) { + if (auto EC = writeFixedString(Str)) + return EC; + if (auto EC = writeObject('\0')) + return EC; + + return Error::success(); +} + +Error StreamWriter::writeFixedString(StringRef Str) { + ArrayRef<uint8_t> Bytes(Str.bytes_begin(), Str.bytes_end()); + if (auto EC = Stream.writeBytes(Offset, Bytes)) + return EC; + + Offset += Str.size(); + return Error::success(); +} + +Error StreamWriter::writeStreamRef(StreamRef Ref) { + if (auto EC = writeStreamRef(Ref, Ref.getLength())) + return EC; + Offset += Ref.getLength(); + return Error::success(); +} + +Error StreamWriter::writeStreamRef(StreamRef Ref, uint32_t Length) { + Ref = Ref.slice(0, Length); + + StreamReader SrcReader(Ref); + // This is a bit tricky. If we just call readBytes, we are requiring that it + // return us the entire stream as a contiguous buffer. For large streams this + // will allocate a huge amount of space from the pool. Instead, iterate over + // each contiguous chunk until we've consumed the entire stream. + while (SrcReader.bytesRemaining() > 0) { + ArrayRef<uint8_t> Chunk; + if (auto EC = SrcReader.readLongestContiguousChunk(Chunk)) + return EC; + if (auto EC = writeBytes(Chunk)) + return EC; + } + return Error::success(); +} diff --git a/lib/DebugInfo/CodeView/SymbolDumper.cpp b/lib/DebugInfo/CodeView/SymbolDumper.cpp new file mode 100644 index 000000000000..6763c3d562d7 --- /dev/null +++ b/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -0,0 +1,642 @@ +//===-- 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/EnumTables.h" +#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeDumper.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.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 CVSymbolVisitor<CVSymbolDumperImpl> { +public: + CVSymbolDumperImpl(CVTypeDumper &CVTD, SymbolDumpDelegate *ObjDelegate, + ScopedPrinter &W, bool PrintRecordBytes) + : CVSymbolVisitor(ObjDelegate), CVTD(CVTD), ObjDelegate(ObjDelegate), + W(W), PrintRecordBytes(PrintRecordBytes), InFunctionScope(false) {} + +/// CVSymbolVisitor overrides. +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + void visit##Name(SymbolKind Kind, Name &Record); +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CVSymbolTypes.def" + + void visitSymbolBegin(SymbolKind Kind, ArrayRef<uint8_t> Data); + void visitSymbolEnd(SymbolKind Kind, ArrayRef<uint8_t> OriginalSymData); + void visitUnknownSymbol(SymbolKind Kind, ArrayRef<uint8_t> Data); + +private: + void printLocalVariableAddrRange(const LocalVariableAddrRange &Range, + uint32_t RelocationOffset); + void printLocalVariableAddrGap(ArrayRef<LocalVariableAddrGap> Gaps); + + CVTypeDumper &CVTD; + 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::visitSymbolBegin(SymbolKind Kind, + ArrayRef<uint8_t> Data) {} + +void CVSymbolDumperImpl::visitSymbolEnd(SymbolKind Kind, + ArrayRef<uint8_t> OriginalSymData) { + if (PrintRecordBytes && ObjDelegate) + ObjDelegate->printBinaryBlockWithRelocs("SymData", OriginalSymData); +} + +void CVSymbolDumperImpl::visitBlockSym(SymbolKind Kind, BlockSym &Block) { + DictScope S(W, "BlockStart"); + + StringRef LinkageName; + W.printHex("PtrParent", Block.Header.PtrParent); + W.printHex("PtrEnd", Block.Header.PtrEnd); + W.printHex("CodeSize", Block.Header.CodeSize); + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", Block.getRelocationOffset(), + Block.Header.CodeOffset, &LinkageName); + } + W.printHex("Segment", Block.Header.Segment); + W.printString("BlockName", Block.Name); + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitThunk32Sym(SymbolKind Kind, Thunk32Sym &Thunk) { + DictScope S(W, "Thunk32"); + W.printNumber("Parent", Thunk.Header.Parent); + W.printNumber("End", Thunk.Header.End); + W.printNumber("Next", Thunk.Header.Next); + W.printNumber("Off", Thunk.Header.Off); + W.printNumber("Seg", Thunk.Header.Seg); + W.printNumber("Len", Thunk.Header.Len); + W.printEnum("Ordinal", Thunk.Header.Ord, getThunkOrdinalNames()); +} + +void CVSymbolDumperImpl::visitTrampolineSym(SymbolKind Kind, + TrampolineSym &Tramp) { + DictScope S(W, "Trampoline"); + W.printEnum("Type", Tramp.Header.Type, getTrampolineNames()); + W.printNumber("Size", Tramp.Header.Size); + W.printNumber("ThunkOff", Tramp.Header.ThunkOff); + W.printNumber("TargetOff", Tramp.Header.TargetOff); + W.printNumber("ThunkSection", Tramp.Header.ThunkSection); + W.printNumber("TargetSection", Tramp.Header.TargetSection); +} + +void CVSymbolDumperImpl::visitSectionSym(SymbolKind Kind, SectionSym &Section) { + DictScope S(W, "Section"); + W.printNumber("SectionNumber", Section.Header.SectionNumber); + W.printNumber("Alignment", Section.Header.Alignment); + W.printNumber("Reserved", Section.Header.Reserved); + W.printNumber("Rva", Section.Header.Rva); + W.printNumber("Length", Section.Header.Length); + W.printFlags("Characteristics", Section.Header.Characteristics, + getImageSectionCharacteristicNames(), + COFF::SectionCharacteristics(0x00F00000)); + + W.printString("Name", Section.Name); +} + +void CVSymbolDumperImpl::visitCoffGroupSym(SymbolKind Kind, + CoffGroupSym &CoffGroup) { + DictScope S(W, "COFF Group"); + W.printNumber("Size", CoffGroup.Header.Size); + W.printFlags("Characteristics", CoffGroup.Header.Characteristics, + getImageSectionCharacteristicNames(), + COFF::SectionCharacteristics(0x00F00000)); + W.printNumber("Offset", CoffGroup.Header.Offset); + W.printNumber("Segment", CoffGroup.Header.Segment); + W.printString("Name", CoffGroup.Name); +} + +void CVSymbolDumperImpl::visitBPRelativeSym(SymbolKind Kind, + BPRelativeSym &BPRel) { + DictScope S(W, "BPRelativeSym"); + + W.printNumber("Offset", BPRel.Header.Offset); + CVTD.printTypeIndex("Type", BPRel.Header.Type); + W.printString("VarName", BPRel.Name); +} + +void CVSymbolDumperImpl::visitBuildInfoSym(SymbolKind Kind, + BuildInfoSym &BuildInfo) { + DictScope S(W, "BuildInfo"); + + W.printNumber("BuildId", BuildInfo.Header.BuildId); +} + +void CVSymbolDumperImpl::visitCallSiteInfoSym(SymbolKind Kind, + CallSiteInfoSym &CallSiteInfo) { + DictScope S(W, "CallSiteInfo"); + + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField( + "CodeOffset", CallSiteInfo.getRelocationOffset(), + CallSiteInfo.Header.CodeOffset, &LinkageName); + } + W.printHex("Segment", CallSiteInfo.Header.Segment); + W.printHex("Reserved", CallSiteInfo.Header.Reserved); + CVTD.printTypeIndex("Type", CallSiteInfo.Header.Type); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitEnvBlockSym(SymbolKind Kind, + EnvBlockSym &EnvBlock) { + DictScope S(W, "EnvBlock"); + + W.printNumber("Reserved", EnvBlock.Header.Reserved); + ListScope L(W, "Entries"); + for (auto Entry : EnvBlock.Fields) { + W.printString(Entry); + } +} + +void CVSymbolDumperImpl::visitFileStaticSym(SymbolKind Kind, + FileStaticSym &FileStatic) { + DictScope S(W, "FileStatic"); + W.printNumber("Index", FileStatic.Header.Index); + W.printNumber("ModFilenameOffset", FileStatic.Header.ModFilenameOffset); + W.printFlags("Flags", uint16_t(FileStatic.Header.Flags), getLocalFlagNames()); + W.printString("Name", FileStatic.Name); +} + +void CVSymbolDumperImpl::visitExportSym(SymbolKind Kind, ExportSym &Export) { + DictScope S(W, "Export"); + W.printNumber("Ordinal", Export.Header.Ordinal); + W.printFlags("Flags", Export.Header.Flags, getExportSymFlagNames()); + W.printString("Name", Export.Name); +} + +void CVSymbolDumperImpl::visitCompile2Sym(SymbolKind Kind, + Compile2Sym &Compile2) { + DictScope S(W, "CompilerFlags2"); + + W.printEnum("Language", Compile2.Header.getLanguage(), + getSourceLanguageNames()); + W.printFlags("Flags", Compile2.Header.flags & ~0xff, + getCompileSym2FlagNames()); + W.printEnum("Machine", unsigned(Compile2.Header.Machine), getCPUTypeNames()); + std::string FrontendVersion; + { + raw_string_ostream Out(FrontendVersion); + Out << Compile2.Header.VersionFrontendMajor << '.' + << Compile2.Header.VersionFrontendMinor << '.' + << Compile2.Header.VersionFrontendBuild; + } + std::string BackendVersion; + { + raw_string_ostream Out(BackendVersion); + Out << Compile2.Header.VersionBackendMajor << '.' + << Compile2.Header.VersionBackendMinor << '.' + << Compile2.Header.VersionBackendBuild; + } + W.printString("FrontendVersion", FrontendVersion); + W.printString("BackendVersion", BackendVersion); + W.printString("VersionName", Compile2.Version); +} + +void CVSymbolDumperImpl::visitCompile3Sym(SymbolKind Kind, + Compile3Sym &Compile3) { + DictScope S(W, "CompilerFlags3"); + + W.printEnum("Language", Compile3.Header.getLanguage(), + getSourceLanguageNames()); + W.printFlags("Flags", Compile3.Header.flags & ~0xff, + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile3.Header.Machine), getCPUTypeNames()); + std::string FrontendVersion; + { + raw_string_ostream Out(FrontendVersion); + Out << Compile3.Header.VersionFrontendMajor << '.' + << Compile3.Header.VersionFrontendMinor << '.' + << Compile3.Header.VersionFrontendBuild << '.' + << Compile3.Header.VersionFrontendQFE; + } + std::string BackendVersion; + { + raw_string_ostream Out(BackendVersion); + Out << Compile3.Header.VersionBackendMajor << '.' + << Compile3.Header.VersionBackendMinor << '.' + << Compile3.Header.VersionBackendBuild << '.' + << Compile3.Header.VersionBackendQFE; + } + W.printString("FrontendVersion", FrontendVersion); + W.printString("BackendVersion", BackendVersion); + W.printString("VersionName", Compile3.Version); +} + +void CVSymbolDumperImpl::visitConstantSym(SymbolKind Kind, + ConstantSym &Constant) { + DictScope S(W, "Constant"); + + CVTD.printTypeIndex("Type", Constant.Header.Type); + W.printNumber("Value", Constant.Value); + W.printString("Name", Constant.Name); +} + +void CVSymbolDumperImpl::visitDataSym(SymbolKind Kind, DataSym &Data) { + DictScope S(W, "DataSym"); + + W.printEnum("Kind", uint16_t(Kind), getSymbolTypeNames()); + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("DataOffset", Data.getRelocationOffset(), + Data.Header.DataOffset, &LinkageName); + } + CVTD.printTypeIndex("Type", Data.Header.Type); + W.printString("DisplayName", Data.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitDefRangeFramePointerRelFullScopeSym( + SymbolKind Kind, + DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) { + DictScope S(W, "DefRangeFramePointerRelFullScope"); + W.printNumber("Offset", DefRangeFramePointerRelFullScope.Header.Offset); +} + +void CVSymbolDumperImpl::visitDefRangeFramePointerRelSym( + SymbolKind Kind, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + DictScope S(W, "DefRangeFramePointerRel"); + + W.printNumber("Offset", DefRangeFramePointerRel.Header.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Header.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); +} + +void CVSymbolDumperImpl::visitDefRangeRegisterRelSym( + SymbolKind Kind, DefRangeRegisterRelSym &DefRangeRegisterRel) { + DictScope S(W, "DefRangeRegisterRel"); + + W.printNumber("BaseRegister", DefRangeRegisterRel.Header.BaseRegister); + W.printBoolean("HasSpilledUDTMember", + DefRangeRegisterRel.hasSpilledUDTMember()); + W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); + W.printNumber("BasePointerOffset", + DefRangeRegisterRel.Header.BasePointerOffset); + printLocalVariableAddrRange(DefRangeRegisterRel.Header.Range, + DefRangeRegisterRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegisterRel.Gaps); +} + +void CVSymbolDumperImpl::visitDefRangeRegisterSym( + SymbolKind Kind, DefRangeRegisterSym &DefRangeRegister) { + DictScope S(W, "DefRangeRegister"); + + W.printNumber("Register", DefRangeRegister.Header.Register); + W.printNumber("MayHaveNoName", DefRangeRegister.Header.MayHaveNoName); + printLocalVariableAddrRange(DefRangeRegister.Header.Range, + DefRangeRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegister.Gaps); +} + +void CVSymbolDumperImpl::visitDefRangeSubfieldRegisterSym( + SymbolKind Kind, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + DictScope S(W, "DefRangeSubfieldRegister"); + + W.printNumber("Register", DefRangeSubfieldRegister.Header.Register); + W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Header.MayHaveNoName); + W.printNumber("OffsetInParent", + DefRangeSubfieldRegister.Header.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfieldRegister.Header.Range, + DefRangeSubfieldRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfieldRegister.Gaps); +} + +void CVSymbolDumperImpl::visitDefRangeSubfieldSym( + SymbolKind Kind, DefRangeSubfieldSym &DefRangeSubfield) { + DictScope S(W, "DefRangeSubfield"); + + if (ObjDelegate) { + StringRef StringTable = ObjDelegate->getStringTable(); + auto ProgramStringTableOffset = DefRangeSubfield.Header.Program; + if (ProgramStringTableOffset >= StringTable.size()) + return parseError(); + StringRef Program = + StringTable.drop_front(ProgramStringTableOffset).split('\0').first; + W.printString("Program", Program); + } + W.printNumber("OffsetInParent", DefRangeSubfield.Header.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfield.Header.Range, + DefRangeSubfield.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfield.Gaps); +} + +void CVSymbolDumperImpl::visitDefRangeSym(SymbolKind Kind, + DefRangeSym &DefRange) { + DictScope S(W, "DefRange"); + + if (ObjDelegate) { + StringRef StringTable = ObjDelegate->getStringTable(); + auto ProgramStringTableOffset = DefRange.Header.Program; + if (ProgramStringTableOffset >= StringTable.size()) + return parseError(); + StringRef Program = + StringTable.drop_front(ProgramStringTableOffset).split('\0').first; + W.printString("Program", Program); + } + printLocalVariableAddrRange(DefRange.Header.Range, + DefRange.getRelocationOffset()); + printLocalVariableAddrGap(DefRange.Gaps); +} + +void CVSymbolDumperImpl::visitFrameCookieSym(SymbolKind Kind, + FrameCookieSym &FrameCookie) { + DictScope S(W, "FrameCookie"); + + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField( + "CodeOffset", FrameCookie.getRelocationOffset(), + FrameCookie.Header.CodeOffset, &LinkageName); + } + W.printHex("Register", FrameCookie.Header.Register); + W.printEnum("CookieKind", uint16_t(FrameCookie.Header.CookieKind), + getFrameCookieKindNames()); + W.printHex("Flags", FrameCookie.Header.Flags); +} + +void CVSymbolDumperImpl::visitFrameProcSym(SymbolKind Kind, + FrameProcSym &FrameProc) { + DictScope S(W, "FrameProc"); + + W.printHex("TotalFrameBytes", FrameProc.Header.TotalFrameBytes); + W.printHex("PaddingFrameBytes", FrameProc.Header.PaddingFrameBytes); + W.printHex("OffsetToPadding", FrameProc.Header.OffsetToPadding); + W.printHex("BytesOfCalleeSavedRegisters", + FrameProc.Header.BytesOfCalleeSavedRegisters); + W.printHex("OffsetOfExceptionHandler", + FrameProc.Header.OffsetOfExceptionHandler); + W.printHex("SectionIdOfExceptionHandler", + FrameProc.Header.SectionIdOfExceptionHandler); + W.printFlags("Flags", FrameProc.Header.Flags, getFrameProcSymFlagNames()); +} + +void CVSymbolDumperImpl::visitHeapAllocationSiteSym( + SymbolKind Kind, HeapAllocationSiteSym &HeapAllocSite) { + DictScope S(W, "HeapAllocationSite"); + + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField( + "CodeOffset", HeapAllocSite.getRelocationOffset(), + HeapAllocSite.Header.CodeOffset, &LinkageName); + } + W.printHex("Segment", HeapAllocSite.Header.Segment); + W.printHex("CallInstructionSize", HeapAllocSite.Header.CallInstructionSize); + CVTD.printTypeIndex("Type", HeapAllocSite.Header.Type); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitInlineSiteSym(SymbolKind Kind, + InlineSiteSym &InlineSite) { + DictScope S(W, "InlineSite"); + + W.printHex("PtrParent", InlineSite.Header.PtrParent); + W.printHex("PtrEnd", InlineSite.Header.PtrEnd); + CVTD.printTypeIndex("Inlinee", InlineSite.Header.Inlinee); + + ListScope BinaryAnnotations(W, "BinaryAnnotations"); + for (auto &Annotation : InlineSite.annotations()) { + switch (Annotation.OpCode) { + case BinaryAnnotationsOpCode::Invalid: + return parseError(); + 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; + } + } + } +} + +void CVSymbolDumperImpl::visitRegisterSym(SymbolKind Kind, + RegisterSym &Register) { + DictScope S(W, "RegisterSym"); + W.printNumber("Type", Register.Header.Index); + W.printEnum("Seg", uint16_t(Register.Header.Register), getRegisterNames()); + W.printString("Name", Register.Name); +} + +void CVSymbolDumperImpl::visitPublicSym32(SymbolKind Kind, + PublicSym32 &Public) { + DictScope S(W, "PublicSym"); + W.printNumber("Type", Public.Header.Index); + W.printNumber("Seg", Public.Header.Seg); + W.printNumber("Off", Public.Header.Off); + W.printString("Name", Public.Name); +} + +void CVSymbolDumperImpl::visitProcRefSym(SymbolKind Kind, ProcRefSym &ProcRef) { + DictScope S(W, "ProcRef"); + W.printNumber("SumName", ProcRef.Header.SumName); + W.printNumber("SymOffset", ProcRef.Header.SymOffset); + W.printNumber("Mod", ProcRef.Header.Mod); + W.printString("Name", ProcRef.Name); +} + +void CVSymbolDumperImpl::visitLabelSym(SymbolKind Kind, LabelSym &Label) { + DictScope S(W, "Label"); + + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", Label.getRelocationOffset(), + Label.Header.CodeOffset, &LinkageName); + } + W.printHex("Segment", Label.Header.Segment); + W.printHex("Flags", Label.Header.Flags); + W.printFlags("Flags", Label.Header.Flags, getProcSymFlagNames()); + W.printString("DisplayName", Label.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitLocalSym(SymbolKind Kind, LocalSym &Local) { + DictScope S(W, "Local"); + + CVTD.printTypeIndex("Type", Local.Header.Type); + W.printFlags("Flags", uint16_t(Local.Header.Flags), getLocalFlagNames()); + W.printString("VarName", Local.Name); +} + +void CVSymbolDumperImpl::visitObjNameSym(SymbolKind Kind, ObjNameSym &ObjName) { + DictScope S(W, "ObjectName"); + + W.printHex("Signature", ObjName.Header.Signature); + W.printString("ObjectName", ObjName.Name); +} + +void CVSymbolDumperImpl::visitProcSym(SymbolKind Kind, ProcSym &Proc) { + DictScope S(W, "ProcStart"); + + if (InFunctionScope) + return parseError(); + + InFunctionScope = true; + + StringRef LinkageName; + W.printEnum("Kind", uint16_t(Kind), getSymbolTypeNames()); + W.printHex("PtrParent", Proc.Header.PtrParent); + W.printHex("PtrEnd", Proc.Header.PtrEnd); + W.printHex("PtrNext", Proc.Header.PtrNext); + W.printHex("CodeSize", Proc.Header.CodeSize); + W.printHex("DbgStart", Proc.Header.DbgStart); + W.printHex("DbgEnd", Proc.Header.DbgEnd); + CVTD.printTypeIndex("FunctionType", Proc.Header.FunctionType); + if (ObjDelegate) { + ObjDelegate->printRelocatedField("CodeOffset", Proc.getRelocationOffset(), + Proc.Header.CodeOffset, &LinkageName); + } + W.printHex("Segment", Proc.Header.Segment); + W.printFlags("Flags", static_cast<uint8_t>(Proc.Header.Flags), + getProcSymFlagNames()); + W.printString("DisplayName", Proc.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitScopeEndSym(SymbolKind Kind, + ScopeEndSym &ScopeEnd) { + if (Kind == SymbolKind::S_END) + DictScope S(W, "BlockEnd"); + else if (Kind == SymbolKind::S_PROC_ID_END) + DictScope S(W, "ProcEnd"); + else if (Kind == SymbolKind::S_INLINESITE_END) + DictScope S(W, "InlineSiteEnd"); + + InFunctionScope = false; +} + +void CVSymbolDumperImpl::visitCallerSym(SymbolKind Kind, CallerSym &Caller) { + ListScope S(W, Kind == S_CALLEES ? "Callees" : "Callers"); + for (auto FuncID : Caller.Indices) + CVTD.printTypeIndex("FuncID", FuncID); +} + +void CVSymbolDumperImpl::visitRegRelativeSym(SymbolKind Kind, + RegRelativeSym &RegRel) { + DictScope S(W, "RegRelativeSym"); + + W.printHex("Offset", RegRel.Header.Offset); + CVTD.printTypeIndex("Type", RegRel.Header.Type); + W.printHex("Register", RegRel.Header.Register); + W.printString("VarName", RegRel.Name); +} + +void CVSymbolDumperImpl::visitThreadLocalDataSym(SymbolKind Kind, + ThreadLocalDataSym &Data) { + DictScope S(W, "ThreadLocalDataSym"); + + StringRef LinkageName; + if (ObjDelegate) { + ObjDelegate->printRelocatedField("DataOffset", Data.getRelocationOffset(), + Data.Header.DataOffset, &LinkageName); + } + CVTD.printTypeIndex("Type", Data.Header.Type); + W.printString("DisplayName", Data.Name); + if (!LinkageName.empty()) + W.printString("LinkageName", LinkageName); +} + +void CVSymbolDumperImpl::visitUDTSym(SymbolKind Kind, UDTSym &UDT) { + DictScope S(W, "UDT"); + CVTD.printTypeIndex("Type", UDT.Header.Type); + W.printString("UDTName", UDT.Name); +} + +void CVSymbolDumperImpl::visitUnknownSymbol(SymbolKind Kind, + ArrayRef<uint8_t> Data) { + DictScope S(W, "UnknownSym"); + W.printEnum("Kind", uint16_t(Kind), getSymbolTypeNames()); + W.printNumber("Length", uint32_t(Data.size())); +} + +bool CVSymbolDumper::dump(const CVRecord<SymbolKind> &Record) { + CVSymbolDumperImpl Dumper(CVTD, ObjDelegate.get(), W, PrintRecordBytes); + Dumper.visitSymbolRecord(Record); + return !Dumper.hadError(); +} + +bool CVSymbolDumper::dump(const CVSymbolArray &Symbols) { + CVSymbolDumperImpl Dumper(CVTD, ObjDelegate.get(), W, PrintRecordBytes); + Dumper.visitSymbolStream(Symbols); + return !Dumper.hadError(); +} diff --git a/lib/DebugInfo/CodeView/TypeDumper.cpp b/lib/DebugInfo/CodeView/TypeDumper.cpp new file mode 100644 index 000000000000..345e2a49888c --- /dev/null +++ b/lib/DebugInfo/CodeView/TypeDumper.cpp @@ -0,0 +1,696 @@ +//===-- TypeDumper.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/TypeDumper.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/ByteStream.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +/// 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 EnumEntry<SimpleTypeKind> 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}, +}; + +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), +}; + +#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" + case LF_FIELDLIST: + return "FieldList"; + default: + break; + } + return "UnknownLeaf"; +} + +Error CVTypeDumper::visitTypeBegin(const CVRecord<TypeLeafKind> &Record) { + // Reset Name to the empty string. If the visitor sets it, we know it. + Name = ""; + + W->startLine() << getLeafTypeName(Record.Type) << " (" + << HexNumber(getNextTypeIndex()) << ") {\n"; + W->indent(); + W->printEnum("TypeLeafKind", unsigned(Record.Type), + makeArrayRef(LeafTypeNames)); + return Error::success(); +} + +Error CVTypeDumper::visitTypeEnd(const CVRecord<TypeLeafKind> &Record) { + if (Record.Type == LF_FIELDLIST) + Name = "<field list>"; + + // Always record some name for every type, even if Name is empty. CVUDTNames + // is indexed by type index, and must have one entry for every type. + recordType(Name); + + if (PrintRecordBytes) + W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.Data)); + + W->unindent(); + W->startLine() << "}\n"; + return Error::success(); +} + +Error CVTypeDumper::visitStringId(StringIdRecord &String) { + printTypeIndex("Id", String.getId()); + W->printString("StringData", String.getString()); + // Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE. + Name = String.getString(); + return Error::success(); +} + +Error CVTypeDumper::visitArgList(ArgListRecord &Args) { + auto Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + W->printNumber("NumArgs", Size); + ListScope Arguments(*W, "Arguments"); + SmallString<256> TypeName("("); + for (uint32_t I = 0; I < Size; ++I) { + printTypeIndex("ArgType", Indices[I]); + StringRef ArgTypeName = getTypeName(Indices[I]); + TypeName.append(ArgTypeName); + if (I + 1 != Size) + TypeName.append(", "); + } + TypeName.push_back(')'); + Name = saveName(TypeName); + return Error::success(); +} + +Error CVTypeDumper::visitClass(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()); + Name = Class.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitUnion(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()); + Name = Union.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitEnum(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()); + Name = Enum.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitArray(ArrayRecord &AT) { + printTypeIndex("ElementType", AT.getElementType()); + printTypeIndex("IndexType", AT.getIndexType()); + W->printNumber("SizeOf", AT.getSize()); + W->printString("Name", AT.getName()); + Name = AT.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitVFTable(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); + Name = VFT.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitMemberFuncId(MemberFuncIdRecord &Id) { + printTypeIndex("ClassType", Id.getClassType()); + printTypeIndex("FunctionType", Id.getFunctionType()); + W->printString("Name", Id.getName()); + Name = Id.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitProcedure(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()); + + StringRef ReturnTypeName = getTypeName(Proc.getReturnType()); + StringRef ArgListTypeName = getTypeName(Proc.getArgumentList()); + SmallString<256> TypeName(ReturnTypeName); + TypeName.push_back(' '); + TypeName.append(ArgListTypeName); + Name = saveName(TypeName); + return Error::success(); +} + +Error CVTypeDumper::visitMemberFunction(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()); + + 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 = saveName(TypeName); + return Error::success(); +} + +Error CVTypeDumper::visitMethodOverloadList( + MethodOverloadListRecord &MethodList) { + for (auto &M : MethodList.getMethods()) { + ListScope S(*W, "Method"); + printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions()); + printTypeIndex("Type", M.getType()); + if (M.isIntroducingVirtual()) + W->printHex("VFTableOffset", M.getVFTableOffset()); + } + return Error::success(); +} + +Error CVTypeDumper::visitFuncId(FuncIdRecord &Func) { + printTypeIndex("ParentScope", Func.getParentScope()); + printTypeIndex("FunctionType", Func.getFunctionType()); + W->printString("Name", Func.getName()); + Name = Func.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitTypeServer2(TypeServer2Record &TS) { + W->printBinary("Signature", TS.getGuid()); + W->printNumber("Age", TS.getAge()); + W->printString("Name", TS.getName()); + Name = TS.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitPointer(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)); + + StringRef PointeeName = getTypeName(Ptr.getReferentType()); + StringRef ClassName = getTypeName(MI.getContainingType()); + SmallString<256> TypeName(PointeeName); + TypeName.push_back(' '); + TypeName.append(ClassName); + TypeName.append("::*"); + Name = saveName(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 = saveName(TypeName); + } + return Error::success(); +} + +Error CVTypeDumper::visitModifier(ModifierRecord &Mod) { + uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); + printTypeIndex("ModifiedType", Mod.getModifiedType()); + W->printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames)); + + 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 = saveName(TypeName); + return Error::success(); +} + +Error CVTypeDumper::visitBitField(BitFieldRecord &BitField) { + printTypeIndex("Type", BitField.getType()); + W->printNumber("BitSize", BitField.getBitSize()); + W->printNumber("BitOffset", BitField.getBitOffset()); + return Error::success(); +} + +Error CVTypeDumper::visitVFTableShape(VFTableShapeRecord &Shape) { + W->printNumber("VFEntryCount", Shape.getEntryCount()); + return Error::success(); +} + +Error CVTypeDumper::visitUdtSourceLine(UdtSourceLineRecord &Line) { + printTypeIndex("UDT", Line.getUDT()); + printTypeIndex("SourceFile", Line.getSourceFile()); + W->printNumber("LineNumber", Line.getLineNumber()); + return Error::success(); +} + +Error CVTypeDumper::visitUdtModSourceLine(UdtModSourceLineRecord &Line) { + printTypeIndex("UDT", Line.getUDT()); + printTypeIndex("SourceFile", Line.getSourceFile()); + W->printNumber("LineNumber", Line.getLineNumber()); + W->printNumber("Module", Line.getModule()); + return Error::success(); +} + +Error CVTypeDumper::visitBuildInfo(BuildInfoRecord &Args) { + W->printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size())); + + ListScope Arguments(*W, "Arguments"); + for (auto Arg : Args.getArgs()) { + printTypeIndex("ArgType", Arg); + } + return Error::success(); +} + +void CVTypeDumper::printMemberAttributes(MemberAttributes Attrs) { + return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(), + Attrs.getFlags()); +} + +void CVTypeDumper::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 CVTypeDumper::visitUnknownMember(const CVRecord<TypeLeafKind> &Record) { + W->printHex("UnknownMember", unsigned(Record.Type)); + return Error::success(); +} + +Error CVTypeDumper::visitUnknownType(const CVRecord<TypeLeafKind> &Record) { + DictScope S(*W, "UnknownType"); + W->printEnum("Kind", uint16_t(Record.Type), makeArrayRef(LeafTypeNames)); + W->printNumber("Length", uint32_t(Record.Data.size())); + return Error::success(); +} + +Error CVTypeDumper::visitNestedType(NestedTypeRecord &Nested) { + DictScope S(*W, "NestedType"); + printTypeIndex("Type", Nested.getNestedType()); + W->printString("Name", Nested.getName()); + Name = Nested.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitOneMethod(OneMethodRecord &Method) { + DictScope S(*W, "OneMethod"); + MethodKind K = Method.getKind(); + 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()); + Name = Method.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitOverloadedMethod(OverloadedMethodRecord &Method) { + DictScope S(*W, "OverloadedMethod"); + W->printHex("MethodCount", Method.getNumOverloads()); + printTypeIndex("MethodListIndex", Method.getMethodList()); + W->printString("Name", Method.getName()); + Name = Method.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitDataMember(DataMemberRecord &Field) { + DictScope S(*W, "DataMember"); + printMemberAttributes(Field.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("Type", Field.getType()); + W->printHex("FieldOffset", Field.getFieldOffset()); + W->printString("Name", Field.getName()); + Name = Field.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitStaticDataMember(StaticDataMemberRecord &Field) { + DictScope S(*W, "StaticDataMember"); + printMemberAttributes(Field.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("Type", Field.getType()); + W->printString("Name", Field.getName()); + Name = Field.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitVFPtr(VFPtrRecord &VFTable) { + DictScope S(*W, "VFPtr"); + printTypeIndex("Type", VFTable.getType()); + return Error::success(); +} + +Error CVTypeDumper::visitEnumerator(EnumeratorRecord &Enum) { + DictScope S(*W, "Enumerator"); + printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + W->printNumber("EnumValue", Enum.getValue()); + W->printString("Name", Enum.getName()); + Name = Enum.getName(); + return Error::success(); +} + +Error CVTypeDumper::visitBaseClass(BaseClassRecord &Base) { + DictScope S(*W, "BaseClass"); + printMemberAttributes(Base.getAccess(), MethodKind::Vanilla, + MethodOptions::None); + printTypeIndex("BaseType", Base.getBaseType()); + W->printHex("BaseOffset", Base.getBaseOffset()); + return Error::success(); +} + +Error CVTypeDumper::visitVirtualBaseClass(VirtualBaseClassRecord &Base) { + DictScope S(*W, "VirtualBaseClass"); + 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 CVTypeDumper::visitListContinuation(ListContinuationRecord &Cont) { + DictScope S(*W, "ListContinuation"); + printTypeIndex("ContinuationIndex", Cont.getContinuationIndex()); + return Error::success(); +} + +StringRef CVTypeDumper::getTypeName(TypeIndex TI) { + if (TI.isNoneType()) + return "<no type>"; + + if (TI.isSimple()) { + // This is a simple type. + for (const auto &SimpleTypeName : SimpleTypeNames) { + if (SimpleTypeName.Value == TI.getSimpleKind()) { + if (TI.getSimpleMode() == SimpleTypeMode::Direct) + return SimpleTypeName.Name.drop_back(1); + // Otherwise, this is a pointer type. We gloss over the distinction + // between near, far, 64, 32, etc, and just give a pointer type. + return SimpleTypeName.Name; + } + } + return "<unknown simple type>"; + } + + // User-defined type. + StringRef UDTName; + unsigned UDTIndex = TI.getIndex() - 0x1000; + if (UDTIndex < CVUDTNames.size()) + return CVUDTNames[UDTIndex]; + + return "<unknown UDT>"; +} + +void CVTypeDumper::printTypeIndex(StringRef FieldName, TypeIndex TI) { + StringRef TypeName; + if (!TI.isNoneType()) + TypeName = getTypeName(TI); + if (!TypeName.empty()) + W->printHex(FieldName, TypeName, TI.getIndex()); + else + W->printHex(FieldName, TI.getIndex()); +} + +Error CVTypeDumper::dump(const CVRecord<TypeLeafKind> &Record) { + assert(W && "printer should not be null"); + CVTypeVisitor Visitor(*this); + + if (auto EC = Visitor.visitTypeRecord(Record)) + return EC; + return Error::success(); +} + +Error CVTypeDumper::dump(const CVTypeArray &Types) { + assert(W && "printer should not be null"); + CVTypeVisitor Visitor(*this); + if (auto EC = Visitor.visitTypeStream(Types)) + return EC; + return Error::success(); +} + +Error CVTypeDumper::dump(ArrayRef<uint8_t> Data) { + ByteStream<> Stream(Data); + CVTypeArray Types; + StreamReader Reader(Stream); + if (auto EC = Reader.readArray(Types, Reader.getLength())) + return EC; + + return dump(Types); +} + +void CVTypeDumper::setPrinter(ScopedPrinter *P) { + static ScopedPrinter NullP(llvm::nulls()); + W = P ? P : &NullP; +} diff --git a/lib/DebugInfo/CodeView/TypeRecord.cpp b/lib/DebugInfo/CodeView/TypeRecord.cpp new file mode 100644 index 000000000000..f63371e8c14f --- /dev/null +++ b/lib/DebugInfo/CodeView/TypeRecord.cpp @@ -0,0 +1,572 @@ +//===-- TypeRecord.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/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" + +using namespace llvm; +using namespace llvm::codeview; + +//===----------------------------------------------------------------------===// +// Type record deserialization +//===----------------------------------------------------------------------===// + +ErrorOr<MemberPointerInfo> +MemberPointerInfo::deserialize(ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + if (auto EC = consumeObject(Data, L)) + return EC; + + TypeIndex T = L->ClassType; + uint16_t R = L->Representation; + PointerToMemberRepresentation PMR = + static_cast<PointerToMemberRepresentation>(R); + return MemberPointerInfo(T, PMR); +} + +ErrorOr<ModifierRecord> ModifierRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + if (auto EC = consumeObject(Data, L)) + return EC; + + TypeIndex M = L->ModifiedType; + uint16_t O = L->Modifiers; + ModifierOptions MO = static_cast<ModifierOptions>(O); + return ModifierRecord(M, MO); +} + +ErrorOr<ProcedureRecord> ProcedureRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + if (auto EC = consumeObject(Data, L)) + return EC; + return ProcedureRecord(L->ReturnType, L->CallConv, L->Options, + L->NumParameters, L->ArgListType); +} + +ErrorOr<MemberFunctionRecord> +MemberFunctionRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + CV_DESERIALIZE(Data, L); + return MemberFunctionRecord(L->ReturnType, L->ClassType, L->ThisType, + L->CallConv, L->Options, L->NumParameters, + L->ArgListType, L->ThisAdjustment); +} + +ErrorOr<MemberFuncIdRecord> +MemberFuncIdRecord::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return MemberFuncIdRecord(L->ClassType, L->FunctionType, Name); +} + +ErrorOr<ArgListRecord> ArgListRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + if (Kind != TypeRecordKind::StringList && Kind != TypeRecordKind::ArgList) + return std::make_error_code(std::errc::illegal_byte_sequence); + + const Layout *L = nullptr; + ArrayRef<TypeIndex> Indices; + CV_DESERIALIZE(Data, L, CV_ARRAY_FIELD_N(Indices, L->NumArgs)); + return ArgListRecord(Kind, Indices); +} + +ErrorOr<PointerRecord> PointerRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + if (auto EC = consumeObject(Data, L)) + return EC; + + PointerKind PtrKind = L->getPtrKind(); + PointerMode Mode = L->getPtrMode(); + uint32_t Opts = L->Attrs; + PointerOptions Options = static_cast<PointerOptions>(Opts); + uint8_t Size = L->getPtrSize(); + + if (L->isPointerToMember()) { + auto E = MemberPointerInfo::deserialize(Data); + if (E.getError()) + return std::make_error_code(std::errc::illegal_byte_sequence); + return PointerRecord(L->PointeeType, PtrKind, Mode, Options, Size, *E); + } + + return PointerRecord(L->PointeeType, PtrKind, Mode, Options, Size); +} + +ErrorOr<NestedTypeRecord> +NestedTypeRecord::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return NestedTypeRecord(L->Type, Name); +} + +ErrorOr<ArrayRecord> ArrayRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + uint64_t Size; + StringRef Name; + CV_DESERIALIZE(Data, L, CV_NUMERIC_FIELD(Size), Name); + return ArrayRecord(L->ElementType, L->IndexType, Size, Name); +} + +ErrorOr<ClassRecord> ClassRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + uint64_t Size = 0; + StringRef Name; + StringRef UniqueName; + uint16_t Props; + const Layout *L = nullptr; + + CV_DESERIALIZE(Data, L, CV_NUMERIC_FIELD(Size), Name, + CV_CONDITIONAL_FIELD(UniqueName, L->hasUniqueName())); + + Props = L->Properties; + uint16_t WrtValue = (Props & WinRTKindMask) >> WinRTKindShift; + WindowsRTClassKind WRT = static_cast<WindowsRTClassKind>(WrtValue); + uint16_t HfaMask = (Props & HfaKindMask) >> HfaKindShift; + HfaKind Hfa = static_cast<HfaKind>(HfaMask); + + ClassOptions Options = static_cast<ClassOptions>(Props); + return ClassRecord(Kind, L->MemberCount, Options, Hfa, WRT, L->FieldList, + L->DerivedFrom, L->VShape, Size, Name, UniqueName); +} + +ErrorOr<UnionRecord> UnionRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + uint64_t Size = 0; + StringRef Name; + StringRef UniqueName; + uint16_t Props; + + const Layout *L = nullptr; + CV_DESERIALIZE(Data, L, CV_NUMERIC_FIELD(Size), Name, + CV_CONDITIONAL_FIELD(UniqueName, L->hasUniqueName())); + + Props = L->Properties; + + uint16_t HfaMask = (Props & HfaKindMask) >> HfaKindShift; + HfaKind Hfa = static_cast<HfaKind>(HfaMask); + ClassOptions Options = static_cast<ClassOptions>(Props); + return UnionRecord(L->MemberCount, Options, Hfa, L->FieldList, Size, Name, + UniqueName); +} + +ErrorOr<EnumRecord> EnumRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + StringRef UniqueName; + CV_DESERIALIZE(Data, L, Name, + CV_CONDITIONAL_FIELD(UniqueName, L->hasUniqueName())); + + uint16_t P = L->Properties; + ClassOptions Options = static_cast<ClassOptions>(P); + return EnumRecord(L->NumEnumerators, Options, L->FieldListType, Name, + UniqueName, L->UnderlyingType); +} + +ErrorOr<BitFieldRecord> BitFieldRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + CV_DESERIALIZE(Data, L); + return BitFieldRecord(L->Type, L->BitSize, L->BitOffset); +} + +ErrorOr<VFTableShapeRecord> +VFTableShapeRecord::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + if (auto EC = consumeObject(Data, L)) + return EC; + + std::vector<VFTableSlotKind> Slots; + uint16_t Count = L->VFEntryCount; + while (Count > 0) { + if (Data.empty()) + return std::make_error_code(std::errc::illegal_byte_sequence); + + // Process up to 2 nibbles at a time (if there are at least 2 remaining) + uint8_t Value = Data[0] & 0x0F; + Slots.push_back(static_cast<VFTableSlotKind>(Value)); + if (--Count > 0) { + Value = (Data[0] & 0xF0) >> 4; + Slots.push_back(static_cast<VFTableSlotKind>(Value)); + --Count; + } + Data = Data.slice(1); + } + + return VFTableShapeRecord(Slots); +} + +ErrorOr<TypeServer2Record> +TypeServer2Record::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return TypeServer2Record(StringRef(L->Guid, 16), L->Age, Name); +} + +ErrorOr<StringIdRecord> StringIdRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return StringIdRecord(L->id, Name); +} + +ErrorOr<FuncIdRecord> FuncIdRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return FuncIdRecord(L->ParentScope, L->FunctionType, Name); +} + +ErrorOr<UdtSourceLineRecord> +UdtSourceLineRecord::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + CV_DESERIALIZE(Data, L); + return UdtSourceLineRecord(L->UDT, L->SourceFile, L->LineNumber); +} + +ErrorOr<BuildInfoRecord> BuildInfoRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + ArrayRef<TypeIndex> Indices; + CV_DESERIALIZE(Data, L, CV_ARRAY_FIELD_N(Indices, L->NumArgs)); + return BuildInfoRecord(Indices); +} + +ErrorOr<VFTableRecord> VFTableRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + std::vector<StringRef> Names; + CV_DESERIALIZE(Data, L, Name, CV_ARRAY_FIELD_TAIL(Names)); + return VFTableRecord(L->CompleteClass, L->OverriddenVFTable, L->VFPtrOffset, + Name, Names); +} + +ErrorOr<OneMethodRecord> OneMethodRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + int32_t VFTableOffset = -1; + + CV_DESERIALIZE(Data, L, CV_CONDITIONAL_FIELD(VFTableOffset, + L->Attrs.isIntroducedVirtual()), + Name); + + MethodOptions Options = L->Attrs.getFlags(); + MethodKind MethKind = L->Attrs.getMethodKind(); + MemberAccess Access = L->Attrs.getAccess(); + OneMethodRecord Method(L->Type, MethKind, Options, Access, VFTableOffset, + Name); + // Validate the vftable offset. + if (Method.isIntroducingVirtual() && Method.getVFTableOffset() < 0) + return std::make_error_code(std::errc::illegal_byte_sequence); + return Method; +} + +ErrorOr<MethodOverloadListRecord> +MethodOverloadListRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + std::vector<OneMethodRecord> Methods; + while (!Data.empty()) { + const Layout *L = nullptr; + int32_t VFTableOffset = -1; + CV_DESERIALIZE(Data, L, CV_CONDITIONAL_FIELD( + VFTableOffset, L->Attrs.isIntroducedVirtual())); + + MethodOptions Options = L->Attrs.getFlags(); + MethodKind MethKind = L->Attrs.getMethodKind(); + MemberAccess Access = L->Attrs.getAccess(); + + Methods.emplace_back(L->Type, MethKind, Options, Access, VFTableOffset, + StringRef()); + + // Validate the vftable offset. + auto &Method = Methods.back(); + if (Method.isIntroducingVirtual() && Method.getVFTableOffset() < 0) + return std::make_error_code(std::errc::illegal_byte_sequence); + } + return MethodOverloadListRecord(Methods); +} + +ErrorOr<OverloadedMethodRecord> +OverloadedMethodRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return OverloadedMethodRecord(L->MethodCount, L->MethList, Name); +} + +ErrorOr<DataMemberRecord> +DataMemberRecord::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + uint64_t Offset; + StringRef Name; + CV_DESERIALIZE(Data, L, CV_NUMERIC_FIELD(Offset), Name); + return DataMemberRecord(L->Attrs.getAccess(), L->Type, Offset, Name); +} + +ErrorOr<StaticDataMemberRecord> +StaticDataMemberRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, L, Name); + return StaticDataMemberRecord(L->Attrs.getAccess(), L->Type, Name); +} + +ErrorOr<EnumeratorRecord> +EnumeratorRecord::deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + APSInt Value; + StringRef Name; + CV_DESERIALIZE(Data, L, Value, Name); + return EnumeratorRecord(L->Attrs.getAccess(), Value, Name); +} + +ErrorOr<VFPtrRecord> VFPtrRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + if (auto EC = consumeObject(Data, L)) + return EC; + return VFPtrRecord(L->Type); +} + +ErrorOr<BaseClassRecord> BaseClassRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + uint64_t Offset; + CV_DESERIALIZE(Data, L, CV_NUMERIC_FIELD(Offset)); + return BaseClassRecord(L->Attrs.getAccess(), L->BaseType, Offset); +} + +ErrorOr<VirtualBaseClassRecord> +VirtualBaseClassRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + uint64_t Offset; + uint64_t Index; + CV_DESERIALIZE(Data, L, CV_NUMERIC_FIELD(Offset), CV_NUMERIC_FIELD(Index)); + return VirtualBaseClassRecord(L->Attrs.getAccess(), L->BaseType, L->VBPtrType, + Offset, Index); +} + +ErrorOr<ListContinuationRecord> +ListContinuationRecord::deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + CV_DESERIALIZE(Data, L); + return ListContinuationRecord(L->ContinuationIndex); +} + +//===----------------------------------------------------------------------===// +// Type index remapping +//===----------------------------------------------------------------------===// + +static bool remapIndex(ArrayRef<TypeIndex> IndexMap, TypeIndex &Idx) { + // Simple types are unchanged. + if (Idx.isSimple()) + return true; + unsigned MapPos = Idx.getIndex() - TypeIndex::FirstNonSimpleIndex; + if (MapPos < IndexMap.size()) { + Idx = IndexMap[MapPos]; + return true; + } + + // This type index is invalid. Remap this to "not translated by cvpack", + // and return failure. + Idx = TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct); + return false; +} + +bool ModifierRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, ModifiedType); +} + +bool ProcedureRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ReturnType); + Success &= remapIndex(IndexMap, ArgumentList); + return Success; +} + +bool MemberFunctionRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ReturnType); + Success &= remapIndex(IndexMap, ClassType); + Success &= remapIndex(IndexMap, ThisType); + Success &= remapIndex(IndexMap, ArgumentList); + return Success; +} + +bool MemberFuncIdRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ClassType); + Success &= remapIndex(IndexMap, FunctionType); + return Success; +} + +bool ArgListRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + for (TypeIndex &Str : StringIndices) + Success &= remapIndex(IndexMap, Str); + return Success; +} + +bool MemberPointerInfo::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, ContainingType); +} + +bool PointerRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ReferentType); + if (isPointerToMember()) + Success &= MemberInfo.remapTypeIndices(IndexMap); + return Success; +} + +bool NestedTypeRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool ArrayRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ElementType); + Success &= remapIndex(IndexMap, IndexType); + return Success; +} + +bool TagRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, FieldList); +} + +bool ClassRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= TagRecord::remapTypeIndices(IndexMap); + Success &= remapIndex(IndexMap, DerivationList); + Success &= remapIndex(IndexMap, VTableShape); + return Success; +} + +bool EnumRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= TagRecord::remapTypeIndices(IndexMap); + Success &= remapIndex(IndexMap, UnderlyingType); + return Success; +} + +bool BitFieldRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool VFTableShapeRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return true; +} + +bool TypeServer2Record::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return true; +} + +bool StringIdRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Id); +} + +bool FuncIdRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ParentScope); + Success &= remapIndex(IndexMap, FunctionType); + return Success; +} + +bool UdtSourceLineRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, UDT); + Success &= remapIndex(IndexMap, SourceFile); + return Success; +} + +bool UdtModSourceLineRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, UDT); + Success &= remapIndex(IndexMap, SourceFile); + return Success; +} + +bool BuildInfoRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + for (TypeIndex &Arg : ArgIndices) + Success &= remapIndex(IndexMap, Arg); + return Success; +} + +bool VFTableRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, CompleteClass); + Success &= remapIndex(IndexMap, OverriddenVFTable); + return Success; +} + +bool OneMethodRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, Type); + return Success; +} + +bool MethodOverloadListRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + for (OneMethodRecord &Meth : Methods) + if ((Success = Meth.remapTypeIndices(IndexMap))) + return Success; + return Success; +} + +bool OverloadedMethodRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, MethodList); +} + +bool DataMemberRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool StaticDataMemberRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool EnumeratorRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return true; +} + +bool VFPtrRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool BaseClassRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool VirtualBaseClassRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, BaseType); + Success &= remapIndex(IndexMap, VBPtrType); + return Success; +} + +bool ListContinuationRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, ContinuationIndex); +} diff --git a/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp b/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp index cbf464fd7668..112612cc85ea 100644 --- a/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp +++ b/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp @@ -12,8 +12,8 @@ using namespace llvm; using namespace codeview; -TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind) : Stream(Buffer), - Writer(Stream) { +TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind) + : Stream(Buffer), Writer(Stream) { writeTypeRecordKind(Kind); } @@ -60,50 +60,50 @@ void TypeRecordBuilder::writeEncodedInteger(int64_t Value) { void TypeRecordBuilder::writeEncodedSignedInteger(int64_t Value) { if (Value >= std::numeric_limits<int8_t>::min() && Value <= std::numeric_limits<int8_t>::max()) { - writeUInt16(static_cast<uint16_t>(TypeRecordKind::SByte)); + writeUInt16(LF_CHAR); writeInt16(static_cast<int8_t>(Value)); } else if (Value >= std::numeric_limits<int16_t>::min() && Value <= std::numeric_limits<int16_t>::max()) { - writeUInt16(static_cast<uint16_t>(TypeRecordKind::Int16)); + writeUInt16(LF_SHORT); writeInt16(static_cast<int16_t>(Value)); } else if (Value >= std::numeric_limits<int32_t>::min() && Value <= std::numeric_limits<int32_t>::max()) { - writeUInt16(static_cast<uint32_t>(TypeRecordKind::Int32)); + writeUInt16(LF_LONG); writeInt32(static_cast<int32_t>(Value)); } else { - writeUInt16(static_cast<uint16_t>(TypeRecordKind::Int64)); + writeUInt16(LF_QUADWORD); writeInt64(Value); } } void TypeRecordBuilder::writeEncodedUnsignedInteger(uint64_t Value) { - if (Value < static_cast<uint16_t>(TypeRecordKind::SByte)) { + if (Value < LF_CHAR) { writeUInt16(static_cast<uint16_t>(Value)); } else if (Value <= std::numeric_limits<uint16_t>::max()) { - writeUInt16(static_cast<uint16_t>(TypeRecordKind::UInt16)); + writeUInt16(LF_USHORT); writeUInt16(static_cast<uint16_t>(Value)); } else if (Value <= std::numeric_limits<uint32_t>::max()) { - writeUInt16(static_cast<uint16_t>(TypeRecordKind::UInt32)); + writeUInt16(LF_ULONG); writeUInt32(static_cast<uint32_t>(Value)); } else { - writeUInt16(static_cast<uint16_t>(TypeRecordKind::UInt64)); + writeUInt16(LF_UQUADWORD); writeUInt64(Value); } } -void TypeRecordBuilder::writeNullTerminatedString(const char *Value) { - assert(Value != nullptr); - - size_t Length = strlen(Value); - Stream.write(Value, Length); - writeUInt8(0); -} - void TypeRecordBuilder::writeNullTerminatedString(StringRef Value) { + // Microsoft's linker seems to have trouble with symbol names longer than + // 0xffd8 bytes. + Value = Value.substr(0, 0xffd8); Stream.write(Value.data(), Value.size()); writeUInt8(0); } +void TypeRecordBuilder::writeGuid(StringRef Guid) { + assert(Guid.size() == 16); + Stream.write(Guid.data(), 16); +} + void TypeRecordBuilder::writeTypeIndex(TypeIndex TypeInd) { writeUInt32(TypeInd.getIndex()); } diff --git a/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp new file mode 100644 index 000000000000..ebfda2462be1 --- /dev/null +++ b/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -0,0 +1,149 @@ +//===-- 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/FieldListRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/StreamRef.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.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. +class TypeStreamMerger : public TypeVisitorCallbacks { +public: + TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) { + assert(!hadError()); + } + +/// TypeVisitorCallbacks overrides. +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + Error visit##Name(Name##Record &Record) override; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + + Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) override; + + Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override; + Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override; + + Error visitFieldListEnd(const CVRecord<TypeLeafKind> &Record) override; + + bool mergeStream(const CVTypeArray &Types); + +private: + bool hadError() { return FoundBadTypeIndex; } + + bool FoundBadTypeIndex = false; + + FieldListRecordBuilder FieldBuilder; + + TypeTableBuilder &DestStream; + + size_t BeginIndexMapSize = 0; + + /// Map from source type index to destination type index. Indexed by source + /// type index minus 0x1000. + SmallVector<TypeIndex, 0> IndexMap; +}; + +} // end anonymous namespace + +Error TypeStreamMerger::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) { + BeginIndexMapSize = IndexMap.size(); + return Error::success(); +} + +Error TypeStreamMerger::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) { + assert(IndexMap.size() == BeginIndexMapSize + 1); + return Error::success(); +} + +Error TypeStreamMerger::visitFieldListEnd(const CVRecord<TypeLeafKind> &Rec) { + IndexMap.push_back(DestStream.writeFieldList(FieldBuilder)); + FieldBuilder.reset(); + return Error::success(); +} + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + Error TypeStreamMerger::visit##Name(Name##Record &Record) { \ + FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \ + IndexMap.push_back(DestStream.write##Name(Record)); \ + return Error::success(); \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + Error TypeStreamMerger::visit##Name(Name##Record &Record) { \ + FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \ + FieldBuilder.write##Name(Record); \ + return Error::success(); \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + +Error TypeStreamMerger::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) { + // We failed to translate a type. Translate this index as "not translated". + IndexMap.push_back( + TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct)); + return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); +} + +bool TypeStreamMerger::mergeStream(const CVTypeArray &Types) { + assert(IndexMap.empty()); + CVTypeVisitor Visitor(*this); + if (auto EC = Visitor.visitTypeStream(Types)) { + consumeError(std::move(EC)); + return false; + } + IndexMap.clear(); + return !hadError(); +} + +bool llvm::codeview::mergeTypeStreams(TypeTableBuilder &DestStream, + const CVTypeArray &Types) { + return TypeStreamMerger(DestStream).mergeStream(Types); +} diff --git a/lib/DebugInfo/CodeView/TypeTableBuilder.cpp b/lib/DebugInfo/CodeView/TypeTableBuilder.cpp index 4af5dcaf7228..647538ee8ceb 100644 --- a/lib/DebugInfo/CodeView/TypeTableBuilder.cpp +++ b/lib/DebugInfo/CodeView/TypeTableBuilder.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" #include "llvm/DebugInfo/CodeView/MethodListRecordBuilder.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" @@ -18,42 +17,21 @@ using namespace llvm; using namespace codeview; -namespace { - -const int PointerKindShift = 0; -const int PointerModeShift = 5; -const int PointerSizeShift = 13; - -const int ClassHfaKindShift = 11; -const int ClassWindowsRTClassKindShift = 14; - -void writePointerBase(TypeRecordBuilder &Builder, - const PointerRecordBase &Record) { - Builder.writeTypeIndex(Record.getReferentType()); - uint32_t flags = - static_cast<uint32_t>(Record.getOptions()) | - (Record.getSize() << PointerSizeShift) | - (static_cast<uint32_t>(Record.getMode()) << PointerModeShift) | - (static_cast<uint32_t>(Record.getPointerKind()) << PointerKindShift); - Builder.writeUInt32(flags); -} -} - TypeTableBuilder::TypeTableBuilder() {} TypeTableBuilder::~TypeTableBuilder() {} TypeIndex TypeTableBuilder::writeModifier(const ModifierRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Modifier); + TypeRecordBuilder Builder(Record.getKind()); Builder.writeTypeIndex(Record.getModifiedType()); - Builder.writeUInt16(static_cast<uint16_t>(Record.getOptions())); + Builder.writeUInt16(static_cast<uint16_t>(Record.getModifiers())); return writeRecord(Builder); } TypeIndex TypeTableBuilder::writeProcedure(const ProcedureRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Procedure); + TypeRecordBuilder Builder(Record.getKind()); Builder.writeTypeIndex(Record.getReturnType()); Builder.writeUInt8(static_cast<uint8_t>(Record.getCallConv())); @@ -66,7 +44,7 @@ TypeIndex TypeTableBuilder::writeProcedure(const ProcedureRecord &Record) { TypeIndex TypeTableBuilder::writeMemberFunction(const MemberFunctionRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::MemberFunction); + TypeRecordBuilder Builder(Record.getKind()); Builder.writeTypeIndex(Record.getReturnType()); Builder.writeTypeIndex(Record.getClassType()); @@ -80,12 +58,11 @@ TypeTableBuilder::writeMemberFunction(const MemberFunctionRecord &Record) { return writeRecord(Builder); } -TypeIndex -TypeTableBuilder::writeArgumentList(const ArgumentListRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::ArgumentList); +TypeIndex TypeTableBuilder::writeArgList(const ArgListRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); - Builder.writeUInt32(Record.getArgumentTypes().size()); - for (TypeIndex TI : Record.getArgumentTypes()) { + Builder.writeUInt32(Record.getIndices().size()); + for (TypeIndex TI : Record.getIndices()) { Builder.writeTypeIndex(TI); } @@ -93,27 +70,28 @@ TypeTableBuilder::writeArgumentList(const ArgumentListRecord &Record) { } TypeIndex TypeTableBuilder::writePointer(const PointerRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Pointer); - - writePointerBase(Builder, Record); - - return writeRecord(Builder); -} - -TypeIndex -TypeTableBuilder::writePointerToMember(const PointerToMemberRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Pointer); + TypeRecordBuilder Builder(Record.getKind()); - writePointerBase(Builder, Record); + Builder.writeTypeIndex(Record.getReferentType()); + uint32_t flags = static_cast<uint32_t>(Record.getOptions()) | + (Record.getSize() << PointerRecord::PointerSizeShift) | + (static_cast<uint32_t>(Record.getMode()) + << PointerRecord::PointerModeShift) | + (static_cast<uint32_t>(Record.getPointerKind()) + << PointerRecord::PointerKindShift); + Builder.writeUInt32(flags); - Builder.writeTypeIndex(Record.getContainingType()); - Builder.writeUInt16(static_cast<uint16_t>(Record.getRepresentation())); + if (Record.isPointerToMember()) { + const MemberPointerInfo &M = Record.getMemberInfo(); + Builder.writeTypeIndex(M.getContainingType()); + Builder.writeUInt16(static_cast<uint16_t>(M.getRepresentation())); + } return writeRecord(Builder); } TypeIndex TypeTableBuilder::writeArray(const ArrayRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Array); + TypeRecordBuilder Builder(Record.getKind()); Builder.writeTypeIndex(Record.getElementType()); Builder.writeTypeIndex(Record.getIndexType()); @@ -123,28 +101,23 @@ TypeIndex TypeTableBuilder::writeArray(const ArrayRecord &Record) { return writeRecord(Builder); } -TypeIndex TypeTableBuilder::writeAggregate(const AggregateRecord &Record) { - assert((Record.getKind() == TypeRecordKind::Structure) || +TypeIndex TypeTableBuilder::writeClass(const ClassRecord &Record) { + assert((Record.getKind() == TypeRecordKind::Struct) || (Record.getKind() == TypeRecordKind::Class) || - (Record.getKind() == TypeRecordKind::Union)); + (Record.getKind() == TypeRecordKind::Interface)); TypeRecordBuilder Builder(Record.getKind()); Builder.writeUInt16(Record.getMemberCount()); uint16_t Flags = static_cast<uint16_t>(Record.getOptions()) | - (static_cast<uint16_t>(Record.getHfa()) << ClassHfaKindShift) | + (static_cast<uint16_t>(Record.getHfa()) << ClassRecord::HfaKindShift) | (static_cast<uint16_t>(Record.getWinRTKind()) - << ClassWindowsRTClassKindShift); + << ClassRecord::WinRTKindShift); Builder.writeUInt16(Flags); Builder.writeTypeIndex(Record.getFieldList()); - if (Record.getKind() != TypeRecordKind::Union) { - Builder.writeTypeIndex(Record.getDerivationList()); - Builder.writeTypeIndex(Record.getVTableShape()); - } else { - assert(Record.getDerivationList() == TypeIndex()); - assert(Record.getVTableShape() == TypeIndex()); - } + Builder.writeTypeIndex(Record.getDerivationList()); + Builder.writeTypeIndex(Record.getVTableShape()); Builder.writeEncodedUnsignedInteger(Record.getSize()); Builder.writeNullTerminatedString(Record.getName()); if ((Record.getOptions() & ClassOptions::HasUniqueName) != @@ -155,8 +128,25 @@ TypeIndex TypeTableBuilder::writeAggregate(const AggregateRecord &Record) { return writeRecord(Builder); } +TypeIndex TypeTableBuilder::writeUnion(const UnionRecord &Record) { + TypeRecordBuilder Builder(TypeRecordKind::Union); + Builder.writeUInt16(Record.getMemberCount()); + uint16_t Flags = + static_cast<uint16_t>(Record.getOptions()) | + (static_cast<uint16_t>(Record.getHfa()) << ClassRecord::HfaKindShift); + Builder.writeUInt16(Flags); + Builder.writeTypeIndex(Record.getFieldList()); + Builder.writeEncodedUnsignedInteger(Record.getSize()); + Builder.writeNullTerminatedString(Record.getName()); + if ((Record.getOptions() & ClassOptions::HasUniqueName) != + ClassOptions::None) { + Builder.writeNullTerminatedString(Record.getUniqueName()); + } + return writeRecord(Builder); +} + TypeIndex TypeTableBuilder::writeEnum(const EnumRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::Enum); + TypeRecordBuilder Builder(Record.getKind()); Builder.writeUInt16(Record.getMemberCount()); Builder.writeUInt16(static_cast<uint16_t>(Record.getOptions())); @@ -172,7 +162,7 @@ TypeIndex TypeTableBuilder::writeEnum(const EnumRecord &Record) { } TypeIndex TypeTableBuilder::writeBitField(const BitFieldRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::BitField); + TypeRecordBuilder Builder(Record.getKind()); Builder.writeTypeIndex(Record.getType()); Builder.writeUInt8(Record.getBitSize()); @@ -181,11 +171,11 @@ TypeIndex TypeTableBuilder::writeBitField(const BitFieldRecord &Record) { return writeRecord(Builder); } -TypeIndex TypeTableBuilder::writeVirtualTableShape( - const VirtualTableShapeRecord &Record) { - TypeRecordBuilder Builder(TypeRecordKind::VirtualTableShape); +TypeIndex +TypeTableBuilder::writeVFTableShape(const VFTableShapeRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); - ArrayRef<VirtualTableSlotKind> Slots = Record.getSlots(); + ArrayRef<VFTableSlotKind> Slots = Record.getSlots(); Builder.writeUInt16(Slots.size()); for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { @@ -199,19 +189,115 @@ TypeIndex TypeTableBuilder::writeVirtualTableShape( return writeRecord(Builder); } +TypeIndex +TypeTableBuilder::writeVFTable(const VFTableRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getCompleteClass()); + Builder.writeTypeIndex(Record.getOverriddenVTable()); + Builder.writeUInt32(Record.getVFPtrOffset()); + + // Sum up the lengths of the null-terminated names. + size_t NamesLen = Record.getName().size() + 1; + for (StringRef MethodName : Record.getMethodNames()) + NamesLen += MethodName.size() + 1; + + Builder.writeUInt32(NamesLen); + Builder.writeNullTerminatedString(Record.getName()); + for (StringRef MethodName : Record.getMethodNames()) + Builder.writeNullTerminatedString(MethodName); + + return writeRecord(Builder); +} + +TypeIndex TypeTableBuilder::writeStringId(const StringIdRecord &Record) { + TypeRecordBuilder Builder(TypeRecordKind::StringId); + Builder.writeTypeIndex(Record.getId()); + Builder.writeNullTerminatedString(Record.getString()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeUdtSourceLine(const UdtSourceLineRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getUDT()); + Builder.writeTypeIndex(Record.getSourceFile()); + Builder.writeUInt32(Record.getLineNumber()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeUdtModSourceLine(const UdtModSourceLineRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getUDT()); + Builder.writeTypeIndex(Record.getSourceFile()); + Builder.writeUInt32(Record.getLineNumber()); + Builder.writeUInt16(Record.getModule()); + return writeRecord(Builder); +} + +TypeIndex TypeTableBuilder::writeFuncId(const FuncIdRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getParentScope()); + Builder.writeTypeIndex(Record.getFunctionType()); + Builder.writeNullTerminatedString(Record.getName()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeMemberFuncId(const MemberFuncIdRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getClassType()); + Builder.writeTypeIndex(Record.getFunctionType()); + Builder.writeNullTerminatedString(Record.getName()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeBuildInfo(const BuildInfoRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + assert(Record.getArgs().size() <= UINT16_MAX); + Builder.writeUInt16(Record.getArgs().size()); + for (TypeIndex Arg : Record.getArgs()) + Builder.writeTypeIndex(Arg); + return writeRecord(Builder); +} + TypeIndex TypeTableBuilder::writeRecord(TypeRecordBuilder &Builder) { return writeRecord(Builder.str()); } TypeIndex TypeTableBuilder::writeFieldList(FieldListRecordBuilder &FieldList) { - // TODO: Split the list into multiple records if it's longer than 64KB, using - // a subrecord of TypeRecordKind::Index to chain the records together. - return writeRecord(FieldList.str()); + return FieldList.writeListRecord(*this); } -TypeIndex -TypeTableBuilder::writeMethodList(MethodListRecordBuilder &MethodList) { +TypeIndex TypeTableBuilder::writeMethodOverloadList( + const MethodOverloadListRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + for (const OneMethodRecord &Method : Record.getMethods()) { + uint16_t Flags = static_cast<uint16_t>(Method.getAccess()); + Flags |= static_cast<uint16_t>(Method.getKind()) + << MemberAttributes::MethodKindShift; + Flags |= static_cast<uint16_t>(Method.getOptions()); + Builder.writeUInt16(Flags); + Builder.writeUInt16(0); // padding + Builder.writeTypeIndex(Method.getType()); + if (Method.isIntroducingVirtual()) { + assert(Method.getVFTableOffset() >= 0); + Builder.writeInt32(Method.getVFTableOffset()); + } else { + assert(Method.getVFTableOffset() == -1); + } + } + // TODO: Split the list into multiple records if it's longer than 64KB, using // a subrecord of TypeRecordKind::Index to chain the records together. - return writeRecord(MethodList.str()); + return writeRecord(Builder); +} + +TypeIndex TypeTableBuilder::writeTypeServer2(const TypeServer2Record &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeGuid(Record.getGuid()); + Builder.writeUInt32(Record.getAge()); + Builder.writeNullTerminatedString(Record.getName()); + return writeRecord(Builder); } diff --git a/lib/DebugInfo/DWARF/DWARFContext.cpp b/lib/DebugInfo/DWARF/DWARFContext.cpp index a4195b75c47d..e8ea71b325ae 100644 --- a/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -13,8 +13,11 @@ #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/RelocVisitor.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Dwarf.h" +#include "llvm/Support/ELF.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -72,7 +75,7 @@ static void dumpAccelSection(raw_ostream &OS, StringRef Name, Accel.dump(OS); } -void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) { +void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH) { if (DumpType == DIDT_All || DumpType == DIDT_Abbrev) { OS << ".debug_abbrev contents:\n"; getDebugAbbrev()->dump(OS); @@ -125,6 +128,10 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) { 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) { @@ -355,7 +362,18 @@ const DWARFDebugFrame *DWARFContext::getDebugFrame() { // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html DataExtractor debugFrameData(getDebugFrameSection(), isLittleEndian(), getAddressSize()); - DebugFrame.reset(new DWARFDebugFrame()); + 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(); } @@ -575,8 +593,8 @@ DWARFContext::getInliningInfoForAddress(uint64_t Address, return InliningInfo; } -static bool consumeCompressedDebugSectionHeader(StringRef &data, - uint64_t &OriginalSize) { +static bool consumeCompressedGnuHeader(StringRef &data, + uint64_t &OriginalSize) { // Consume "ZLIB" prefix. if (!data.startswith("ZLIB")) return false; @@ -591,6 +609,50 @@ static bool consumeCompressedDebugSectionHeader(StringRef &data, return true; } +static bool consumeCompressedZLibHeader(StringRef &Data, uint64_t &OriginalSize, + bool IsLE, bool Is64Bit) { + using namespace ELF; + uint64_t HdrSize = Is64Bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr); + if (Data.size() < HdrSize) + return false; + + DataExtractor Extractor(Data, IsLE, 0); + uint32_t Offset = 0; + if (Extractor.getUnsigned(&Offset, Is64Bit ? sizeof(Elf64_Word) + : sizeof(Elf32_Word)) != + ELFCOMPRESS_ZLIB) + return false; + + // Skip Elf64_Chdr::ch_reserved field. + if (Is64Bit) + Offset += sizeof(Elf64_Word); + + OriginalSize = Extractor.getUnsigned(&Offset, Is64Bit ? sizeof(Elf64_Xword) + : sizeof(Elf32_Word)); + Data = Data.substr(HdrSize); + return true; +} + +static bool tryDecompress(StringRef &Name, StringRef &Data, + SmallString<32> &Out, bool ZLibStyle, bool IsLE, + bool Is64Bit) { + if (!zlib::isAvailable()) + return false; + + uint64_t OriginalSize; + bool Result = + ZLibStyle ? consumeCompressedZLibHeader(Data, OriginalSize, IsLE, Is64Bit) + : consumeCompressedGnuHeader(Data, OriginalSize); + + if (!Result || zlib::uncompress(Data, Out, OriginalSize) != zlib::StatusOK) + return false; + + // gnu-style names are started from "z", consume that. + if (!ZLibStyle) + Name = Name.substr(1); + return true; +} + DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L) : IsLittleEndian(Obj.isLittleEndian()), @@ -616,20 +678,13 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, name = name.substr(name.find_first_not_of("._")); // Skip . and _ prefixes. - // Check if debug info section is compressed with zlib. - if (name.startswith("zdebug_")) { - uint64_t OriginalSize; - if (!zlib::isAvailable() || - !consumeCompressedDebugSectionHeader(data, OriginalSize)) - continue; - UncompressedSections.resize(UncompressedSections.size() + 1); - if (zlib::uncompress(data, UncompressedSections.back(), OriginalSize) != - zlib::StatusOK) { - UncompressedSections.pop_back(); + bool ZLibStyleCompressed = Section.isCompressed(); + if (ZLibStyleCompressed || name.startswith("zdebug_")) { + SmallString<32> Out; + if (!tryDecompress(name, data, Out, ZLibStyleCompressed, IsLittleEndian, + AddressSize == 8)) continue; - } - // Make data point to uncompressed section contents and save its contents. - name = name.substr(1); + UncompressedSections.emplace_back(std::move(Out)); data = UncompressedSections.back(); } @@ -641,6 +696,7 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, .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) .Case("debug_macinfo", &MacinfoSection) @@ -739,15 +795,29 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj, // First calculate the address of the symbol or section as it appears // in the objct file if (Sym != Obj.symbol_end()) { - ErrorOr<uint64_t> SymAddrOrErr = Sym->getAddress(); - if (std::error_code EC = SymAddrOrErr.getError()) { + Expected<uint64_t> SymAddrOrErr = Sym->getAddress(); + if (!SymAddrOrErr) { + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(SymAddrOrErr.takeError(), OS, ""); + OS.flush(); errs() << "error: failed to compute symbol address: " - << EC.message() << '\n'; + << Buf << '\n'; continue; } SymAddr = *SymAddrOrErr; // Also remember what section this symbol is in for later - RSec = *Sym->getSection(); + auto SectOrErr = Sym->getSection(); + if (!SectOrErr) { + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(SectOrErr.takeError(), OS, ""); + OS.flush(); + errs() << "error: failed to get symbol section: " + << Buf << '\n'; + continue; + } + RSec = *SectOrErr; } else if (auto *MObj = dyn_cast<MachOObjectFile>(&Obj)) { // MachO also has relocations that point to sections and // scattered relocations. diff --git a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 1aa31be71fee..9b6a9a788230 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -10,7 +10,9 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Dwarf.h" @@ -18,6 +20,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include <string> +#include <utility> #include <vector> using namespace llvm; @@ -160,18 +163,26 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset, case DW_CFA_offset_extended: case DW_CFA_register: case DW_CFA_def_cfa: - case DW_CFA_val_offset: + case DW_CFA_val_offset: { // Operands: ULEB128, ULEB128 - addInstruction(Opcode, Data.getULEB128(Offset), - Data.getULEB128(Offset)); + // 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: + case DW_CFA_val_offset_sf: { // Operands: ULEB128, SLEB128 - addInstruction(Opcode, Data.getULEB128(Offset), - Data.getSLEB128(Offset)); + // 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: @@ -191,19 +202,30 @@ public: 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) + 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), + Augmentation(std::move(Augmentation)), AddressSize(AddressSize), SegmentDescriptorSize(SegmentDescriptorSize), CodeAlignmentFactor(CodeAlignmentFactor), DataAlignmentFactor(DataAlignmentFactor), - ReturnAddressRegister(ReturnAddressRegister) {} + ReturnAddressRegister(ReturnAddressRegister), + AugmentationData(std::move(AugmentationData)), + FDEPointerEncoding(FDEPointerEncoding), + LSDAPointerEncoding(LSDAPointerEncoding) {} ~CIE() override {} + 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", @@ -223,6 +245,12 @@ public: (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"; } @@ -239,6 +267,11 @@ private: 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; }; @@ -423,7 +456,7 @@ void FrameEntry::dumpInstructions(raw_ostream &OS) const { } } -DWARFDebugFrame::DWARFDebugFrame() { +DWARFDebugFrame::DWARFDebugFrame(bool IsEH) : IsEH(IsEH) { } DWARFDebugFrame::~DWARFDebugFrame() { @@ -439,6 +472,39 @@ static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, errs() << "\n"; } +static unsigned getSizeForEncoding(const DataExtractor &Data, + unsigned symbolEncoding) { + unsigned format = symbolEncoding & 0x0f; + switch (format) { + default: llvm_unreachable("Unknown Encoding"); + case dwarf::DW_EH_PE_absptr: + case dwarf::DW_EH_PE_signed: + return Data.getAddressSize(); + case dwarf::DW_EH_PE_udata2: + case dwarf::DW_EH_PE_sdata2: + return 2; + case dwarf::DW_EH_PE_udata4: + case dwarf::DW_EH_PE_sdata4: + return 4; + case dwarf::DW_EH_PE_udata8: + case dwarf::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; @@ -447,6 +513,14 @@ void DWARFDebugFrame::parse(DataExtractor Data) { 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; @@ -465,47 +539,132 @@ void DWARFDebugFrame::parse(DataExtractor Data) { // 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 ? 8 : 4); - bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID); + 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); - uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : Data.getU8(&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 = make_unique<CIE>(StartOffset, Length, Version, - StringRef(Augmentation), AddressSize, + AugmentationString, AddressSize, SegmentDescriptorSize, CodeAlignmentFactor, - DataAlignmentFactor, ReturnAddressRegister); + DataAlignmentFactor, ReturnAddressRegister, + AugmentationData, FDEPointerEncoding, + LSDAPointerEncoding); CIEs[StartOffset] = Cie.get(); Entries.emplace_back(std::move(Cie)); } else { // FDE uint64_t CIEPointer = Id; - uint64_t InitialLocation = Data.getAddress(&Offset); - uint64_t AddressRange = Data.getAddress(&Offset); + 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, - CIEs[CIEPointer])); + Cie)); } Entries.back()->parseInstructions(Data, &Offset, EndStructureOffset); - if (Offset != EndStructureOffset) { - std::string Str; - raw_string_ostream OS(Str); - OS << format("Parsing entry instructions at %lx failed", StartOffset); - report_fatal_error(Str); - } + if (Offset != EndStructureOffset) + ReportError("Parsing entry instructions at %lx failed"); } } diff --git a/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index a0bee0da1765..30cb83398dd4 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -17,9 +17,7 @@ using namespace llvm; using namespace dwarf; typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind; -DWARFDebugLine::Prologue::Prologue() { - clear(); -} +DWARFDebugLine::Prologue::Prologue() { clear(); } void DWARFDebugLine::Prologue::clear() { TotalLength = Version = PrologueLength = 0; @@ -44,12 +42,12 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { << 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), + OS << format("standard_opcode_lengths[%s] = %u\n", LNStandardString(i + 1), StandardOpcodeLengths[i]); if (!IncludeDirectories.empty()) for (uint32_t i = 0; i < IncludeDirectories.size(); ++i) - OS << format("include_directories[%3u] = '", i+1) + OS << format("include_directories[%3u] = '", i + 1) << IncludeDirectories[i] << "'\n"; if (!FileNames.empty()) { @@ -57,10 +55,10 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const { << " ---- ---------- ---------- -----------" "----------------\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) + 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'; } } @@ -82,8 +80,8 @@ bool DWARFDebugLine::Prologue::parse(DataExtractor debug_line_data, if (Version < 2) return false; - PrologueLength = debug_line_data.getUnsigned(offset_ptr, - sizeofPrologueLength()); + PrologueLength = + debug_line_data.getUnsigned(offset_ptr, sizeofPrologueLength()); const uint64_t end_prologue_offset = PrologueLength + *offset_ptr; MinInstLength = debug_line_data.getU8(offset_ptr); if (Version >= 4) @@ -131,9 +129,7 @@ bool DWARFDebugLine::Prologue::parse(DataExtractor debug_line_data, return true; } -DWARFDebugLine::Row::Row(bool default_is_stmt) { - reset(default_is_stmt); -} +DWARFDebugLine::Row::Row(bool default_is_stmt) { reset(default_is_stmt); } void DWARFDebugLine::Row::postAppend() { BasicBlock = false; @@ -158,17 +154,13 @@ void DWARFDebugLine::Row::reset(bool default_is_stmt) { 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" : "") + << (IsStmt ? " is_stmt" : "") << (BasicBlock ? " basic_block" : "") << (PrologueEnd ? " prologue_end" : "") << (EpilogueBegin ? " epilogue_begin" : "") - << (EndSequence ? " end_sequence" : "") - << '\n'; + << (EndSequence ? " end_sequence" : "") << '\n'; } -DWARFDebugLine::Sequence::Sequence() { - reset(); -} +DWARFDebugLine::Sequence::Sequence() { reset(); } void DWARFDebugLine::Sequence::reset() { LowPC = 0; @@ -178,9 +170,7 @@ void DWARFDebugLine::Sequence::reset() { Empty = true; } -DWARFDebugLine::LineTable::LineTable() { - clear(); -} +DWARFDebugLine::LineTable::LineTable() { clear(); } void DWARFDebugLine::LineTable::dump(raw_ostream &OS) const { Prologue.dump(OS); @@ -244,7 +234,7 @@ const DWARFDebugLine::LineTable * DWARFDebugLine::getOrParseLineTable(DataExtractor debug_line_data, uint32_t offset) { std::pair<LineTableIter, bool> pos = - LineTableMap.insert(LineTableMapTy::value_type(offset, LineTable())); + LineTableMap.insert(LineTableMapTy::value_type(offset, LineTable())); LineTable *LT = &pos.first->second; if (pos.second) { if (!LT->parse(debug_line_data, RelocMap, &offset)) @@ -266,8 +256,8 @@ bool DWARFDebugLine::LineTable::parse(DataExtractor debug_line_data, return false; } - const uint32_t end_offset = debug_line_offset + Prologue.TotalLength + - Prologue.sizeofTotalLength(); + const uint32_t end_offset = + debug_line_offset + Prologue.TotalLength + Prologue.sizeofTotalLength(); ParsingState State(this); @@ -307,9 +297,9 @@ bool DWARFDebugLine::LineTable::parse(DataExtractor debug_line_data, // If this address is in our relocation map, apply the relocation. RelocAddrMap::const_iterator AI = RMap->find(*offset_ptr); if (AI != RMap->end()) { - const std::pair<uint8_t, int64_t> &R = AI->second; - State.Row.Address = - debug_line_data.getAddress(offset_ptr) + R.second; + const std::pair<uint8_t, int64_t> &R = AI->second; + State.Row.Address = + debug_line_data.getAddress(offset_ptr) + R.second; } else State.Row.Address = debug_line_data.getAddress(offset_ptr); } @@ -509,6 +499,8 @@ bool DWARFDebugLine::LineTable::parse(DataExtractor debug_line_data, State.Row.Line += line_offset; State.Row.Address += addr_offset; State.appendRowToMatrix(*offset_ptr); + // Reset discriminator to 0. + State.Row.Discriminator = 0; } } @@ -566,8 +558,8 @@ uint32_t DWARFDebugLine::LineTable::lookupAddress(uint64_t address) const { sequence.LowPC = address; SequenceIter first_seq = Sequences.begin(); SequenceIter last_seq = Sequences.end(); - SequenceIter seq_pos = std::lower_bound(first_seq, last_seq, sequence, - DWARFDebugLine::Sequence::orderByLowPC); + SequenceIter seq_pos = std::lower_bound( + first_seq, last_seq, sequence, DWARFDebugLine::Sequence::orderByLowPC); DWARFDebugLine::Sequence found_seq; if (seq_pos == last_seq) { found_seq = Sequences.back(); @@ -591,8 +583,8 @@ bool DWARFDebugLine::LineTable::lookupAddressRange( sequence.LowPC = address; SequenceIter first_seq = Sequences.begin(); SequenceIter last_seq = Sequences.end(); - SequenceIter seq_pos = std::lower_bound(first_seq, last_seq, sequence, - DWARFDebugLine::Sequence::orderByLowPC); + SequenceIter seq_pos = std::lower_bound( + first_seq, last_seq, sequence, DWARFDebugLine::Sequence::orderByLowPC); if (seq_pos == last_seq || seq_pos->LowPC != address) { if (seq_pos == first_seq) return false; @@ -632,11 +624,10 @@ bool DWARFDebugLine::LineTable::lookupAddressRange( return true; } -bool -DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, - const char *CompDir, - FileLineInfoKind Kind, - std::string &Result) const { +bool DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, + const char *CompDir, + FileLineInfoKind Kind, + std::string &Result) const { if (FileIndex == 0 || FileIndex > Prologue.FileNames.size() || Kind == FileLineInfoKind::None) return false; @@ -669,11 +660,9 @@ DWARFDebugLine::LineTable::getFileNameByIndex(uint64_t FileIndex, return true; } -bool -DWARFDebugLine::LineTable::getFileLineInfoForAddress(uint64_t Address, - const char *CompDir, - FileLineInfoKind Kind, - DILineInfo &Result) const { +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) diff --git a/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp index cd6fbefd05dd..a7b46b842fee 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp index b6555fa6272e..375ff7c99a04 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -7,9 +7,8 @@ //
//===----------------------------------------------------------------------===//
-#include "SyntaxHighlighting.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h"
-#include "llvm/Support/Compiler.h"
+#include "SyntaxHighlighting.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
diff --git a/lib/DebugInfo/DWARF/DWARFUnit.cpp b/lib/DebugInfo/DWARF/DWARFUnit.cpp index 92ca2d4c3ff0..13c2b508bfa3 100644 --- a/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -20,7 +20,7 @@ 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()); + C.getLineSection().Data, C.isLittleEndian(), false); } void DWARFUnitSectionBase::parseDWO(DWARFContext &C, @@ -28,13 +28,14 @@ void DWARFUnitSectionBase::parseDWO(DWARFContext &C, DWARFUnitIndex *Index) { parseImpl(C, DWOSection, C.getDebugAbbrevDWO(), C.getRangeDWOSection(), C.getStringDWOSection(), C.getStringOffsetDWOSection(), - C.getAddrSection(), C.getLineDWOSection().Data, C.isLittleEndian()); + C.getAddrSection(), C.getLineDWOSection().Data, C.isLittleEndian(), + true); } DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, const DWARFDebugAbbrev *DA, StringRef RS, StringRef SS, StringRef SOS, StringRef AOS, StringRef LS, bool LE, - const DWARFUnitSectionBase &UnitSection, + bool IsDWO, const DWARFUnitSectionBase &UnitSection, const DWARFUnitIndex::Entry *IndexEntry) : Context(DC), InfoSection(Section), Abbrev(DA), RangeSection(RS), LineSection(LS), StringSection(SS), StringOffsetSection([&]() { @@ -43,8 +44,8 @@ DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, return SOS.slice(C->Offset, C->Offset + C->Length); return SOS; }()), - AddrOffsetSection(AOS), isLittleEndian(LE), UnitSection(UnitSection), - IndexEntry(IndexEntry) { + AddrOffsetSection(AOS), isLittleEndian(LE), isDWO(IsDWO), + UnitSection(UnitSection), IndexEntry(IndexEntry) { clear(); } @@ -268,8 +269,11 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { DWARFUnit::DWOHolder::DWOHolder(StringRef DWOPath) : DWOFile(), DWOContext(), DWOU(nullptr) { auto Obj = object::ObjectFile::createObjectFile(DWOPath); - if (!Obj) + if (!Obj) { + // TODO: Actually report errors helpfully. + consumeError(Obj.takeError()); return; + } DWOFile = std::move(Obj.get()); DWOContext.reset( cast<DWARFContext>(new DWARFContextInMemory(*DWOFile.getBinary()))); @@ -278,6 +282,8 @@ DWARFUnit::DWOHolder::DWOHolder(StringRef DWOPath) } bool DWARFUnit::parseDWO() { + if (isDWO) + return false; if (DWO.get()) return false; extractDIEsIfNeeded(true); @@ -375,19 +381,14 @@ DWARFUnit::getInlinedChainForAddress(uint64_t Address) { // First, find a subprogram that contains the given address (the root // of inlined chain). const DWARFUnit *ChainCU = nullptr; - const DWARFDebugInfoEntryMinimal *SubprogramDIE = - getSubprogramForAddress(Address); - if (SubprogramDIE) { + const DWARFDebugInfoEntryMinimal *SubprogramDIE; + // Try to look for subprogram DIEs in the DWO file. + parseDWO(); + if (DWO) { + if ((SubprogramDIE = DWO->getUnit()->getSubprogramForAddress(Address))) + ChainCU = DWO->getUnit(); + } else if ((SubprogramDIE = getSubprogramForAddress(Address))) ChainCU = this; - } else { - // Try to look for subprogram DIEs in the DWO file. - parseDWO(); - if (DWO.get()) { - SubprogramDIE = DWO->getUnit()->getSubprogramForAddress(Address); - if (SubprogramDIE) - ChainCU = DWO->getUnit(); - } - } // Get inlined chain rooted at this subprogram DIE. if (!SubprogramDIE) diff --git a/lib/DebugInfo/DWARF/Makefile b/lib/DebugInfo/DWARF/Makefile deleted file mode 100644 index 863337353d0a..000000000000 --- a/lib/DebugInfo/DWARF/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lib/DebugInfo/DWARF/Makefile ------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -LIBRARYNAME = LLVMDebugInfoDWARF -BUILD_ARCHIVE := 1 - -include $(LEVEL)/Makefile.common diff --git a/lib/DebugInfo/DWARF/module.modulemap b/lib/DebugInfo/DWARF/module.modulemap deleted file mode 100644 index c2f624fd4b6c..000000000000 --- a/lib/DebugInfo/DWARF/module.modulemap +++ /dev/null @@ -1 +0,0 @@ -module DebugInfoDWARF { requires cplusplus umbrella "." module * { export * } } diff --git a/lib/DebugInfo/Makefile b/lib/DebugInfo/Makefile deleted file mode 100644 index 6072af314416..000000000000 --- a/lib/DebugInfo/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- lib/DebugInfo/Makefile ------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -LEVEL = ../.. - -include $(LEVEL)/Makefile.config - -PARALLEL_DIRS := CodeView DWARF PDB Symbolize - -include $(LEVEL)/Makefile.common diff --git a/lib/DebugInfo/PDB/CMakeLists.txt b/lib/DebugInfo/PDB/CMakeLists.txt index 1645a95aac36..b5a2bc1600fc 100644 --- a/lib/DebugInfo/PDB/CMakeLists.txt +++ b/lib/DebugInfo/PDB/CMakeLists.txt @@ -17,6 +17,7 @@ if(HAVE_DIA_SDK) DIA/DIAEnumLineNumbers.cpp DIA/DIAEnumSourceFiles.cpp DIA/DIAEnumSymbols.cpp + DIA/DIAError.cpp DIA/DIALineNumber.cpp DIA/DIARawSymbol.cpp DIA/DIASession.cpp @@ -24,12 +25,37 @@ if(HAVE_DIA_SDK) ) set(LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB/DIA") - endif() +add_pdb_impl_folder(Raw + Raw/DbiStream.cpp + Raw/DbiStreamBuilder.cpp + Raw/EnumTables.cpp + Raw/Hash.cpp + Raw/IndexedStreamData.cpp + Raw/InfoStream.cpp + Raw/InfoStreamBuilder.cpp + Raw/MappedBlockStream.cpp + Raw/ModInfo.cpp + Raw/ModStream.cpp + Raw/MsfBuilder.cpp + Raw/MsfCommon.cpp + Raw/NameHashTable.cpp + Raw/NameMap.cpp + Raw/NameMapBuilder.cpp + Raw/PDBFile.cpp + Raw/PDBFileBuilder.cpp + Raw/PublicsStream.cpp + Raw/RawError.cpp + Raw/RawSession.cpp + Raw/SymbolStream.cpp + Raw/TpiStream.cpp) + +list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB/Raw") list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB") add_llvm_library(LLVMDebugInfoPDB + GenericError.cpp IPDBSourceFile.cpp PDB.cpp PDBContext.cpp diff --git a/lib/DebugInfo/PDB/DIA/DIADataStream.cpp b/lib/DebugInfo/PDB/DIA/DIADataStream.cpp index e0e1b2712467..7eabed8cad48 100644 --- a/lib/DebugInfo/PDB/DIA/DIADataStream.cpp +++ b/lib/DebugInfo/PDB/DIA/DIADataStream.cpp @@ -8,9 +8,11 @@ //===----------------------------------------------------------------------===// #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) {} diff --git a/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp b/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp index 23c6489c7e18..cae817c1b367 100644 --- a/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp +++ b/lib/DebugInfo/PDB/DIA/DIAEnumDebugStreams.cpp @@ -12,6 +12,7 @@ #include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h" using namespace llvm; +using namespace llvm::pdb; DIAEnumDebugStreams::DIAEnumDebugStreams( CComPtr<IDiaEnumDebugStreams> DiaEnumerator) diff --git a/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp b/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp index 32a9af214dc0..4741d9c9a849 100644 --- a/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp +++ b/lib/DebugInfo/PDB/DIA/DIAEnumLineNumbers.cpp @@ -12,6 +12,7 @@ #include "llvm/DebugInfo/PDB/DIA/DIALineNumber.h" using namespace llvm; +using namespace llvm::pdb; DIAEnumLineNumbers::DIAEnumLineNumbers( CComPtr<IDiaEnumLineNumbers> DiaEnumerator) diff --git a/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp b/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp index 1a946100fef2..ccf8c4e622cc 100644 --- a/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp +++ b/lib/DebugInfo/PDB/DIA/DIAEnumSourceFiles.cpp @@ -12,6 +12,7 @@ #include "llvm/DebugInfo/PDB/DIA/DIASourceFile.h" using namespace llvm; +using namespace llvm::pdb; DIAEnumSourceFiles::DIAEnumSourceFiles( const DIASession &PDBSession, CComPtr<IDiaEnumSourceFiles> DiaEnumerator) diff --git a/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp b/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp index 6754d9a97d70..3c211b569044 100644 --- a/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp +++ b/lib/DebugInfo/PDB/DIA/DIAEnumSymbols.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/PDB/DIA/DIASession.h" using namespace llvm; +using namespace llvm::pdb; DIAEnumSymbols::DIAEnumSymbols(const DIASession &PDBSession, CComPtr<IDiaEnumSymbols> DiaEnumerator) diff --git a/lib/DebugInfo/PDB/DIA/DIAError.cpp b/lib/DebugInfo/PDB/DIA/DIAError.cpp new file mode 100644 index 000000000000..1d72a92b5145 --- /dev/null +++ b/lib/DebugInfo/PDB/DIA/DIAError.cpp @@ -0,0 +1,59 @@ +#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 LLVM_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(const std::string &Context) + : DIAError(dia_error_code::unspecified, Context) {} + +DIAError::DIAError(dia_error_code C, const std::string &Context) : Code(C) { + ErrMsg = "DIA Error: "; + std::error_code EC = convertToErrorCode(); + if (Code != dia_error_code::unspecified) + ErrMsg += EC.message() + " "; + if (!Context.empty()) + ErrMsg += Context; +} + +void DIAError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +const std::string &DIAError::getErrorMessage() const { return ErrMsg; } + +std::error_code DIAError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp b/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp index c5577f15eb8e..b19be6b595ab 100644 --- a/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp +++ b/lib/DebugInfo/PDB/DIA/DIALineNumber.cpp @@ -10,6 +10,7 @@ #include "llvm/DebugInfo/PDB/DIA/DIALineNumber.h" using namespace llvm; +using namespace llvm::pdb; DIALineNumber::DIALineNumber(CComPtr<IDiaLineNumber> DiaLineNumber) : LineNumber(DiaLineNumber) {} diff --git a/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp b/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp index abe0ab55e56c..bba5b0f94dca 100644 --- a/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp +++ b/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp @@ -7,64 +7,77 @@ // //===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h" -#include "llvm/DebugInfo/PDB/DIA/DIARawSymbol.h" #include "llvm/DebugInfo/PDB/DIA/DIASession.h" #include "llvm/DebugInfo/PDB/PDBExtras.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.Int8 = V.cVal; + Result.Value.Int8 = V.cVal; Result.Type = PDB_VariantType::Int8; break; case VT_I2: - Result.Int16 = V.iVal; + Result.Value.Int16 = V.iVal; Result.Type = PDB_VariantType::Int16; break; case VT_I4: - Result.Int32 = V.intVal; + Result.Value.Int32 = V.intVal; Result.Type = PDB_VariantType::Int32; break; case VT_I8: - Result.Int64 = V.llVal; + Result.Value.Int64 = V.llVal; Result.Type = PDB_VariantType::Int64; break; case VT_UI1: - Result.UInt8 = V.bVal; + Result.Value.UInt8 = V.bVal; Result.Type = PDB_VariantType::UInt8; break; case VT_UI2: - Result.UInt16 = V.uiVal; + Result.Value.UInt16 = V.uiVal; Result.Type = PDB_VariantType::UInt16; break; case VT_UI4: - Result.UInt32 = V.uintVal; + Result.Value.UInt32 = V.uintVal; Result.Type = PDB_VariantType::UInt32; break; case VT_UI8: - Result.UInt64 = V.ullVal; + Result.Value.UInt64 = V.ullVal; Result.Type = PDB_VariantType::UInt64; break; case VT_BOOL: - Result.Bool = (V.boolVal == VARIANT_TRUE) ? true : false; + Result.Value.Bool = (V.boolVal == VARIANT_TRUE) ? true : false; Result.Type = PDB_VariantType::Bool; break; case VT_R4: - Result.Single = V.fltVal; + Result.Value.Single = V.fltVal; Result.Type = PDB_VariantType::Single; break; case VT_R8: - Result.Double = V.dblVal; + 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; @@ -521,8 +534,8 @@ uint32_t DIARawSymbol::getLiveRangeStartRelativeVirtualAddress() const { Symbol, &IDiaSymbol::get_liveRangeStartRelativeVirtualAddress); } -PDB_RegisterId DIARawSymbol::getLocalBasePointerRegisterId() const { - return PrivateGetDIAValue<DWORD, PDB_RegisterId>( +codeview::RegisterId DIARawSymbol::getLocalBasePointerRegisterId() const { + return PrivateGetDIAValue<DWORD, codeview::RegisterId>( Symbol, &IDiaSymbol::get_localBasePointerRegisterId); } @@ -583,9 +596,9 @@ uint32_t DIARawSymbol::getRank() const { return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_rank); } -PDB_RegisterId DIARawSymbol::getRegisterId() const { - return PrivateGetDIAValue<DWORD, PDB_RegisterId>(Symbol, - &IDiaSymbol::get_registerId); +codeview::RegisterId DIARawSymbol::getRegisterId() const { + return PrivateGetDIAValue<DWORD, codeview::RegisterId>( + Symbol, &IDiaSymbol::get_registerId); } uint32_t DIARawSymbol::getRegisterType() const { @@ -738,8 +751,8 @@ PDB_Machine DIARawSymbol::getMachineType() const { &IDiaSymbol::get_machineType); } -PDB_ThunkOrdinal DIARawSymbol::getThunkOrdinal() const { - return PrivateGetDIAValue<DWORD, PDB_ThunkOrdinal>( +codeview::ThunkOrdinal DIARawSymbol::getThunkOrdinal() const { + return PrivateGetDIAValue<DWORD, codeview::ThunkOrdinal>( Symbol, &IDiaSymbol::get_thunkOrdinal); } diff --git a/lib/DebugInfo/PDB/DIA/DIASession.cpp b/lib/DebugInfo/PDB/DIA/DIASession.cpp index 99fe750ebac6..fa224af8cb87 100644 --- a/lib/DebugInfo/PDB/DIA/DIASession.cpp +++ b/lib/DebugInfo/PDB/DIA/DIASession.cpp @@ -6,107 +6,125 @@ // 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/DIASession.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" using namespace llvm; +using namespace llvm::pdb; + +namespace { + +Error ErrorFromHResult(HRESULT Result) { + switch (Result) { + case E_PDB_NOT_FOUND: + return make_error<GenericError>(generic_error_code::invalid_path); + case E_PDB_FORMAT: + return make_error<DIAError>(dia_error_code::invalid_file_format); + case E_INVALIDARG: + return make_error<DIAError>(dia_error_code::invalid_parameter); + case E_UNEXPECTED: + return make_error<DIAError>(dia_error_code::already_loaded); + case E_PDB_INVALID_SIG: + case E_PDB_INVALID_AGE: + return make_error<DIAError>(dia_error_code::debug_info_mismatch); + default: + return make_error<DIAError>(dia_error_code::unspecified); + } +} -namespace {} +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."); +#endif + + const wchar_t *msdia_dll = nullptr; +#if _MSC_VER == 1900 + 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); + return Error::success(); +} + +} DIASession::DIASession(CComPtr<IDiaSession> DiaSession) : Session(DiaSession) {} -PDB_ErrorCode DIASession::createFromPdb(StringRef Path, - std::unique_ptr<IPDBSession> &Session) { +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. - HRESULT Result = ::CoCreateInstance( - CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, IID_IDiaDataSource, - reinterpret_cast<LPVOID *>(&DiaDataSource)); - if (FAILED(Result)) - return PDB_ErrorCode::NoPdbImpl; + if (auto E = LoadDIA(DiaDataSource)) + return E; llvm::SmallVector<UTF16, 128> Path16; if (!llvm::convertUTF8ToUTF16String(Path, Path16)) - return PDB_ErrorCode::InvalidPath; + return make_error<GenericError>(generic_error_code::invalid_path); const wchar_t *Path16Str = reinterpret_cast<const wchar_t*>(Path16.data()); - if (FAILED(Result = DiaDataSource->loadDataFromPdb(Path16Str))) { - if (Result == E_PDB_NOT_FOUND) - return PDB_ErrorCode::InvalidPath; - else if (Result == E_PDB_FORMAT) - return PDB_ErrorCode::InvalidFileFormat; - else if (Result == E_INVALIDARG) - return PDB_ErrorCode::InvalidParameter; - else if (Result == E_UNEXPECTED) - return PDB_ErrorCode::AlreadyLoaded; - else - return PDB_ErrorCode::UnknownError; - } + HRESULT HR; + if (FAILED(HR = DiaDataSource->loadDataFromPdb(Path16Str))) + return ErrorFromHResult(HR); - if (FAILED(Result = DiaDataSource->openSession(&DiaSession))) { - if (Result == E_OUTOFMEMORY) - return PDB_ErrorCode::NoMemory; - else - return PDB_ErrorCode::UnknownError; - } + if (FAILED(HR = DiaDataSource->openSession(&DiaSession))) + return ErrorFromHResult(HR); Session.reset(new DIASession(DiaSession)); - return PDB_ErrorCode::Success; + return Error::success(); } -PDB_ErrorCode DIASession::createFromExe(StringRef Path, - std::unique_ptr<IPDBSession> &Session) { +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. - HRESULT Result = ::CoCreateInstance( - CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, IID_IDiaDataSource, - reinterpret_cast<LPVOID *>(&DiaDataSource)); - if (FAILED(Result)) - return PDB_ErrorCode::NoPdbImpl; + if (auto EC = LoadDIA(DiaDataSource)) + return EC; llvm::SmallVector<UTF16, 128> Path16; if (!llvm::convertUTF8ToUTF16String(Path, Path16)) - return PDB_ErrorCode::InvalidPath; + return make_error<GenericError>(generic_error_code::invalid_path, Path); const wchar_t *Path16Str = reinterpret_cast<const wchar_t *>(Path16.data()); - if (FAILED(Result = - DiaDataSource->loadDataForExe(Path16Str, nullptr, nullptr))) { - if (Result == E_PDB_NOT_FOUND) - return PDB_ErrorCode::InvalidPath; - else if (Result == E_PDB_FORMAT) - return PDB_ErrorCode::InvalidFileFormat; - else if (Result == E_PDB_INVALID_SIG || Result == E_PDB_INVALID_AGE) - return PDB_ErrorCode::DebugInfoMismatch; - else if (Result == E_INVALIDARG) - return PDB_ErrorCode::InvalidParameter; - else if (Result == E_UNEXPECTED) - return PDB_ErrorCode::AlreadyLoaded; - else - return PDB_ErrorCode::UnknownError; - } + HRESULT HR; + if (FAILED(HR = DiaDataSource->loadDataForExe(Path16Str, nullptr, nullptr))) + return ErrorFromHResult(HR); - if (FAILED(Result = DiaDataSource->openSession(&DiaSession))) { - if (Result == E_OUTOFMEMORY) - return PDB_ErrorCode::NoMemory; - else - return PDB_ErrorCode::UnknownError; - } + if (FAILED(HR = DiaDataSource->openSession(&DiaSession))) + return ErrorFromHResult(HR); Session.reset(new DIASession(DiaSession)); - return PDB_ErrorCode::Success; + return Error::success(); } uint64_t DIASession::getLoadAddress() const { @@ -158,6 +176,22 @@ DIASession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const { } 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)) @@ -166,6 +200,56 @@ DIASession::findLineNumbersByAddress(uint64_t Address, uint32_t Length) const { 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)) diff --git a/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp b/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp index 0a9c444f5e6e..8605f55b402c 100644 --- a/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp +++ b/lib/DebugInfo/PDB/DIA/DIASourceFile.cpp @@ -7,12 +7,16 @@ // //===----------------------------------------------------------------------===// +#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/DIA/DIASourceFile.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) @@ -56,12 +60,15 @@ PDB_Checksum DIASourceFile::getChecksumType() const { return static_cast<PDB_Checksum>(Type); } -std::unique_ptr<IPDBEnumSymbols> DIASourceFile::getCompilands() const { +std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> +DIASourceFile::getCompilands() const { CComPtr<IDiaEnumSymbols> DiaEnumerator; HRESULT Result = SourceFile->get_compilands(&DiaEnumerator); if (S_OK != Result) return nullptr; - return std::unique_ptr<IPDBEnumSymbols>( + 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/lib/DebugInfo/PDB/GenericError.cpp b/lib/DebugInfo/PDB/GenericError.cpp new file mode 100644 index 000000000000..34e179998021 --- /dev/null +++ b/lib/DebugInfo/PDB/GenericError.cpp @@ -0,0 +1,67 @@ +//===- 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 LLVM_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(const std::string &Context) + : GenericError(generic_error_code::unspecified, Context) {} + +GenericError::GenericError(generic_error_code C, const std::string &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"; } + +const std::string &GenericError::getErrorMessage() const { return ErrMsg; } + +std::error_code GenericError::convertToErrorCode() const { + return std::error_code(static_cast<int>(Code), *Category); +} diff --git a/lib/DebugInfo/PDB/IPDBSourceFile.cpp b/lib/DebugInfo/PDB/IPDBSourceFile.cpp index 3abe59dba623..46b422f5a76a 100644 --- a/lib/DebugInfo/PDB/IPDBSourceFile.cpp +++ b/lib/DebugInfo/PDB/IPDBSourceFile.cpp @@ -14,6 +14,7 @@ #include "llvm/Support/raw_ostream.h" using namespace llvm; +using namespace llvm::pdb; IPDBSourceFile::~IPDBSourceFile() {} diff --git a/lib/DebugInfo/PDB/LLVMBuild.txt b/lib/DebugInfo/PDB/LLVMBuild.txt index 9ee9f4067e99..76e537a57fc1 100644 --- a/lib/DebugInfo/PDB/LLVMBuild.txt +++ b/lib/DebugInfo/PDB/LLVMBuild.txt @@ -19,5 +19,5 @@ type = Library name = DebugInfoPDB parent = DebugInfo -required_libraries = Object Support +required_libraries = Object Support DebugInfoCodeView diff --git a/lib/DebugInfo/PDB/Makefile b/lib/DebugInfo/PDB/Makefile deleted file mode 100644 index 444019e5a184..000000000000 --- a/lib/DebugInfo/PDB/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- lib/DebugInfo/PDB/Makefile --------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -LIBRARYNAME = LLVMDebugInfoPDB -BUILD_ARCHIVE := 1 - -include $(LEVEL)/Makefile.common diff --git a/lib/DebugInfo/PDB/PDB.cpp b/lib/DebugInfo/PDB/PDB.cpp index 613407eb1346..69a908eb341c 100644 --- a/lib/DebugInfo/PDB/PDB.cpp +++ b/lib/DebugInfo/PDB/PDB.cpp @@ -11,29 +11,41 @@ #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 HAVE_DIA_SDK #include "llvm/DebugInfo/PDB/DIA/DIASession.h" #endif +#include "llvm/DebugInfo/PDB/Raw/RawSession.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" using namespace llvm; +using namespace llvm::pdb; -PDB_ErrorCode llvm::loadDataForPDB(PDB_ReaderType Type, StringRef Path, - std::unique_ptr<IPDBSession> &Session) { +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::Raw) + return RawSession::createFromPdb(Path, Session); + #if HAVE_DIA_SDK return DIASession::createFromPdb(Path, Session); +#else + return llvm::make_error<GenericError>("DIA is not installed on the system"); #endif - return PDB_ErrorCode::NoPdbImpl; } -PDB_ErrorCode llvm::loadDataForEXE(PDB_ReaderType Type, StringRef Path, - std::unique_ptr<IPDBSession> &Session) { +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::Raw) + return RawSession::createFromExe(Path, Session); + #if HAVE_DIA_SDK return DIASession::createFromExe(Path, Session); +#else + return llvm::make_error<GenericError>("DIA is not installed on the system"); #endif - return PDB_ErrorCode::NoPdbImpl; } diff --git a/lib/DebugInfo/PDB/PDBContext.cpp b/lib/DebugInfo/PDB/PDBContext.cpp index ca2ae6665ce8..773230263da8 100644 --- a/lib/DebugInfo/PDB/PDBContext.cpp +++ b/lib/DebugInfo/PDB/PDBContext.cpp @@ -19,6 +19,7 @@ using namespace llvm; using namespace llvm::object; +using namespace llvm::pdb; PDBContext::PDBContext(const COFFObjectFile &Object, std::unique_ptr<IPDBSession> PDBSession) @@ -28,7 +29,8 @@ PDBContext::PDBContext(const COFFObjectFile &Object, Session->setLoadAddress(ImageBase.get()); } -void PDBContext::dump(raw_ostream &OS, DIDumpType DumpType) {} +void PDBContext::dump(raw_ostream &OS, DIDumpType DumpType, + bool DumpEH) {} DILineInfo PDBContext::getLineInfoForAddress(uint64_t Address, DILineInfoSpecifier Specifier) { @@ -95,26 +97,24 @@ std::string PDBContext::getFunctionName(uint64_t Address, 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())) - return PS->getName(); + 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(); + } } - auto FuncSymbol = - Session->findSymbolByAddress(Address, PDB_SymType::Function); - - // This could happen either if there was no public symbol (e.g. not - // external) or the user requested the short name. In the former case, - // although they technically requested the linkage name, if the linkage - // name is not available we fallback to at least returning a non-empty - // string. - if (auto Func = dyn_cast_or_null<PDBSymbolFunc>(FuncSymbol.get())) - return Func->getName(); - - return std::string(); + return Func ? Func->getName() : std::string(); } diff --git a/lib/DebugInfo/PDB/PDBExtras.cpp b/lib/DebugInfo/PDB/PDBExtras.cpp index 4b9437c58243..b7eee6e53941 100644 --- a/lib/DebugInfo/PDB/PDBExtras.cpp +++ b/lib/DebugInfo/PDB/PDBExtras.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/ArrayRef.h" using namespace llvm; +using namespace llvm::pdb; #define CASE_OUTPUT_ENUM_CLASS_STR(Class, Value, Str, Stream) \ case Class::Value: \ @@ -21,7 +22,8 @@ using namespace llvm; #define CASE_OUTPUT_ENUM_CLASS_NAME(Class, Value, Stream) \ CASE_OUTPUT_ENUM_CLASS_STR(Class, Value, #Value, Stream) -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_VariantType &Type) { +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) @@ -40,42 +42,39 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_VariantType &Type) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_CallingConv &Conv) { +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const PDB_CallingConv &Conv) { OS << "__"; switch (Conv) { - CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearCdecl, "cdecl", OS) - CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarCdecl, "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, NearFastcall, "fastcall", OS) - CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarFastcall, "fastcall", OS) - CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, Skipped, "skippedcall", 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, SuperHCall, "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, M32R, "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, NearVectorcall, "vectorcall", - OS) - default: - OS << "unknowncall"; + 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::operator<<(raw_ostream &OS, const PDB_DataKind &Data) { +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) @@ -91,62 +90,63 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_DataKind &Data) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_RegisterId &Reg) { +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const codeview::RegisterId &Reg) { switch (Reg) { - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, AL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, CL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, DL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, BL, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, AH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, CH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, DH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, BH, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, AX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, CX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, DX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, BX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, SP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, BP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, SI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, DI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, EAX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, ECX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, EDX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, EBX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, ESP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, EBP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, ESI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, EDI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, ES, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, CS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, SS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, DS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, FS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, GS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, IP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RAX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RBX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RCX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RDX, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RSI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RDI, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RBP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, RSP, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R8, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R9, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R10, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R11, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R12, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R13, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R14, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, R15, OS) + 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::operator<<(raw_ostream &OS, const PDB_LocType &Loc) { +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) @@ -164,20 +164,22 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_LocType &Loc) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_ThunkOrdinal &Thunk) { +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const codeview::ThunkOrdinal &Thunk) { switch (Thunk) { - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, BranchIsland, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, Pcode, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, Standard, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, ThisAdjustor, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, TrampIncremental, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, UnknownLoad, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_ThunkOrdinal, Vcall, OS) + 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::operator<<(raw_ostream &OS, const PDB_Checksum &Checksum) { +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) @@ -186,7 +188,7 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_Checksum &Checksum) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_Lang &Lang) { +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) @@ -209,7 +211,7 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_Lang &Lang) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_SymType &Tag) { +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) @@ -247,7 +249,8 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_SymType &Tag) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_MemberAccess &Access) { +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) @@ -256,7 +259,7 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_MemberAccess &Access) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_UdtType &Type) { +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) @@ -266,7 +269,7 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_UdtType &Type) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_UniqueId &Id) { +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const PDB_UniqueId &Id) { static const char *Lookup = "0123456789ABCDEF"; static_assert(sizeof(PDB_UniqueId) == 16, "Expected 16-byte GUID"); @@ -285,40 +288,72 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_UniqueId &Id) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const Variant &Value) { +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.Bool ? "true" : "false"); + OS << (Value.Value.Bool ? "true" : "false"); break; case PDB_VariantType::Double: - OS << Value.Double; + OS << Value.Value.Double; break; case PDB_VariantType::Int16: - OS << Value.Int16; + OS << Value.Value.Int16; break; case PDB_VariantType::Int32: - OS << Value.Int32; + OS << Value.Value.Int32; break; case PDB_VariantType::Int64: - OS << Value.Int64; + OS << Value.Value.Int64; break; case PDB_VariantType::Int8: - OS << static_cast<int>(Value.Int8); + OS << static_cast<int>(Value.Value.Int8); break; case PDB_VariantType::Single: - OS << Value.Single; + OS << Value.Value.Single; break; case PDB_VariantType::UInt16: - OS << Value.Double; + OS << Value.Value.Double; break; case PDB_VariantType::UInt32: - OS << Value.UInt32; + OS << Value.Value.UInt32; break; case PDB_VariantType::UInt64: - OS << Value.UInt64; + OS << Value.Value.UInt64; break; case PDB_VariantType::UInt8: - OS << static_cast<unsigned>(Value.UInt8); + OS << static_cast<unsigned>(Value.Value.UInt8); + break; + case PDB_VariantType::String: + OS << Value.Value.String; break; default: OS << Value.Type; @@ -326,12 +361,13 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const Variant &Value) { return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const VersionInfo &Version) { +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, + const VersionInfo &Version) { OS << Version.Major << "." << Version.Minor << "." << Version.Build; return OS; } -raw_ostream &llvm::operator<<(raw_ostream &OS, const TagStats &Stats) { +raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const TagStats &Stats) { for (auto Tag : Stats) { OS << Tag.first << ":" << Tag.second << " "; } diff --git a/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp b/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp index 7b6268d8f655..a347c67ba8e1 100644 --- a/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp +++ b/lib/DebugInfo/PDB/PDBInterfaceAnchors.cpp @@ -18,6 +18,7 @@ #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" using namespace llvm; +using namespace llvm::pdb; IPDBSession::~IPDBSession() {} diff --git a/lib/DebugInfo/PDB/PDBSymDumper.cpp b/lib/DebugInfo/PDB/PDBSymDumper.cpp index 121e2d13d0c1..9450a988dd6f 100644 --- a/lib/DebugInfo/PDB/PDBSymDumper.cpp +++ b/lib/DebugInfo/PDB/PDBSymDumper.cpp @@ -11,6 +11,7 @@ #include "llvm/Support/ErrorHandling.h" using namespace llvm; +using namespace llvm::pdb; #define PDB_SYMDUMP_UNREACHABLE(Type) \ if (RequireImpl) \ diff --git a/lib/DebugInfo/PDB/PDBSymbol.cpp b/lib/DebugInfo/PDB/PDBSymbol.cpp index f9aaf3ae934d..78b3afc6079a 100644 --- a/lib/DebugInfo/PDB/PDBSymbol.cpp +++ b/lib/DebugInfo/PDB/PDBSymbol.cpp @@ -50,6 +50,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbol::PDBSymbol(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) @@ -112,6 +113,7 @@ void PDBSymbol::defaultDump(raw_ostream &OS, int Indent) const { } 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); diff --git a/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp b/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp index a782cad00ae9..cdb167b6191c 100644 --- a/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolAnnotation.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolAnnotation::PDBSymbolAnnotation(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolBlock.cpp b/lib/DebugInfo/PDB/PDBSymbolBlock.cpp index 46b0ea553e7b..fd5dc9427abf 100644 --- a/lib/DebugInfo/PDB/PDBSymbolBlock.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolBlock.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolBlock::PDBSymbolBlock(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp b/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp index 74369148e26e..ebff08846cac 100644 --- a/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolCompiland.cpp @@ -8,12 +8,14 @@ //===----------------------------------------------------------------------===// #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) @@ -22,3 +24,21 @@ PDBSymbolCompiland::PDBSymbolCompiland(const IPDBSession &PDBSession, 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/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp b/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp index 7b351a042d05..6dbd5228f2cd 100644 --- a/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolCompilandDetails.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolCompilandDetails::PDBSymbolCompilandDetails( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp b/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp index e863ccf1ffa3..9c7f0b1be56f 100644 --- a/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolCompilandEnv.cpp @@ -16,14 +16,17 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolCompilandEnv::PDBSymbolCompilandEnv( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) : PDBSymbol(PDBSession, std::move(Symbol)) {} std::string PDBSymbolCompilandEnv::getValue() const { - // call RawSymbol->getValue() and convert the result to an std::string. - return std::string(); + 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 { diff --git a/lib/DebugInfo/PDB/PDBSymbolCustom.cpp b/lib/DebugInfo/PDB/PDBSymbolCustom.cpp index bd7d9cda2181..0ea387a0eabb 100644 --- a/lib/DebugInfo/PDB/PDBSymbolCustom.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolCustom.cpp @@ -16,6 +16,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolCustom::PDBSymbolCustom(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> CustomSymbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolData.cpp b/lib/DebugInfo/PDB/PDBSymbolData.cpp index a948c2d08afb..62bb6f3f41e2 100644 --- a/lib/DebugInfo/PDB/PDBSymbolData.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolData.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolData::PDBSymbolData(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> DataSymbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolExe.cpp b/lib/DebugInfo/PDB/PDBSymbolExe.cpp index c9e34ea501dd..60101c168a79 100644 --- a/lib/DebugInfo/PDB/PDBSymbolExe.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolExe.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolExe::PDBSymbolExe(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp index 0aff327366cb..35251c0cc1c1 100644 --- a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp @@ -23,6 +23,7 @@ #include <vector> using namespace llvm; +using namespace llvm::pdb; namespace { class FunctionArgEnumerator : public IPDBEnumChildren<PDBSymbolData> { diff --git a/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp b/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp index 8e559b324059..77e996f651df 100644 --- a/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolFuncDebugEnd.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolFuncDebugEnd::PDBSymbolFuncDebugEnd( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp b/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp index ff4254f11504..9c653879176b 100644 --- a/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolFuncDebugStart.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolFuncDebugStart::PDBSymbolFuncDebugStart( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolLabel.cpp b/lib/DebugInfo/PDB/PDBSymbolLabel.cpp index f39dee8d949a..d2cfd11c35e4 100644 --- a/lib/DebugInfo/PDB/PDBSymbolLabel.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolLabel.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolLabel::PDBSymbolLabel(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp b/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp index bd6fe89ac325..97d668740818 100644 --- a/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolPublicSymbol.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolPublicSymbol::PDBSymbolPublicSymbol( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolThunk.cpp b/lib/DebugInfo/PDB/PDBSymbolThunk.cpp index 733eb5f75031..ef8897d12af4 100644 --- a/lib/DebugInfo/PDB/PDBSymbolThunk.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolThunk.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolThunk::PDBSymbolThunk(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp index 19809650361f..c010cc5d7678 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeArray.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeArray::PDBSymbolTypeArray(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp index c1f0d2f6db11..382c397b24d2 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeBaseClass.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeBaseClass::PDBSymbolTypeBaseClass( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp index b302b6629898..e5d65bf5d1fd 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeBuiltin.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeBuiltin::PDBSymbolTypeBuiltin( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp index cc391f1856c8..1d80c97f9ede 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeCustom.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeCustom::PDBSymbolTypeCustom(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp index 1e19d0b00122..535d97dcd21e 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeDimension.cpp @@ -16,6 +16,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeDimension::PDBSymbolTypeDimension( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp index 8dd26a342e73..788f2b732aaa 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeEnum.cpp @@ -17,6 +17,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeEnum::PDBSymbolTypeEnum(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp index d33266094542..5831baebb993 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeFriend.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeFriend::PDBSymbolTypeFriend(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp index f8f71ead88b9..c6f586db9e57 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeFunctionArg.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeFunctionArg::PDBSymbolTypeFunctionArg( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp index af3563f891f8..057ae260885f 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeFunctionSig.cpp @@ -19,6 +19,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; namespace { class FunctionArgEnumerator : public IPDBEnumSymbols { diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp index a7fac3030e99..072d2cfd42fb 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeManaged.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeManaged::PDBSymbolTypeManaged( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp b/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp index 082ed83fcf4e..699771450a5d 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypePointer.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypePointer::PDBSymbolTypePointer( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp index 5a426993869d..0f283b9e21a4 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeTypedef.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeTypedef::PDBSymbolTypeTypedef( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp index 2b5da295fde3..c71838cc7a6f 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeUDT::PDBSymbolTypeUDT(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp index b465d023c59e..6b76db5912ce 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeVTable.cpp @@ -14,6 +14,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeVTable::PDBSymbolTypeVTable(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp b/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp index 16052f1e6810..ef509d64bf60 100644 --- a/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolTypeVTableShape.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolTypeVTableShape::PDBSymbolTypeVTableShape( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp b/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp index 48dc11558ccb..dbbea9c93e20 100644 --- a/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolUnknown.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolUnknown::PDBSymbolUnknown(const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp b/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp index 6cf13de08512..6a62d554f42c 100644 --- a/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolUsingNamespace.cpp @@ -15,6 +15,7 @@ #include <utility> using namespace llvm; +using namespace llvm::pdb; PDBSymbolUsingNamespace::PDBSymbolUsingNamespace( const IPDBSession &PDBSession, std::unique_ptr<IPDBRawSymbol> Symbol) diff --git a/lib/DebugInfo/PDB/Raw/DbiStream.cpp b/lib/DebugInfo/PDB/Raw/DbiStream.cpp new file mode 100644 index 000000000000..3c0586c728f9 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/DbiStream.cpp @@ -0,0 +1,462 @@ +//===- 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/Raw/DbiStream.h" + +#include "llvm/DebugInfo/CodeView/StreamArray.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" +#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" +#include "llvm/DebugInfo/PDB/Raw/NameHashTable.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/RawTypes.h" +#include "llvm/Object/COFF.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; +using namespace llvm::support; + +namespace { +// Some of the values are stored in bitfields. Since this needs to be portable +// across compilers and architectures (big / little endian in particular) we +// can't use the actual structures below, but must instead do the shifting +// and masking ourselves. The struct definitions are provided for reference. + +// struct DbiFlags { +// uint16_t IncrementalLinking : 1; // True if linked incrementally +// uint16_t IsStripped : 1; // True if private symbols were stripped. +// uint16_t HasCTypes : 1; // True if linked with /debug:ctypes. +// uint16_t Reserved : 13; +//}; +const uint16_t FlagIncrementalMask = 0x0001; +const uint16_t FlagStrippedMask = 0x0002; +const uint16_t FlagHasCTypesMask = 0x0004; + +// struct DbiBuildNo { +// uint16_t MinorVersion : 8; +// uint16_t MajorVersion : 7; +// uint16_t NewVersionFormat : 1; +//}; +const uint16_t BuildMinorMask = 0x00FF; +const uint16_t BuildMinorShift = 0; + +const uint16_t BuildMajorMask = 0x7F00; +const uint16_t BuildMajorShift = 8; + +struct FileInfoSubstreamHeader { + ulittle16_t NumModules; // Total # of modules, should match number of + // records in the ModuleInfo substream. + ulittle16_t NumSourceFiles; // Total # of source files. This value is not + // accurate because PDB actually supports more + // than 64k source files, so we ignore it and + // compute the value from other stream fields. +}; +} + +template <typename ContribType> +static Error loadSectionContribs(FixedStreamArray<ContribType> &Output, + StreamReader &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) { + static_assert(sizeof(HeaderInfo) == 64, "Invalid HeaderInfo size!"); +} + +DbiStream::~DbiStream() {} + +Error DbiStream::reload() { + StreamReader Reader(*Stream); + + if (Stream->getLength() < sizeof(HeaderInfo)) + 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."); + + auto IS = Pdb.getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + if (Header->Age != IS->getAge()) + return make_error<RawError>(raw_error_code::corrupt_file, + "DBI Age does not match PDB Age."); + + if (Stream->getLength() != + sizeof(HeaderInfo) + 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."); + + // Since each ModInfo in the stream is a variable length, we have to iterate + // them to know how many there actually are. + VarStreamArray<ModInfo> ModInfoArray; + if (auto EC = Reader.readArray(ModInfoArray, Header->ModiSubstreamSize)) + return EC; + for (auto &Info : ModInfoArray) { + ModuleInfos.emplace_back(Info); + } + + 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 = initializeSectionContributionData()) + return EC; + if (auto EC = initializeSectionHeadersData()) + return EC; + if (auto EC = initializeSectionMapData()) + return EC; + if (auto EC = initializeFileInfo()) + 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) { + StreamReader ECReader(ECSubstream); + if (auto EC = ECNames.load(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 & FlagIncrementalMask) != 0; +} + +bool DbiStream::hasCTypes() const { + return (Header->Flags & FlagHasCTypesMask) != 0; +} + +bool DbiStream::isStripped() const { + return (Header->Flags & FlagStrippedMask) != 0; +} + +uint16_t DbiStream::getBuildNumber() const { return Header->BuildNumber; } + +uint16_t DbiStream::getBuildMajorVersion() const { + return (Header->BuildNumber & BuildMajorMask) >> BuildMajorShift; +} + +uint16_t DbiStream::getBuildMinorVersion() const { + return (Header->BuildNumber & BuildMinorMask) >> 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); +} + +codeview::FixedStreamArray<object::coff_section> +DbiStream::getSectionHeaders() { + return SectionHeaders; +} + +codeview::FixedStreamArray<object::FpoData> DbiStream::getFpoRecords() { + return FpoRecords; +} + +ArrayRef<ModuleInfoEx> DbiStream::modules() const { return ModuleInfos; } +codeview::FixedStreamArray<SecMapEntry> DbiStream::getSectionMap() const { + return SectionMap; +} + +void llvm::pdb::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(); + + StreamReader 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(StreamNum, Pdb); + if (!SHS) + return SHS.takeError(); + + 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); + codeview::StreamReader 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 == InvalidStreamIndex) + return Error::success(); + + if (StreamNum >= Pdb.getNumStreams()) + return make_error<RawError>(raw_error_code::no_stream); + + auto FS = MappedBlockStream::createIndexedStream(StreamNum, Pdb); + if (!FS) + return FS.takeError(); + + 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); + codeview::StreamReader 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(); + + StreamReader 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(); +} + +Error DbiStream::initializeFileInfo() { + // The layout of the FileInfoSubstream is like this: + // struct { + // ulittle16_t NumModules; + // ulittle16_t NumSourceFiles; + // ulittle16_t ModIndices[NumModules]; + // ulittle16_t ModFileCounts[NumModules]; + // ulittle32_t FileNameOffsets[NumSourceFiles]; + // char Names[][NumSourceFiles]; + // }; + // with the caveat that `NumSourceFiles` cannot be trusted, so + // it is computed by summing `ModFileCounts`. + // + if (FileInfoSubstream.getLength() == 0) + return Error::success(); + + const FileInfoSubstreamHeader *FH; + StreamReader FISR(FileInfoSubstream); + if (auto EC = FISR.readObject(FH)) + return EC; + + // The number of modules in the stream should be the same as reported by + // the FileInfoSubstreamHeader. + if (FH->NumModules != ModuleInfos.size()) + return make_error<RawError>(raw_error_code::corrupt_file, + "FileInfo substream count doesn't match DBI."); + + FixedStreamArray<ulittle16_t> ModIndexArray; + FixedStreamArray<ulittle16_t> ModFileCountArray; + + // First is an array of `NumModules` module indices. This is not used for the + // same reason that `NumSourceFiles` is not used. It's an array of uint16's, + // but it's possible there are more than 64k source files, which would imply + // more than 64k modules (e.g. object files) as well. So we ignore this + // field. + if (auto EC = FISR.readArray(ModIndexArray, ModuleInfos.size())) + return EC; + if (auto EC = FISR.readArray(ModFileCountArray, ModuleInfos.size())) + return EC; + + // Compute the real number of source files. + uint32_t NumSourceFiles = 0; + for (auto Count : ModFileCountArray) + NumSourceFiles += Count; + + // This is the array that in the reference implementation corresponds to + // `ModInfo::FileLayout::FileNameOffs`, which is commented there as being a + // pointer. Due to the mentioned problems of pointers causing difficulty + // when reading from the file on 64-bit systems, we continue to ignore that + // field in `ModInfo`, and instead build a vector of StringRefs and stores + // them in `ModuleInfoEx`. The value written to and read from the file is + // not used anyway, it is only there as a way to store the offsets for the + // purposes of later accessing the names at runtime. + if (auto EC = FISR.readArray(FileNameOffsets, NumSourceFiles)) + return EC; + + if (auto EC = FISR.readStreamRef(NamesBuffer)) + return EC; + + // We go through each ModuleInfo, determine the number N of source files for + // that module, and then get the next N offsets from the Offsets array, using + // them to get the corresponding N names from the Names buffer and associating + // each one with the corresponding module. + uint32_t NextFileIndex = 0; + for (size_t I = 0; I < ModuleInfos.size(); ++I) { + uint32_t NumFiles = ModFileCountArray[I]; + ModuleInfos[I].SourceFiles.resize(NumFiles); + for (size_t J = 0; J < NumFiles; ++J, ++NextFileIndex) { + auto ThisName = getFileNameForIndex(NextFileIndex); + if (!ThisName) + return ThisName.takeError(); + ModuleInfos[I].SourceFiles[J] = *ThisName; + } + } + + return Error::success(); +} + +uint32_t DbiStream::getDebugStreamIndex(DbgHeaderType Type) const { + return DbgStreams[static_cast<uint16_t>(Type)]; +} + +Expected<StringRef> DbiStream::getFileNameForIndex(uint32_t Index) const { + StreamReader Names(NamesBuffer); + if (Index >= FileNameOffsets.size()) + 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.readZeroString(Name)) + return std::move(EC); + return Name; +} + +Error DbiStream::commit() { + StreamWriter Writer(*Stream); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + return Error::success(); +} diff --git a/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp b/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp new file mode 100644 index 000000000000..34ff8ae3a907 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp @@ -0,0 +1,81 @@ +//===- 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/Raw/DbiStreamBuilder.h" + +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +DbiStreamBuilder::DbiStreamBuilder() + : Age(1), BuildNumber(0), PdbDllVersion(0), PdbDllRbld(0), Flags(0), + MachineType(PDB_Machine::x86) {} + +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; } + +uint32_t DbiStreamBuilder::calculateSerializedLength() const { + // For now we only support serializing the header. + return sizeof(DbiStream::HeaderInfo); +} + +Expected<std::unique_ptr<DbiStream>> DbiStreamBuilder::build(PDBFile &File) { + if (!VerHeader.hasValue()) + return make_error<RawError>(raw_error_code::unspecified, + "Missing DBI Stream Version"); + + auto DbiS = MappedBlockStream::createIndexedStream(StreamDBI, File); + if (!DbiS) + return DbiS.takeError(); + auto DS = std::move(*DbiS); + DbiStream::HeaderInfo *H = + static_cast<DbiStream::HeaderInfo *>(DS->getAllocator().Allocate( + sizeof(DbiStream::HeaderInfo), + llvm::AlignOf<DbiStream::HeaderInfo>::Alignment)); + 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 = 0; + H->ModiSubstreamSize = 0; + H->OptionalDbgHdrSize = 0; + H->SecContrSubstreamSize = 0; + H->SectionMapSize = 0; + H->TypeServerSize = 0; + H->SymRecordStreamIndex = DbiStream::InvalidStreamIndex; + H->PublicSymbolStreamIndex = DbiStream::InvalidStreamIndex; + H->MFCTypeServerIndex = DbiStream::InvalidStreamIndex; + H->GlobalSymbolStreamIndex = DbiStream::InvalidStreamIndex; + + auto Dbi = llvm::make_unique<DbiStream>(File, std::move(DS)); + Dbi->Header = H; + return std::move(Dbi); +} diff --git a/lib/DebugInfo/PDB/Raw/EnumTables.cpp b/lib/DebugInfo/PDB/Raw/EnumTables.cpp new file mode 100644 index 000000000000..fc9270c69947 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/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/Raw/EnumTables.h" +#include "llvm/DebugInfo/PDB/Raw/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/lib/DebugInfo/PDB/Raw/Hash.cpp b/lib/DebugInfo/PDB/Raw/Hash.cpp new file mode 100644 index 000000000000..23cb55786d78 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/Hash.cpp @@ -0,0 +1,131 @@ +//===- 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/Raw/Hash.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.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 * 1664525L + 1013904223L; +} + +static const uint32_t V8HashTable[] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, +}; + +// Corresponds to `SigForPbCb` in langapi/shared/crc32.h. +uint32_t pdb::hashBufferV8(ArrayRef<uint8_t> Buf) { + uint32_t Hash = 0; + for (uint8_t Byte : Buf) + Hash = (Hash >> 8) ^ V8HashTable[(Hash & 0xff) ^ Byte]; + return Hash; +} diff --git a/lib/DebugInfo/PDB/Raw/IndexedStreamData.cpp b/lib/DebugInfo/PDB/Raw/IndexedStreamData.cpp new file mode 100644 index 000000000000..9bd16ea76efc --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/IndexedStreamData.cpp @@ -0,0 +1,25 @@ +//===- IndexedStreamData.cpp - Standard PDB Stream Data ---------*- 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/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/IPDBFile.h" + +using namespace llvm; +using namespace llvm::pdb; + +IndexedStreamData::IndexedStreamData(uint32_t StreamIdx, const IPDBFile &File) + : StreamIdx(StreamIdx), File(File) {} + +uint32_t IndexedStreamData::getLength() { + return File.getStreamByteSize(StreamIdx); +} + +ArrayRef<support::ulittle32_t> IndexedStreamData::getStreamBlocks() { + return File.getStreamBlockList(StreamIdx); +} diff --git a/lib/DebugInfo/PDB/Raw/InfoStream.cpp b/lib/DebugInfo/PDB/Raw/InfoStream.cpp new file mode 100644 index 000000000000..c33a764587c7 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/InfoStream.cpp @@ -0,0 +1,90 @@ +//===- 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/Raw/InfoStream.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +InfoStream::InfoStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +Error InfoStream::reload() { + codeview::StreamReader Reader(*Stream); + + const HeaderInfo *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; + + return NamedStreams.load(Reader); +} + +uint32_t InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { + uint32_t Result; + if (!NamedStreams.tryGetValue(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; } + +Error InfoStream::commit() { + StreamWriter Writer(*Stream); + + HeaderInfo H; + H.Age = Age; + H.Signature = Signature; + H.Version = Version; + H.Guid = Guid; + if (auto EC = Writer.writeObject(H)) + return EC; + + return NamedStreams.commit(Writer); +} diff --git a/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp b/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp new file mode 100644 index 000000000000..7be9cc32db96 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp @@ -0,0 +1,67 @@ +//===- 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/Raw/InfoStreamBuilder.h" + +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +InfoStreamBuilder::InfoStreamBuilder() {} + +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; } + +NameMapBuilder &InfoStreamBuilder::getNamedStreamsBuilder() { + return NamedStreams; +} + +uint32_t InfoStreamBuilder::calculateSerializedLength() const { + return sizeof(InfoStream::HeaderInfo) + + NamedStreams.calculateSerializedLength(); +} + +Expected<std::unique_ptr<InfoStream>> InfoStreamBuilder::build(PDBFile &File) { + if (!Ver.hasValue()) + return make_error<RawError>(raw_error_code::unspecified, + "Missing PDB Stream Version"); + if (!Sig.hasValue()) + return make_error<RawError>(raw_error_code::unspecified, + "Missing PDB Stream Signature"); + if (!Age.hasValue()) + return make_error<RawError>(raw_error_code::unspecified, + "Missing PDB Stream Age"); + if (!Guid.hasValue()) + return make_error<RawError>(raw_error_code::unspecified, + "Missing PDB Stream Guid"); + + auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, File); + if (!InfoS) + return InfoS.takeError(); + auto Info = llvm::make_unique<InfoStream>(std::move(*InfoS)); + Info->Version = *Ver; + Info->Signature = *Sig; + Info->Age = *Age; + Info->Guid = *Guid; + auto NS = NamedStreams.build(); + if (!NS) + return NS.takeError(); + Info->NamedStreams = **NS; + return std::move(Info); +} diff --git a/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp b/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp new file mode 100644 index 000000000000..92b2048c3c22 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp @@ -0,0 +1,310 @@ +//===- MappedBlockStream.cpp - Reads stream data from a PDBFile -----------===// +// +// 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/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// This exists so that we can use make_unique while still keeping the +// constructor of MappedBlockStream private, forcing users to go through +// the `create` interface. +class MappedBlockStreamImpl : public MappedBlockStream { +public: + MappedBlockStreamImpl(std::unique_ptr<IPDBStreamData> Data, + const IPDBFile &File) + : MappedBlockStream(std::move(Data), File) {} +}; +} + +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(std::unique_ptr<IPDBStreamData> Data, + const IPDBFile &Pdb) + : Pdb(Pdb), Data(std::move(Data)) {} + +Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const { + // Make sure we aren't trying to read beyond the end of the stream. + if (Size > Data->getLength()) + return make_error<RawError>(raw_error_code::insufficient_buffer); + if (Offset > Data->getLength() - Size) + return make_error<RawError>(raw_error_code::insufficient_buffer); + + 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) const { + // Make sure we aren't trying to read beyond the end of the stream. + if (Offset >= Data->getLength()) + return make_error<RawError>(raw_error_code::insufficient_buffer); + uint32_t First = Offset / Pdb.getBlockSize(); + uint32_t Last = First; + + auto BlockList = Data->getStreamBlocks(); + while (Last < Pdb.getBlockCount() - 1) { + if (BlockList[Last] != BlockList[Last + 1] - 1) + break; + ++Last; + } + + uint32_t OffsetInFirstBlock = Offset % Pdb.getBlockSize(); + uint32_t BytesFromFirstBlock = Pdb.getBlockSize() - OffsetInFirstBlock; + uint32_t BlockSpan = Last - First + 1; + uint32_t ByteSpan = + BytesFromFirstBlock + (BlockSpan - 1) * Pdb.getBlockSize(); + auto Result = Pdb.getBlockData(BlockList[First], Pdb.getBlockSize()); + if (!Result) + return Result.takeError(); + Buffer = Result->drop_front(OffsetInFirstBlock); + Buffer = ArrayRef<uint8_t>(Buffer.data(), ByteSpan); + return Error::success(); +} + +uint32_t MappedBlockStream::getLength() const { return Data->getLength(); } + +Error MappedBlockStream::commit() const { return Error::success(); } + +bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const { + // 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 / Pdb.getBlockSize(); + uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); + uint32_t BytesFromFirstBlock = + std::min(Size, Pdb.getBlockSize() - OffsetInBlock); + uint32_t NumAdditionalBlocks = + llvm::alignTo(Size - BytesFromFirstBlock, Pdb.getBlockSize()) / + Pdb.getBlockSize(); + + auto BlockList = Data->getStreamBlocks(); + uint32_t RequiredContiguousBlocks = NumAdditionalBlocks + 1; + uint32_t E = BlockList[BlockNum]; + for (uint32_t I = 0; I < RequiredContiguousBlocks; ++I, ++E) { + if (BlockList[I + BlockNum] != E) + return false; + } + + uint32_t FirstBlockAddr = BlockList[BlockNum]; + auto Result = Pdb.getBlockData(FirstBlockAddr, Pdb.getBlockSize()); + if (!Result) { + consumeError(Result.takeError()); + return false; + } + auto Data = Result->drop_front(OffsetInBlock); + Buffer = ArrayRef<uint8_t>(Data.data(), Size); + return true; +} + +Error MappedBlockStream::readBytes(uint32_t Offset, + MutableArrayRef<uint8_t> Buffer) const { + uint32_t BlockNum = Offset / Pdb.getBlockSize(); + uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); + + // Make sure we aren't trying to read beyond the end of the stream. + if (Buffer.size() > Data->getLength()) + return make_error<RawError>(raw_error_code::insufficient_buffer); + if (Offset > Data->getLength() - Buffer.size()) + return make_error<RawError>(raw_error_code::insufficient_buffer); + + uint32_t BytesLeft = Buffer.size(); + uint32_t BytesWritten = 0; + uint8_t *WriteBuffer = Buffer.data(); + auto BlockList = Data->getStreamBlocks(); + while (BytesLeft > 0) { + uint32_t StreamBlockAddr = BlockList[BlockNum]; + + auto Result = Pdb.getBlockData(StreamBlockAddr, Pdb.getBlockSize()); + if (!Result) + return Result.takeError(); + + auto Data = *Result; + const uint8_t *ChunkStart = Data.data() + OffsetInBlock; + uint32_t BytesInChunk = + std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock); + ::memcpy(WriteBuffer + BytesWritten, ChunkStart, BytesInChunk); + + BytesWritten += BytesInChunk; + BytesLeft -= BytesInChunk; + ++BlockNum; + OffsetInBlock = 0; + } + + return Error::success(); +} + +Error MappedBlockStream::writeBytes(uint32_t Offset, + ArrayRef<uint8_t> Buffer) const { + // Make sure we aren't trying to write beyond the end of the stream. + if (Buffer.size() > Data->getLength()) + return make_error<RawError>(raw_error_code::insufficient_buffer); + + if (Offset > Data->getLength() - Buffer.size()) + return make_error<RawError>(raw_error_code::insufficient_buffer); + + uint32_t BlockNum = Offset / Pdb.getBlockSize(); + uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); + + uint32_t BytesLeft = Buffer.size(); + auto BlockList = Data->getStreamBlocks(); + uint32_t BytesWritten = 0; + while (BytesLeft > 0) { + uint32_t StreamBlockAddr = BlockList[BlockNum]; + uint32_t BytesToWriteInChunk = + std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock); + + const uint8_t *Chunk = Buffer.data() + BytesWritten; + ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk); + if (auto EC = Pdb.setBlockData(StreamBlockAddr, OffsetInBlock, ChunkData)) + return EC; + + BytesLeft -= BytesToWriteInChunk; + BytesWritten += BytesToWriteInChunk; + ++BlockNum; + OffsetInBlock = 0; + } + + // 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 + BytesWritten < 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 + BytesWritten); + 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, Buffer.data() + SrcOffset, Length); + } + } + + return Error::success(); +} + +uint32_t MappedBlockStream::getNumBytesCopied() const { + return static_cast<uint32_t>(Pool.getBytesAllocated()); +} + +Expected<std::unique_ptr<MappedBlockStream>> +MappedBlockStream::createIndexedStream(uint32_t StreamIdx, + const IPDBFile &File) { + if (StreamIdx >= File.getNumStreams()) + return make_error<RawError>(raw_error_code::no_stream); + + auto Data = llvm::make_unique<IndexedStreamData>(StreamIdx, File); + return llvm::make_unique<MappedBlockStreamImpl>(std::move(Data), File); +} + +Expected<std::unique_ptr<MappedBlockStream>> +MappedBlockStream::createDirectoryStream(const PDBFile &File) { + auto Data = llvm::make_unique<DirectoryStreamData>(File); + return llvm::make_unique<MappedBlockStreamImpl>(std::move(Data), File); +} diff --git a/lib/DebugInfo/PDB/Raw/ModInfo.cpp b/lib/DebugInfo/PDB/Raw/ModInfo.cpp new file mode 100644 index 000000000000..bae135f77bc0 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/ModInfo.cpp @@ -0,0 +1,127 @@ +//===- ModInfo.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/Raw/ModInfo.h" + +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::support; + +namespace { + +struct SCBytes { + ulittle16_t Section; + char Padding1[2]; + little32_t Offset; + little32_t Size; + ulittle32_t Characteristics; + ulittle16_t ModuleIndex; + char Padding2[2]; + ulittle32_t DataCrc; + ulittle32_t RelocCrc; +}; + +// struct Flags { +// uint16_t fWritten : 1; // True if ModInfo is dirty +// uint16_t fECEnabled : 1; // Is EC symbolic info present? (What is EC?) +// uint16_t unused : 6; // Reserved +// uint16_t iTSM : 8; // Type Server Index for this module +//}; +const uint16_t HasECFlagMask = 0x2; + +const uint16_t TypeServerIndexMask = 0xFF00; +const uint16_t TypeServerIndexShift = 8; +} + +struct ModInfo::FileLayout { + ulittle32_t Mod; // Currently opened module. This field is a + // pointer in the reference implementation, but + // that won't work on 64-bit systems, and anyway + // it doesn't make sense to read a pointer from a + // file. For now it is unused, so just ignore it. + SCBytes SC; // First section contribution of this module. + ulittle16_t Flags; // See Flags definition. + ulittle16_t ModDiStream; // Stream Number of module debug info + ulittle32_t SymBytes; // Size of local symbol debug info in above stream + ulittle32_t LineBytes; // Size of line number debug info in above stream + ulittle32_t C13Bytes; // Size of C13 line number info in above stream + ulittle16_t NumFiles; // Number of files contributing to this module + char Padding1[2]; // Padding so the next field is 4-byte aligned. + ulittle32_t FileNameOffs; // array of [0..NumFiles) DBI name buffer offsets. + // This field is a pointer in the reference + // implementation, but as with `Mod`, we ignore it + // for now since it is unused. + ulittle32_t SrcFileNameNI; // Name Index for src file name + ulittle32_t PdbFilePathNI; // Name Index for path to compiler PDB + // Null terminated Module name + // Null terminated Obj File Name +}; + +ModInfo::ModInfo() : Layout(nullptr) {} + +ModInfo::ModInfo(const ModInfo &Info) + : ModuleName(Info.ModuleName), ObjFileName(Info.ObjFileName), + Layout(Info.Layout) {} + +ModInfo::~ModInfo() {} + +Error ModInfo::initialize(codeview::StreamRef Stream, ModInfo &Info) { + codeview::StreamReader Reader(Stream); + if (auto EC = Reader.readObject(Info.Layout)) + return EC; + + if (auto EC = Reader.readZeroString(Info.ModuleName)) + return EC; + + if (auto EC = Reader.readZeroString(Info.ObjFileName)) + return EC; + return Error::success(); +} + +bool ModInfo::hasECInfo() const { return (Layout->Flags & HasECFlagMask) != 0; } + +uint16_t ModInfo::getTypeServerIndex() const { + return (Layout->Flags & TypeServerIndexMask) >> TypeServerIndexShift; +} + +uint16_t ModInfo::getModuleStreamIndex() const { return Layout->ModDiStream; } + +uint32_t ModInfo::getSymbolDebugInfoByteSize() const { + return Layout->SymBytes; +} + +uint32_t ModInfo::getLineInfoByteSize() const { return Layout->LineBytes; } + +uint32_t ModInfo::getC13LineInfoByteSize() const { return Layout->C13Bytes; } + +uint32_t ModInfo::getNumberOfFiles() const { return Layout->NumFiles; } + +uint32_t ModInfo::getSourceFileNameIndex() const { + return Layout->SrcFileNameNI; +} + +uint32_t ModInfo::getPdbFilePathNameIndex() const { + return Layout->PdbFilePathNI; +} + +StringRef ModInfo::getModuleName() const { return ModuleName; } + +StringRef ModInfo::getObjFileName() const { return ObjFileName; } + +uint32_t ModInfo::getRecordLength() const { + uint32_t M = ModuleName.str().size() + 1; + uint32_t O = ObjFileName.str().size() + 1; + uint32_t Size = sizeof(FileLayout) + M + O; + Size = llvm::alignTo(Size, 4); + return Size; +} diff --git a/lib/DebugInfo/PDB/Raw/ModStream.cpp b/lib/DebugInfo/PDB/Raw/ModStream.cpp new file mode 100644 index 000000000000..3415fcd47790 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/ModStream.cpp @@ -0,0 +1,82 @@ +//===- ModStream.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/Raw/ModStream.h" + +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/RawTypes.h" + +using namespace llvm; +using namespace llvm::pdb; + +ModStream::ModStream(const ModInfo &Module, + std::unique_ptr<MappedBlockStream> Stream) + : Mod(Module), Stream(std::move(Stream)) {} + +ModStream::~ModStream() {} + +Error ModStream::reload() { + codeview::StreamReader Reader(*Stream); + + uint32_t SymbolSize = Mod.getSymbolDebugInfoByteSize(); + uint32_t C11Size = Mod.getLineInfoByteSize(); + uint32_t C13Size = Mod.getC13LineInfoByteSize(); + + if (C11Size > 0 && C13Size > 0) + return llvm::make_error<RawError>(raw_error_code::corrupt_file, + "Module has both C11 and C13 line info"); + + codeview::StreamRef S; + + uint32_t SymbolSubstreamSig = 0; + if (auto EC = Reader.readInteger(SymbolSubstreamSig)) + return EC; + if (auto EC = Reader.readArray(SymbolsSubstream, SymbolSize - 4)) + return EC; + + if (auto EC = Reader.readStreamRef(LinesSubstream, C11Size)) + return EC; + if (auto EC = Reader.readStreamRef(C13LinesSubstream, C13Size)) + return EC; + + codeview::StreamReader LineReader(C13LinesSubstream); + if (auto EC = LineReader.readArray(LineInfo, 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 llvm::make_error<RawError>(raw_error_code::corrupt_file, + "Unexpected bytes in module stream."); + + return Error::success(); +} + +iterator_range<codeview::CVSymbolArray::Iterator> +ModStream::symbols(bool *HadError) const { + // It's OK if the stream is empty. + if (SymbolsSubstream.getUnderlyingStream().getLength() == 0) + return llvm::make_range(SymbolsSubstream.end(), SymbolsSubstream.end()); + return llvm::make_range(SymbolsSubstream.begin(HadError), + SymbolsSubstream.end()); +} + +iterator_range<codeview::ModuleSubstreamArray::Iterator> +ModStream::lines(bool *HadError) const { + return llvm::make_range(LineInfo.begin(HadError), LineInfo.end()); +} + +Error ModStream::commit() { return Error::success(); } diff --git a/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp b/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp new file mode 100644 index 000000000000..16b086bdaa98 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp @@ -0,0 +1,279 @@ +//===- MSFBuilder.cpp - MSF Directory & Metadata Builder --------*- 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/Raw/MsfBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::pdb::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 kDefaultBlockMapAddr = kNumReservedPages; +} + +MsfBuilder::MsfBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow, + BumpPtrAllocator &Allocator) + : Allocator(Allocator), IsGrowable(CanGrow), 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 (!msf::isValidBlockSize(BlockSize)) + return make_error<RawError>(raw_error_code::unspecified, + "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<RawError>(raw_error_code::unspecified, + "Cannot grow the number of blocks"); + FreeBlocks.resize(Addr + 1); + } + + if (!isBlockFree(Addr)) + return make_error<RawError>(raw_error_code::unspecified, + "Attempt to reuse an allocated block"); + 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<RawError>(raw_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<RawError>(raw_error_code::unspecified, + "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]; } + +Error 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<RawError>( + raw_error_code::unspecified, + "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<RawError>( + raw_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 Error::success(); +} + +Error 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 EC; + StreamData.push_back(std::make_pair(Size, NewBlocks)); + return Error::success(); +} + +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<Layout> MsfBuilder::build() { + Layout L; + L.SB = Allocator.Allocate<SuperBlock>(); + std::memcpy(L.SB->MagicBytes, Magic, sizeof(Magic)); + L.SB->BlockMapAddr = BlockMapAddr; + L.SB->BlockSize = BlockSize; + L.SB->NumDirectoryBytes = computeDirectoryByteSize(); + L.SB->FreeBlockMapBlock = FreePageMap; + L.SB->Unknown1 = Unknown1; + + uint32_t NumDirectoryBlocks = + bytesToBlocks(L.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. + L.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/lib/DebugInfo/PDB/Raw/MsfCommon.cpp b/lib/DebugInfo/PDB/Raw/MsfCommon.cpp new file mode 100644 index 000000000000..5d97f33e1103 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/MsfCommon.cpp @@ -0,0 +1,48 @@ +//===- 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/PDB/Raw/MsfCommon.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::pdb::msf; + +Error llvm::pdb::msf::validateSuperBlock(const SuperBlock &SB) { + // Check the magic bytes. + if (std::memcmp(SB.MagicBytes, Magic, sizeof(Magic)) != 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "MSF magic header doesn't match"); + + if (!isValidBlockSize(SB.BlockSize)) + return make_error<RawError>(raw_error_code::corrupt_file, + "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<RawError>(raw_error_code::corrupt_file, + "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<RawError>(raw_error_code::corrupt_file, + "Too many directory blocks."); + + if (SB.BlockMapAddr == 0) + return make_error<RawError>(raw_error_code::corrupt_file, + "Block 0 is reserved"); + + return Error::success(); +} diff --git a/lib/DebugInfo/PDB/Raw/NameHashTable.cpp b/lib/DebugInfo/PDB/Raw/NameHashTable.cpp new file mode 100644 index 000000000000..ae4ebf27721e --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/NameHashTable.cpp @@ -0,0 +1,104 @@ +//===- NameHashTable.cpp - PDB Name 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/Raw/NameHashTable.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/PDB/Raw/Hash.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace llvm::pdb; + +NameHashTable::NameHashTable() : Signature(0), HashVersion(0), NameCount(0) {} + +Error NameHashTable::load(codeview::StreamReader &Stream) { + struct Header { + support::ulittle32_t Signature; + support::ulittle32_t HashVersion; + support::ulittle32_t ByteSize; + }; + + const Header *H; + if (auto EC = Stream.readObject(H)) + return EC; + + if (H->Signature != 0xEFFEEFFE) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid hash table signature"); + if (H->HashVersion != 1 && H->HashVersion != 2) + return make_error<RawError>(raw_error_code::corrupt_file, + "Unsupported hash version"); + + Signature = H->Signature; + HashVersion = H->HashVersion; + if (auto EC = Stream.readStreamRef(NamesBuffer, H->ByteSize)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Invalid hash table byte length")); + + const support::ulittle32_t *HashCount; + if (auto EC = Stream.readObject(HashCount)) + return EC; + + if (auto EC = Stream.readArray(IDs, *HashCount)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Could not read bucket array")); + + if (Stream.bytesRemaining() < sizeof(support::ulittle32_t)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Missing name count"); + + if (auto EC = Stream.readInteger(NameCount)) + return EC; + return Error::success(); +} + +StringRef NameHashTable::getStringForID(uint32_t ID) const { + if (ID == IDs[0]) + return StringRef(); + + // NamesBuffer is a buffer of null terminated strings back to back. ID is + // the starting offset of the string we're looking for. So just seek into + // the desired offset and a read a null terminated stream from that offset. + StringRef Result; + codeview::StreamReader NameReader(NamesBuffer); + NameReader.setOffset(ID); + if (auto EC = NameReader.readZeroString(Result)) + consumeError(std::move(EC)); + return Result; +} + +uint32_t NameHashTable::getIDForString(StringRef Str) const { + uint32_t Hash = (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]; + StringRef S = getStringForID(ID); + if (S == Str) + return ID; + } + // IDs[0] contains the ID of the "invalid" entry. + return IDs[0]; +} + +codeview::FixedStreamArray<support::ulittle32_t> +NameHashTable::name_ids() const { + return IDs; +} diff --git a/lib/DebugInfo/PDB/Raw/NameMap.cpp b/lib/DebugInfo/PDB/Raw/NameMap.cpp new file mode 100644 index 000000000000..b8a4eb79a48c --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/NameMap.cpp @@ -0,0 +1,213 @@ +//===- NameMap.cpp - PDB Name 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/Raw/NameMap.h" +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +NameMap::NameMap() {} + +Error NameMap::load(codeview::StreamReader &Stream) { + + // This is some sort of weird string-set/hash table encoded in the stream. + // It starts with the number of bytes in the table. + uint32_t NumberOfBytes; + if (auto EC = Stream.readInteger(NumberOfBytes)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map length")); + if (Stream.bytesRemaining() < NumberOfBytes) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid name map length"); + + // Following that field is the starting offset of strings in the name table. + uint32_t StringsOffset = Stream.getOffset(); + Stream.setOffset(StringsOffset + NumberOfBytes); + + // This appears to be equivalent to the total number of strings *actually* + // in the name table. + uint32_t HashSize; + if (auto EC = Stream.readInteger(HashSize)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map hash size")); + + // This appears to be an upper bound on the number of strings in the name + // table. + uint32_t MaxNumberOfStrings; + if (auto EC = Stream.readInteger(MaxNumberOfStrings)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map max strings")); + + if (MaxNumberOfStrings > (UINT32_MAX / sizeof(uint32_t))) + return make_error<RawError>(raw_error_code::corrupt_file, + "Implausible number of strings"); + + const uint32_t MaxNumberOfWords = UINT32_MAX / (sizeof(uint32_t) * 8); + + // This appears to be a hash table which uses bitfields to determine whether + // or not a bucket is 'present'. + uint32_t NumPresentWords; + if (auto EC = Stream.readInteger(NumPresentWords)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map num words")); + + if (NumPresentWords > MaxNumberOfWords) + return make_error<RawError>(raw_error_code::corrupt_file, + "Number of present words is too large"); + + SparseBitVector<> Present; + for (uint32_t I = 0; I != NumPresentWords; ++I) { + uint32_t Word; + if (auto EC = Stream.readInteger(Word)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map word")); + for (unsigned Idx = 0; Idx < 32; ++Idx) + if (Word & (1U << Idx)) + Present.set((I * 32) + Idx); + } + + // This appears to be a hash table which uses bitfields to determine whether + // or not a bucket is 'deleted'. + uint32_t NumDeletedWords; + if (auto EC = Stream.readInteger(NumDeletedWords)) + return joinErrors( + std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map num deleted words")); + + if (NumDeletedWords > MaxNumberOfWords) + return make_error<RawError>(raw_error_code::corrupt_file, + "Number of deleted words is too large"); + + SparseBitVector<> Deleted; + for (uint32_t I = 0; I != NumDeletedWords; ++I) { + uint32_t Word; + if (auto EC = Stream.readInteger(Word)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map word")); + for (unsigned Idx = 0; Idx < 32; ++Idx) + if (Word & (1U << Idx)) + Deleted.set((I * 32) + Idx); + } + + for (unsigned I : Present) { + // For all present entries, dump out their mapping. + (void)I; + + // This appears to be an offset relative to the start of the strings. + // It tells us where the null-terminated string begins. + uint32_t NameOffset; + if (auto EC = Stream.readInteger(NameOffset)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map name offset")); + + // This appears to be a stream number into the stream directory. + uint32_t NameIndex; + if (auto EC = Stream.readInteger(NameIndex)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map name index")); + + // Compute the offset of the start of the string relative to the stream. + uint32_t StringOffset = StringsOffset + NameOffset; + uint32_t OldOffset = Stream.getOffset(); + // Pump out our c-string from the stream. + StringRef Str; + Stream.setOffset(StringOffset); + if (auto EC = Stream.readZeroString(Str)) + return joinErrors(std::move(EC), + make_error<RawError>(raw_error_code::corrupt_file, + "Expected name map name")); + + Stream.setOffset(OldOffset); + // Add this to a string-map from name to stream number. + Mapping.insert({Str, NameIndex}); + } + + return Error::success(); +} + +Error NameMap::commit(codeview::StreamWriter &Writer) { + // The first field is the number of bytes of string data. So add + // up the length of all strings plus a null terminator for each + // one. + uint32_t NumBytes = 0; + for (auto B = Mapping.begin(), E = Mapping.end(); B != E; ++B) { + NumBytes += B->getKeyLength() + 1; + } + + if (auto EC = Writer.writeInteger(NumBytes)) // Number of bytes of string data + return EC; + // Now all of the string data itself. + for (auto B = Mapping.begin(), E = Mapping.end(); B != E; ++B) { + if (auto EC = Writer.writeZeroString(B->getKey())) + return EC; + } + + if (auto EC = Writer.writeInteger(Mapping.size())) // Hash Size + return EC; + + if (auto EC = Writer.writeInteger(Mapping.size())) // Max Number of Strings + return EC; + + if (auto EC = Writer.writeInteger(Mapping.size())) // Num Present Words + return EC; + + // For each entry in the mapping, write a bit mask which represents a bucket + // to store it in. We don't use this, so the value we write isn't important + // to us, it just has to be there. + for (auto B = Mapping.begin(), E = Mapping.end(); B != E; ++B) { + if (auto EC = Writer.writeInteger(1U)) + return EC; + } + + if (auto EC = Writer.writeInteger(0U)) // Num Deleted Words + return EC; + + // Mappings of each word. + uint32_t OffsetSoFar = 0; + for (auto B = Mapping.begin(), E = Mapping.end(); B != E; ++B) { + // This is a list of key value pairs where the key is the offset into the + // strings buffer, and the value is a stream number. Write each pair. + if (auto EC = Writer.writeInteger(OffsetSoFar)) + return EC; + + if (auto EC = Writer.writeInteger(B->second)) + return EC; + + OffsetSoFar += B->getKeyLength() + 1; + } + + return Error::success(); +} + +iterator_range<StringMapConstIterator<uint32_t>> NameMap::entries() const { + return llvm::make_range<StringMapConstIterator<uint32_t>>(Mapping.begin(), + Mapping.end()); +} + +bool NameMap::tryGetValue(StringRef Name, uint32_t &Value) const { + auto Iter = Mapping.find(Name); + if (Iter == Mapping.end()) + return false; + Value = Iter->second; + return true; +} diff --git a/lib/DebugInfo/PDB/Raw/NameMapBuilder.cpp b/lib/DebugInfo/PDB/Raw/NameMapBuilder.cpp new file mode 100644 index 000000000000..41c6c2cd810a --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/NameMapBuilder.cpp @@ -0,0 +1,50 @@ +//===- NameMapBuilder.cpp - PDB Name Map Builder ----------------*- 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/Raw/NameMapBuilder.h" + +#include "llvm/DebugInfo/PDB/Raw/NameMap.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::pdb; + +NameMapBuilder::NameMapBuilder() {} + +void NameMapBuilder::addMapping(StringRef Name, uint32_t Mapping) { + StringDataBytes += Name.size() + 1; + Map.insert({Name, Mapping}); +} + +Expected<std::unique_ptr<NameMap>> NameMapBuilder::build() { + auto Result = llvm::make_unique<NameMap>(); + Result->Mapping = Map; + return std::move(Result); +} + +uint32_t NameMapBuilder::calculateSerializedLength() const { + uint32_t TotalLength = 0; + + TotalLength += sizeof(support::ulittle32_t); // StringDataBytes value + TotalLength += StringDataBytes; // actual string data + + TotalLength += sizeof(support::ulittle32_t); // Hash Size + TotalLength += sizeof(support::ulittle32_t); // Max Number of Strings + TotalLength += sizeof(support::ulittle32_t); // Num Present Words + // One bitmask word for each present entry + TotalLength += Map.size() * sizeof(support::ulittle32_t); + TotalLength += sizeof(support::ulittle32_t); // Num Deleted Words + + // For each present word, which we are treating as equivalent to the number of + // entries in the table, we have a pair of integers. An offset into the + // string data, and a corresponding stream number. + TotalLength += Map.size() * 2 * sizeof(support::ulittle32_t); + + return TotalLength; +} diff --git a/lib/DebugInfo/PDB/Raw/PDBFile.cpp b/lib/DebugInfo/PDB/Raw/PDBFile.cpp new file mode 100644 index 000000000000..95016753dc14 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/PDBFile.cpp @@ -0,0 +1,365 @@ +//===- 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/Raw/PDBFile.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/StreamArray.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" +#include "llvm/DebugInfo/PDB/Raw/NameHashTable.h" +#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +typedef FixedStreamArray<support::ulittle32_t> ulittle_array; +} + +PDBFile::PDBFile(std::unique_ptr<StreamInterface> PdbFileBuffer) + : Buffer(std::move(PdbFileBuffer)), SB(nullptr) {} + +PDBFile::~PDBFile() {} + +uint32_t PDBFile::getBlockSize() const { return SB->BlockSize; } + +uint32_t PDBFile::getFreeBlockMapBlock() const { return SB->FreeBlockMapBlock; } + +uint32_t PDBFile::getBlockCount() const { return SB->NumBlocks; } + +uint32_t PDBFile::getNumDirectoryBytes() const { return SB->NumDirectoryBytes; } + +uint32_t PDBFile::getBlockMapIndex() const { return SB->BlockMapAddr; } + +uint32_t PDBFile::getUnknown1() const { return SB->Unknown1; } + +uint32_t PDBFile::getNumDirectoryBlocks() const { + return msf::bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize); +} + +uint64_t PDBFile::getBlockMapOffset() const { + return (uint64_t)SB->BlockMapAddr * SB->BlockSize; +} + +uint32_t PDBFile::getNumStreams() const { return StreamSizes.size(); } + +uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const { + return StreamSizes[StreamIndex]; +} + +ArrayRef<support::ulittle32_t> +PDBFile::getStreamBlockList(uint32_t StreamIndex) const { + return 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 { + if (Offset >= getBlockSize()) + return make_error<RawError>( + raw_error_code::invalid_block_address, + "setBlockData attempted to write out of block bounds."); + if (Data.size() > getBlockSize() - Offset) + return make_error<RawError>( + raw_error_code::invalid_block_address, + "setBlockData attempted to write out of block bounds."); + + uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize()); + StreamBlockOffset += Offset; + return Buffer->writeBytes(StreamBlockOffset, Data); +} + +Error PDBFile::parseFileHeaders() { + StreamReader Reader(*Buffer); + + 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 = setSuperBlock(SB)) + return EC; + + Reader.setOffset(getBlockMapOffset()); + if (auto EC = Reader.readArray(DirectoryBlocks, getNumDirectoryBlocks())) + return EC; + + return Error::success(); +} + +Error PDBFile::parseStreamData() { + assert(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(*this); + if (!DS) + return DS.takeError(); + StreamReader Reader(**DS); + if (auto EC = Reader.readInteger(NumStreams)) + return EC; + + if (auto EC = Reader.readArray(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, + 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) * SB->BlockSize; + if (BlockEndOffset > getFileSize()) + return make_error<RawError>(raw_error_code::corrupt_file, + "Stream block map is corrupt."); + } + StreamMap.push_back(Blocks); + } + + // We should have read exactly SB->NumDirectoryBytes bytes. + assert(Reader.bytesRemaining() == 0); + DirectoryStream = std::move(*DS); + return Error::success(); +} + +llvm::ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const { + return DirectoryBlocks; +} + +Expected<InfoStream &> PDBFile::getPDBInfoStream() { + if (!Info) { + auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, *this); + 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 = MappedBlockStream::createIndexedStream(StreamDBI, *this); + 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 = MappedBlockStream::createIndexedStream(StreamTPI, *this); + 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 = MappedBlockStream::createIndexedStream(StreamIPI, *this); + 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(); + + uint32_t PublicsStreamNum = DbiS->getPublicSymbolStreamIndex(); + + auto PublicS = + MappedBlockStream::createIndexedStream(PublicsStreamNum, *this); + 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 = + MappedBlockStream::createIndexedStream(SymbolStreamNum, *this); + 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<NameHashTable &> PDBFile::getStringTable() { + if (!StringTable || !StringTableStream) { + auto IS = getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names"); + + if (NameStreamIndex == 0) + return make_error<RawError>(raw_error_code::no_stream); + if (NameStreamIndex >= getNumStreams()) + return make_error<RawError>(raw_error_code::no_stream); + + auto NS = MappedBlockStream::createIndexedStream(NameStreamIndex, *this); + if (!NS) + return NS.takeError(); + + StreamReader Reader(**NS); + auto N = llvm::make_unique<NameHashTable>(); + if (auto EC = N->load(Reader)) + return std::move(EC); + StringTable = std::move(N); + StringTableStream = std::move(*NS); + } + return *StringTable; +} + +Error PDBFile::setSuperBlock(const msf::SuperBlock *Block) { + if (auto EC = msf::validateSuperBlock(*Block)) + 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"); + + SB = Block; + return Error::success(); +} + +Error PDBFile::commit() { + StreamWriter Writer(*Buffer); + + if (auto EC = Writer.writeObject(*SB)) + return EC; + Writer.setOffset(getBlockMapOffset()); + if (auto EC = Writer.writeArray(DirectoryBlocks)) + return EC; + + auto DS = MappedBlockStream::createDirectoryStream(*this); + if (!DS) + return DS.takeError(); + auto DirStream = std::move(*DS); + StreamWriter DW(*DirStream); + if (auto EC = DW.writeInteger(this->getNumStreams())) + return EC; + + if (auto EC = DW.writeArray(StreamSizes)) + return EC; + + for (const auto &Blocks : StreamMap) { + if (auto EC = DW.writeArray(Blocks)) + return EC; + } + + if (Info) { + if (auto EC = Info->commit()) + return EC; + } + + if (Dbi) { + if (auto EC = Dbi->commit()) + return EC; + } + + if (Symbols) { + if (auto EC = Symbols->commit()) + return EC; + } + + if (Publics) { + if (auto EC = Publics->commit()) + return EC; + } + + if (Tpi) { + if (auto EC = Tpi->commit()) + return EC; + } + + if (Ipi) { + if (auto EC = Ipi->commit()) + return EC; + } + + return Buffer->commit(); +} diff --git a/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp b/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp new file mode 100644 index 000000000000..9063fd62d295 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp @@ -0,0 +1,102 @@ +//===- 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/Raw/PDBFileBuilder.h" + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/DebugInfo/CodeView/StreamWriter.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; +using namespace llvm::support; + +PDBFileBuilder::PDBFileBuilder( + std::unique_ptr<codeview::StreamInterface> FileBuffer) + : File(llvm::make_unique<PDBFile>(std::move(FileBuffer))) {} + +Error PDBFileBuilder::initialize(const msf::SuperBlock &Super) { + auto ExpectedMsf = + MsfBuilder::create(File->Allocator, Super.BlockSize, Super.NumBlocks); + if (!ExpectedMsf) + return ExpectedMsf.takeError(); + + auto &MsfResult = *ExpectedMsf; + if (auto EC = MsfResult.setBlockMapAddr(Super.BlockMapAddr)) + return EC; + Msf = llvm::make_unique<MsfBuilder>(std::move(MsfResult)); + Msf->setFreePageMap(Super.FreeBlockMapBlock); + Msf->setUnknown1(Super.Unknown1); + return Error::success(); +} + +MsfBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } + +InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { + if (!Info) + Info = llvm::make_unique<InfoStreamBuilder>(); + return *Info; +} + +DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { + if (!Dbi) + Dbi = llvm::make_unique<DbiStreamBuilder>(); + return *Dbi; +} + +Expected<std::unique_ptr<PDBFile>> PDBFileBuilder::build() { + if (Info) { + uint32_t Length = Info->calculateSerializedLength(); + if (auto EC = Msf->setStreamSize(StreamPDB, Length)) + return std::move(EC); + } + if (Dbi) { + uint32_t Length = Dbi->calculateSerializedLength(); + if (auto EC = Msf->setStreamSize(StreamDBI, Length)) + return std::move(EC); + } + + auto ExpectedLayout = Msf->build(); + if (!ExpectedLayout) + return ExpectedLayout.takeError(); + + const msf::Layout &L = *ExpectedLayout; + File->StreamMap = L.StreamMap; + File->StreamSizes = L.StreamSizes; + File->DirectoryBlocks = L.DirectoryBlocks; + File->SB = L.SB; + + if (Info) { + auto ExpectedInfo = Info->build(*File); + if (!ExpectedInfo) + return ExpectedInfo.takeError(); + File->Info = std::move(*ExpectedInfo); + } + + if (Dbi) { + auto ExpectedDbi = Dbi->build(*File); + if (!ExpectedDbi) + return ExpectedDbi.takeError(); + File->Dbi = std::move(*ExpectedDbi); + } + + if (File->Info && File->Dbi && File->Info->getAge() != File->Dbi->getAge()) + return llvm::make_error<RawError>( + raw_error_code::corrupt_file, + "PDB Stream Age doesn't match Dbi Stream Age!"); + + return std::move(File); +} diff --git a/lib/DebugInfo/PDB/Raw/PublicsStream.cpp b/lib/DebugInfo/PDB/Raw/PublicsStream.cpp new file mode 100644 index 000000000000..af3d2d026b48 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/PublicsStream.cpp @@ -0,0 +1,173 @@ +//===- 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/Raw/PublicsStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/SymbolStream.h" + +#include "llvm/ADT/BitVector.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm; +using namespace llvm::support; +using namespace llvm::pdb; + + +static const unsigned IPHR_HASH = 4096; + +// 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; +}; + +// This is GSIHashHdr. +struct PublicsStream::GSIHashHeader { + enum : unsigned { + HdrSignature = ~0U, + HdrVersion = 0xeffe0000 + 19990810, + }; + ulittle32_t VerSignature; + ulittle32_t VerHdr; + ulittle32_t HrSize; + ulittle32_t NumBuckets; +}; + +PublicsStream::PublicsStream(PDBFile &File, + std::unique_ptr<MappedBlockStream> Stream) + : Pdb(File), Stream(std::move(Stream)) {} + +PublicsStream::~PublicsStream() {} + +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() { + codeview::StreamReader 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 (Reader.readObject(HashHdr)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Publics Stream does not contain a header."); + + // An array of HashRecord follows. Read them. + 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, + "Could not read an HR array")); + + // A bitmap of a fixed length follows. + 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.")); + for (uint8_t B : Bitmap) + NumBuckets += countPopulation(B); + + // We don't yet understand the following data structures completely, + // but we at least know the types and sizes. Here we are trying + // to read the stream till end so that we at least can detect + // corrupted streams. + + // 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.")); + + // 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 llvm::make_range(Iter, Iter); + } + SymbolStream &SS = SymbolS.get(); + + return SS.getSymbols(HadError); +} + +Error PublicsStream::commit() { return Error::success(); } diff --git a/lib/DebugInfo/PDB/Raw/RawError.cpp b/lib/DebugInfo/PDB/Raw/RawError.cpp new file mode 100644 index 000000000000..eb169f70e11c --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/RawError.cpp @@ -0,0 +1,67 @@ +#include "llvm/DebugInfo/PDB/Raw/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 LLVM_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::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::not_writable: + return "The PDB does not support writing."; + 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/lib/DebugInfo/PDB/Raw/RawSession.cpp b/lib/DebugInfo/PDB/Raw/RawSession.cpp new file mode 100644 index 000000000000..455d33140dd4 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/RawSession.cpp @@ -0,0 +1,146 @@ +//===- RawSession.cpp - Raw 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/Raw/RawSession.h" + +#include "llvm/DebugInfo/CodeView/ByteStream.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// We need a class which behaves like an immutable ByteStream, but whose data +// is backed by an llvm::MemoryBuffer. It also needs to own the underlying +// MemoryBuffer, so this simple adapter is a good way to achieve that. +class InputByteStream : public codeview::ByteStream<false> { +public: + explicit InputByteStream(std::unique_ptr<MemoryBuffer> Buffer) + : ByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(), + Buffer->getBuffer().bytes_end())), + MemBuffer(std::move(Buffer)) {} + + std::unique_ptr<MemoryBuffer> MemBuffer; +}; +} + +RawSession::RawSession(std::unique_ptr<PDBFile> PdbFile) + : Pdb(std::move(PdbFile)) {} + +RawSession::~RawSession() {} + +Error RawSession::createFromPdb(StringRef Path, + std::unique_ptr<IPDBSession> &Session) { + + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return llvm::make_error<GenericError>(generic_error_code::invalid_path); + + std::unique_ptr<MemoryBuffer> Buffer = std::move(*ErrorOrBuffer); + auto Stream = llvm::make_unique<InputByteStream>(std::move(Buffer)); + + std::unique_ptr<PDBFile> File(new PDBFile(std::move(Stream))); + if (auto EC = File->parseFileHeaders()) + return EC; + if (auto EC = File->parseStreamData()) + return EC; + + Session.reset(new RawSession(std::move(File))); + + return Error::success(); +} + +Error RawSession::createFromExe(StringRef Path, + std::unique_ptr<IPDBSession> &Session) { + return llvm::make_error<RawError>(raw_error_code::feature_unsupported); +} + +uint64_t RawSession::getLoadAddress() const { return 0; } + +void RawSession::setLoadAddress(uint64_t Address) {} + +std::unique_ptr<PDBSymbolExe> RawSession::getGlobalScope() const { + return nullptr; +} + +std::unique_ptr<PDBSymbol> RawSession::getSymbolById(uint32_t SymbolId) const { + return nullptr; +} + +std::unique_ptr<PDBSymbol> +RawSession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +RawSession::findLineNumbers(const PDBSymbolCompiland &Compiland, + const IPDBSourceFile &File) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumLineNumbers> +RawSession::findLineNumbersByAddress(uint64_t Address, uint32_t Length) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> +RawSession::findSourceFiles(const PDBSymbolCompiland *Compiland, + llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBSourceFile> +RawSession::findOneSourceFile(const PDBSymbolCompiland *Compiland, + llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>> +RawSession::findCompilandsForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<PDBSymbolCompiland> +RawSession::findOneCompilandForSourceFile(llvm::StringRef Pattern, + PDB_NameSearchFlags Flags) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> RawSession::getAllSourceFiles() const { + return nullptr; +} + +std::unique_ptr<IPDBEnumSourceFiles> RawSession::getSourceFilesForCompiland( + const PDBSymbolCompiland &Compiland) const { + return nullptr; +} + +std::unique_ptr<IPDBSourceFile> +RawSession::getSourceFileById(uint32_t FileId) const { + return nullptr; +} + +std::unique_ptr<IPDBEnumDataStreams> RawSession::getDebugStreams() const { + return nullptr; +} diff --git a/lib/DebugInfo/PDB/Raw/SymbolStream.cpp b/lib/DebugInfo/PDB/Raw/SymbolStream.cpp new file mode 100644 index 000000000000..41b2a64bfb14 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/SymbolStream.cpp @@ -0,0 +1,46 @@ +//===- 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/Raw/SymbolStream.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" + +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace llvm::pdb; + +SymbolStream::SymbolStream(std::unique_ptr<MappedBlockStream> Stream) + : Stream(std::move(Stream)) {} + +SymbolStream::~SymbolStream() {} + +Error SymbolStream::reload() { + codeview::StreamReader 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/lib/DebugInfo/PDB/Raw/TpiStream.cpp b/lib/DebugInfo/PDB/Raw/TpiStream.cpp new file mode 100644 index 000000000000..5617e57ccf67 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/TpiStream.cpp @@ -0,0 +1,273 @@ +//===- 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/Raw/TpiStream.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Raw/Hash.h" +#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" +#include "llvm/DebugInfo/PDB/Raw/RawError.h" +#include "llvm/DebugInfo/PDB/Raw/RawTypes.h" + +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::support; +using namespace llvm::pdb; + +namespace { +const uint32_t MinHashBuckets = 0x1000; +const uint32_t MaxHashBuckets = 0x40000; +} + +// This corresponds to `HDR` in PDB/dbi/tpi.h. +struct TpiStream::HeaderInfo { + struct EmbeddedBuf { + little32_t Off; + ulittle32_t Length; + }; + + ulittle32_t Version; + ulittle32_t HeaderSize; + ulittle32_t TypeIndexBegin; + ulittle32_t TypeIndexEnd; + ulittle32_t TypeRecordBytes; + + // The following members correspond to `TpiHash` in PDB/dbi/tpi.h. + ulittle16_t HashStreamIndex; + ulittle16_t HashAuxStreamIndex; + ulittle32_t HashKeySize; + ulittle32_t NumHashBuckets; + + EmbeddedBuf HashValueBuffer; + EmbeddedBuf IndexOffsetBuffer; + EmbeddedBuf HashAdjBuffer; +}; + +TpiStream::TpiStream(const PDBFile &File, + std::unique_ptr<MappedBlockStream> Stream) + : Pdb(File), Stream(std::move(Stream)) {} + +TpiStream::~TpiStream() {} + +// 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, const CVRecord<TypeLeafKind> &RawRec) { + 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(RawRec.RawData); +} + +namespace { +class TpiHashVerifier : public TypeVisitorCallbacks { +public: + TpiHashVerifier(FixedStreamArray<support::ulittle32_t> &HashValues, + uint32_t NumHashBuckets) + : HashValues(HashValues), NumHashBuckets(NumHashBuckets) {} + + Error visitUdtSourceLine(UdtSourceLineRecord &Rec) override { + return verifySourceLine(Rec); + } + + Error visitUdtModSourceLine(UdtModSourceLineRecord &Rec) override { + return verifySourceLine(Rec); + } + + Error visitClass(ClassRecord &Rec) override { return verify(Rec); } + Error visitEnum(EnumRecord &Rec) override { return verify(Rec); } + Error visitUnion(UnionRecord &Rec) override { return verify(Rec); } + + Error visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) override { + ++Index; + RawRecord = &Rec; + return Error::success(); + } + +private: + template <typename T> Error verify(T &Rec) { + uint32_t Hash = getTpiHash(Rec, *RawRecord); + if (Hash % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); + } + + template <typename T> Error verifySourceLine(T &Rec) { + char Buf[4]; + support::endian::write32le(Buf, Rec.getUDT().getIndex()); + uint32_t Hash = hashStringV1(StringRef(Buf, 4)); + if (Hash % NumHashBuckets != HashValues[Index]) + return errorInvalidHash(); + return Error::success(); + } + + Error errorInvalidHash() { + return make_error<RawError>( + raw_error_code::invalid_tpi_hash, + "Type index is 0x" + utohexstr(TypeIndex::FirstNonSimpleIndex + Index)); + } + + FixedStreamArray<support::ulittle32_t> HashValues; + const CVRecord<TypeLeafKind> *RawRecord; + uint32_t NumHashBuckets; + uint32_t Index = -1; +}; +} + +// Verifies that a given type record matches with a given hash value. +// Currently we only verify SRC_LINE records. +Error TpiStream::verifyHashValues() { + TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets); + CVTypeVisitor Visitor(Verifier); + return Visitor.visitTypeStream(TypeRecords); +} + +Error TpiStream::reload() { + StreamReader Reader(*Stream); + + if (Reader.bytesRemaining() < sizeof(HeaderInfo)) + 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(HeaderInfo)) + 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 < MinHashBuckets || + Header->NumHashBuckets > MaxHashBuckets) + 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 >= Pdb.getNumStreams()) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid TPI hash stream index."); + + auto HS = + MappedBlockStream::createIndexedStream(Header->HashStreamIndex, Pdb); + if (!HS) + return HS.takeError(); + StreamReader HSR(**HS); + + uint32_t NumHashValues = Header->HashValueBuffer.Length / sizeof(ulittle32_t); + if (NumHashValues != NumTypeRecords()) + 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; + + HSR.setOffset(Header->HashAdjBuffer.Off); + uint32_t NumHashAdjustments = + Header->HashAdjBuffer.Length / sizeof(TypeIndexOffset); + if (auto EC = HSR.readArray(HashAdjustments, NumHashAdjustments)) + return EC; + + HashStream = std::move(*HS); + + // TPI hash table is a parallel array for the type records. + // Verify that the hash values match with type records. + if (auto EC = verifyHashValues()) + return EC; + + 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::NumTypeRecords() const { + return TypeIndexEnd() - TypeIndexBegin(); +} + +uint16_t TpiStream::getTypeHashStreamIndex() const { + return Header->HashStreamIndex; +} + +uint16_t TpiStream::getTypeHashStreamAuxIndex() const { + return Header->HashAuxStreamIndex; +} + +uint32_t TpiStream::NumHashBuckets() 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; +} + +FixedStreamArray<TypeIndexOffset> +TpiStream::getHashAdjustments() const { + return HashAdjustments; +} + +iterator_range<CVTypeArray::Iterator> +TpiStream::types(bool *HadError) const { + return llvm::make_range(TypeRecords.begin(HadError), TypeRecords.end()); +} + +Error TpiStream::commit() { return Error::success(); } diff --git a/lib/DebugInfo/Symbolize/DIPrinter.cpp b/lib/DebugInfo/Symbolize/DIPrinter.cpp index a9dee7abeed1..be5c603a38ef 100644 --- a/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -13,9 +13,19 @@ //===----------------------------------------------------------------------===// #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 { @@ -26,7 +36,7 @@ static const char kDILineInfoBadString[] = "<invalid>"; static const char kBadString[] = "??"; // Prints source code around in the FileName the Line. -void DIPrinter::printContext(std::string FileName, int64_t Line) { +void DIPrinter::printContext(const std::string &FileName, int64_t Line) { if (PrintSourceContext <= 0) return; @@ -61,7 +71,7 @@ void DIPrinter::print(const DILineInfo &Info, bool Inlined) { if (FunctionName == kDILineInfoBadString) FunctionName = kBadString; - StringRef Delimiter = (PrintPretty == true) ? " at " : "\n"; + StringRef Delimiter = PrintPretty ? " at " : "\n"; StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; OS << Prefix << FunctionName << Delimiter; } @@ -97,5 +107,5 @@ DIPrinter &DIPrinter::operator<<(const DIGlobal &Global) { return *this; } -} -} +} // end namespace symbolize +} // end namespace llvm diff --git a/lib/DebugInfo/Symbolize/Makefile b/lib/DebugInfo/Symbolize/Makefile deleted file mode 100644 index 17aac9396585..000000000000 --- a/lib/DebugInfo/Symbolize/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -##===- lib/DebugInfo/Symbolize/Makefile --------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -LIBRARYNAME = LLVMSymbolize -BUILD_ARCHIVE := 1 - -include $(LEVEL)/Makefile.common - diff --git a/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp index e31462459844..f6940080089f 100644 --- a/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp +++ b/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "SymbolizableObjectFile.h" +#include "llvm/Object/COFF.h" #include "llvm/Object/SymbolSize.h" #include "llvm/Support/DataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" @@ -119,12 +120,15 @@ std::error_code SymbolizableObjectFile::addSymbol(const SymbolRef &Symbol, uint64_t SymbolSize, DataExtractor *OpdExtractor, uint64_t OpdAddress) { - SymbolRef::Type SymbolType = Symbol.getType(); + 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(); - ErrorOr<uint64_t> SymbolAddressOrErr = Symbol.getAddress(); - if (auto EC = SymbolAddressOrErr.getError()) - return EC; + 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 @@ -138,9 +142,9 @@ std::error_code SymbolizableObjectFile::addSymbol(const SymbolRef &Symbol, OpdExtractor->isValidOffsetForAddress(OpdOffset32)) SymbolAddress = OpdExtractor->getAddress(&OpdOffset32); } - ErrorOr<StringRef> SymbolNameOrErr = Symbol.getName(); - if (auto EC = SymbolNameOrErr.getError()) - return EC; + 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.size() > 0 && SymbolName[0] == '_') diff --git a/lib/DebugInfo/Symbolize/Symbolize.cpp b/lib/DebugInfo/Symbolize/Symbolize.cpp index 3da1963bb791..adbe0cb69edb 100644 --- a/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -20,6 +20,7 @@ #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" @@ -31,7 +32,10 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include <stdlib.h> +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <cstring> #if defined(_MSC_VER) #include <Windows.h> @@ -47,12 +51,18 @@ namespace llvm { namespace symbolize { -ErrorOr<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, +Expected<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, uint64_t ModuleOffset) { - auto InfoOrErr = getOrCreateModuleInfo(ModuleName); - if (auto EC = InfoOrErr.getError()) - return EC; - SymbolizableModule *Info = InfoOrErr.get(); + 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. @@ -66,13 +76,19 @@ ErrorOr<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, return LineInfo; } -ErrorOr<DIInliningInfo> +Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, uint64_t ModuleOffset) { - auto InfoOrErr = getOrCreateModuleInfo(ModuleName); - if (auto EC = InfoOrErr.getError()) - return EC; - SymbolizableModule *Info = InfoOrErr.get(); + 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. @@ -90,12 +106,18 @@ LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, return InlinedContext; } -ErrorOr<DIGlobal> LLVMSymbolizer::symbolizeData(const std::string &ModuleName, - uint64_t ModuleOffset) { - auto InfoOrErr = getOrCreateModuleInfo(ModuleName); - if (auto EC = InfoOrErr.getError()) - return EC; - SymbolizableModule *Info = InfoOrErr.get(); +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 @@ -116,11 +138,12 @@ void LLVMSymbolizer::flush() { 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. -static std::string getDarwinDWARFResourceForPath( const std::string &Path, const std::string &Basename) { SmallString<16> ResourceName = StringRef(Path); @@ -132,7 +155,7 @@ std::string getDarwinDWARFResourceForPath( return ResourceName.str(); } -static bool checkFileCRC(StringRef Path, uint32_t CRCHash) { +bool checkFileCRC(StringRef Path, uint32_t CRCHash) { ErrorOr<std::unique_ptr<MemoryBuffer>> MB = MemoryBuffer::getFileOrSTDIN(Path); if (!MB) @@ -140,9 +163,9 @@ static bool checkFileCRC(StringRef Path, uint32_t CRCHash) { return !zlib::isAvailable() || CRCHash == zlib::crc32(MB.get()->getBuffer()); } -static bool findDebugBinary(const std::string &OrigPath, - const std::string &DebuglinkName, uint32_t CRCHash, - std::string &Result) { +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)) { @@ -177,8 +200,8 @@ static bool findDebugBinary(const std::string &OrigPath, return false; } -static bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, - uint32_t &CRCHash) { +bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, + uint32_t &CRCHash) { if (!Obj) return false; for (const SectionRef &Section : Obj->sections()) { @@ -205,7 +228,6 @@ static bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugNam return false; } -static bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, const MachOObjectFile *Obj) { ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid(); @@ -215,6 +237,8 @@ bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, 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 @@ -227,9 +251,14 @@ ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, } for (const auto &Path : DsymPaths) { auto DbgObjOrErr = getOrCreateObject(Path, ArchName); - if (!DbgObjOrErr) + 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; @@ -250,23 +279,27 @@ ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path, if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) return nullptr; auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); - if (!DbgObjOrErr) + if (!DbgObjOrErr) { + // Ignore errors, the file might not exist. + consumeError(DbgObjOrErr.takeError()); return nullptr; + } return DbgObjOrErr.get(); } -ErrorOr<LLVMSymbolizer::ObjectPair> +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()) + if (I != ObjectPairForPathArch.end()) { return I->second; + } auto ObjOrErr = getOrCreateObject(Path, ArchName); - if (auto EC = ObjOrErr.getError()) { - ObjectPairForPathArch.insert( - std::make_pair(std::make_pair(Path, ArchName), EC)); - return EC; + if (!ObjOrErr) { + ObjectPairForPathArch.insert(std::make_pair(std::make_pair(Path, ArchName), + ObjectPair(nullptr, nullptr))); + return ObjOrErr.takeError(); } ObjectFile *Obj = ObjOrErr.get(); @@ -285,40 +318,37 @@ LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, return Res; } -ErrorOr<ObjectFile *> +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()) { - ErrorOr<OwningBinary<Binary>> BinOrErr = createBinary(Path); - if (auto EC = BinOrErr.getError()) { - BinaryForPath.insert(std::make_pair(Path, EC)); - return EC; + 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 if (auto EC = I->second.getError()) { - return EC; } else { - Bin = I->second->getBinary(); + Bin = I->second.getBinary(); } - assert(Bin != nullptr); + if (!Bin) + return static_cast<ObjectFile *>(nullptr); - if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) { + if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) { const auto &I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); if (I != ObjectForUBPathAndArch.end()) { - if (auto EC = I->second.getError()) - return EC; - return I->second->get(); + return I->second.get(); } - ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = UB->getObjectForArch(ArchName); - if (auto EC = ObjOrErr.getError()) { - ObjectForUBPathAndArch.insert( - std::make_pair(std::make_pair(Path, ArchName), EC)); - return EC; + 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), @@ -328,17 +358,14 @@ LLVMSymbolizer::getOrCreateObject(const std::string &Path, if (Bin->isObject()) { return cast<ObjectFile>(Bin); } - return object_error::arch_not_found; + return errorCodeToError(object_error::arch_not_found); } -ErrorOr<SymbolizableModule *> +Expected<SymbolizableModule *> LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { const auto &I = Modules.find(ModuleName); if (I != Modules.end()) { - auto &InfoOrErr = I->second; - if (auto EC = InfoOrErr.getError()) - return EC; - return InfoOrErr->get(); + return I->second.get(); } std::string BinaryName = ModuleName; std::string ArchName = Opts.DefaultArch; @@ -352,21 +379,30 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { } } auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName); - if (auto EC = ObjectsOrErr.getError()) { + if (!ObjectsOrErr) { // Failed to find valid object file. - Modules.insert(std::make_pair(ModuleName, EC)); - return EC; + 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)) { - // If this is a COFF object, assume it contains PDB debug information. If - // we don't find any we will fall back to the DWARF case. - std::unique_ptr<IPDBSession> Session; - PDB_ErrorCode Error = loadDataForEXE(PDB_ReaderType::DIA, - Objects.first->getFileName(), Session); - if (Error == PDB_ErrorCode::Success) { + const debug_pdb_info *PDBInfo; + StringRef PDBFileName; + auto EC = CoffObject->getDebugPDBInfo(PDBInfo, PDBFileName); + if (!EC && PDBInfo != nullptr) { + 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))); } } @@ -375,21 +411,26 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { 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(InfoOrErr))); + Modules.insert(std::make_pair(ModuleName, std::move(SymMod))); assert(InsertResult.second); - if (auto EC = InsertResult.first->second.getError()) - return EC; - return InsertResult.first->second->get(); + 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'. -static StringRef demanglePE32ExternCFunc(StringRef SymbolName) { +StringRef demanglePE32ExternCFunc(StringRef SymbolName) { // Remove any '_' or '@' prefix. char Front = SymbolName.empty() ? '\0' : SymbolName[0]; if (Front == '_' || Front == '@') @@ -412,6 +453,8 @@ static StringRef demanglePE32ExternCFunc(StringRef SymbolName) { 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, |