diff options
Diffstat (limited to 'lib/DebugInfo/CodeView')
21 files changed, 3529 insertions, 205 deletions
diff --git a/lib/DebugInfo/CodeView/ByteStream.cpp b/lib/DebugInfo/CodeView/ByteStream.cpp new file mode 100644 index 0000000000000..2c43bc6958d26 --- /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 cfa0e4d8b401a..47297a9131ee2 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 0000000000000..09f72214c52b1 --- /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 0000000000000..aad1d8b25cd06 --- /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 0000000000000..d59271b2367ec --- /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 91b71cc4b1198..5f229e3d9f944 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 69c7e87330e6d..eb79e8ac9a3fe 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 535bc10b74426..0000000000000 --- 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 9afce92eeb1d7..8b9e73b94ff5f 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 889302556b2df..ae089a3520811 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 0000000000000..2e31ed6b5b7f6 --- /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 0000000000000..6f237ee67fe4b --- /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 0000000000000..ab9206a33ec0a --- /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 0000000000000..64e45487322ee --- /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 0000000000000..f61c6b522f570 --- /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 0000000000000..6763c3d562d72 --- /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 0000000000000..345e2a49888c7 --- /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 0000000000000..f63371e8c14fe --- /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 cbf464fd76687..112612cc85eab 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 0000000000000..ebfda2462be10 --- /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 4af5dcaf72289..647538ee8ceb6 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); } |