diff options
Diffstat (limited to 'include/llvm/DebugInfo/CodeView')
33 files changed, 5237 insertions, 319 deletions
diff --git a/include/llvm/DebugInfo/CodeView/ByteStream.h b/include/llvm/DebugInfo/CodeView/ByteStream.h new file mode 100644 index 0000000000000..f398c93723e7a --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/ByteStream.h @@ -0,0 +1,58 @@ +//===- ByteStream.h - Reads stream data from a byte sequence ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_BYTESTREAM_H +#define LLVM_DEBUGINFO_CODEVIEW_BYTESTREAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/Support/Error.h" +#include <cstdint> +#include <memory> +#include <type_traits> + +namespace llvm { +namespace codeview { +class StreamReader; + +template <bool Writable = false> class ByteStream : public StreamInterface { + typedef typename std::conditional<Writable, MutableArrayRef<uint8_t>, + ArrayRef<uint8_t>>::type ArrayType; + +public: + ByteStream() {} + explicit ByteStream(ArrayType Data) : Data(Data) {} + ~ByteStream() override {} + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override; + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override; + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) const override; + + uint32_t getLength() const override; + + Error commit() const override; + + ArrayRef<uint8_t> data() const { return Data; } + StringRef str() const; + +private: + ArrayType Data; +}; + +extern template class ByteStream<true>; +extern template class ByteStream<false>; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_BYTESTREAM_H diff --git a/include/llvm/DebugInfo/CodeView/CVRecord.h b/include/llvm/DebugInfo/CodeView/CVRecord.h new file mode 100644 index 0000000000000..dba359fcbe82e --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/CVRecord.h @@ -0,0 +1,56 @@ +//===- RecordIterator.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_RECORDITERATOR_H +#define LLVM_DEBUGINFO_CODEVIEW_RECORDITERATOR_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace codeview { + +template <typename Kind> struct CVRecord { + uint32_t Length; + Kind Type; + ArrayRef<uint8_t> Data; + ArrayRef<uint8_t> RawData; +}; + +template <typename Kind> struct VarStreamArrayExtractor<CVRecord<Kind>> { + Error operator()(StreamRef Stream, uint32_t &Len, + CVRecord<Kind> &Item) const { + const RecordPrefix *Prefix = nullptr; + StreamReader Reader(Stream); + uint32_t Offset = Reader.getOffset(); + + if (auto EC = Reader.readObject(Prefix)) + return EC; + Item.Length = Prefix->RecordLen; + if (Item.Length < 2) + return make_error<CodeViewError>(cv_error_code::corrupt_record); + Item.Type = static_cast<Kind>(uint16_t(Prefix->RecordKind)); + + Reader.setOffset(Offset); + if (auto EC = + Reader.readBytes(Item.RawData, Item.Length + sizeof(uint16_t))) + return EC; + Item.Data = Item.RawData.slice(sizeof(RecordPrefix)); + Len = Prefix->RecordLen + 2; + return Error::success(); + } +}; +} +} + +#endif diff --git a/include/llvm/DebugInfo/CodeView/CVSymbolTypes.def b/include/llvm/DebugInfo/CodeView/CVSymbolTypes.def new file mode 100644 index 0000000000000..32813d861d909 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/CVSymbolTypes.def @@ -0,0 +1,258 @@ +//===-- CVLeafTypes.def - All CodeView leaf types ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// See LEAF_ENUM_e in cvinfo.h. This should match the constants there. +// +//===----------------------------------------------------------------------===// + +#ifndef CV_SYMBOL +#define CV_SYMBOL(ename, value) +#endif + +#ifndef SYMBOL_RECORD +#define SYMBOL_RECORD(lf_ename, value, name) CV_SYMBOL(lf_ename, value) +#endif + +#ifndef SYMBOL_RECORD_ALIAS +#define SYMBOL_RECORD_ALIAS(lf_ename, value, name, alias_name) \ + SYMBOL_RECORD(lf_ename, value, name) +#endif + +// 16 bit symbol types. Not very useful, provided only for reference. +CV_SYMBOL(S_COMPILE , 0x0001) +CV_SYMBOL(S_REGISTER_16t , 0x0002) +CV_SYMBOL(S_CONSTANT_16t , 0x0003) +CV_SYMBOL(S_UDT_16t , 0x0004) +CV_SYMBOL(S_SSEARCH , 0x0005) +CV_SYMBOL(S_SKIP , 0x0007) +CV_SYMBOL(S_CVRESERVE , 0x0008) +CV_SYMBOL(S_OBJNAME_ST , 0x0009) +CV_SYMBOL(S_ENDARG , 0x000a) +CV_SYMBOL(S_COBOLUDT_16t , 0x000b) +CV_SYMBOL(S_MANYREG_16t , 0x000c) +CV_SYMBOL(S_RETURN , 0x000d) +CV_SYMBOL(S_ENTRYTHIS , 0x000e) +CV_SYMBOL(S_BPREL16 , 0x0100) +CV_SYMBOL(S_LDATA16 , 0x0101) +CV_SYMBOL(S_GDATA16 , 0x0102) +CV_SYMBOL(S_PUB16 , 0x0103) +CV_SYMBOL(S_LPROC16 , 0x0104) +CV_SYMBOL(S_GPROC16 , 0x0105) +CV_SYMBOL(S_THUNK16 , 0x0106) +CV_SYMBOL(S_BLOCK16 , 0x0107) +CV_SYMBOL(S_WITH16 , 0x0108) +CV_SYMBOL(S_LABEL16 , 0x0109) +CV_SYMBOL(S_CEXMODEL16 , 0x010a) +CV_SYMBOL(S_VFTABLE16 , 0x010b) +CV_SYMBOL(S_REGREL16 , 0x010c) +CV_SYMBOL(S_BPREL32_16t , 0x0200) +CV_SYMBOL(S_LDATA32_16t , 0x0201) +CV_SYMBOL(S_GDATA32_16t , 0x0202) +CV_SYMBOL(S_PUB32_16t , 0x0203) +CV_SYMBOL(S_LPROC32_16t , 0x0204) +CV_SYMBOL(S_GPROC32_16t , 0x0205) +CV_SYMBOL(S_THUNK32_ST , 0x0206) +CV_SYMBOL(S_BLOCK32_ST , 0x0207) +CV_SYMBOL(S_WITH32_ST , 0x0208) +CV_SYMBOL(S_LABEL32_ST , 0x0209) +CV_SYMBOL(S_CEXMODEL32 , 0x020a) +CV_SYMBOL(S_VFTABLE32_16t , 0x020b) +CV_SYMBOL(S_REGREL32_16t , 0x020c) +CV_SYMBOL(S_LTHREAD32_16t , 0x020d) +CV_SYMBOL(S_GTHREAD32_16t , 0x020e) +CV_SYMBOL(S_SLINK32 , 0x020f) +CV_SYMBOL(S_LPROCMIPS_16t , 0x0300) +CV_SYMBOL(S_GPROCMIPS_16t , 0x0301) +CV_SYMBOL(S_PROCREF_ST , 0x0400) +CV_SYMBOL(S_DATAREF_ST , 0x0401) +CV_SYMBOL(S_ALIGN , 0x0402) +CV_SYMBOL(S_LPROCREF_ST , 0x0403) +CV_SYMBOL(S_OEM , 0x0404) + +// All post 16 bit symbol types have the 0x1000 bit set. +CV_SYMBOL(S_TI16_MAX , 0x1000) + +// Mostly unused "start" symbol types. +CV_SYMBOL(S_REGISTER_ST , 0x1001) +CV_SYMBOL(S_CONSTANT_ST , 0x1002) +CV_SYMBOL(S_UDT_ST , 0x1003) +CV_SYMBOL(S_COBOLUDT_ST , 0x1004) +CV_SYMBOL(S_MANYREG_ST , 0x1005) +CV_SYMBOL(S_BPREL32_ST , 0x1006) +CV_SYMBOL(S_LDATA32_ST , 0x1007) +CV_SYMBOL(S_GDATA32_ST , 0x1008) +CV_SYMBOL(S_PUB32_ST , 0x1009) +CV_SYMBOL(S_LPROC32_ST , 0x100a) +CV_SYMBOL(S_GPROC32_ST , 0x100b) +CV_SYMBOL(S_VFTABLE32 , 0x100c) +CV_SYMBOL(S_REGREL32_ST , 0x100d) +CV_SYMBOL(S_LTHREAD32_ST , 0x100e) +CV_SYMBOL(S_GTHREAD32_ST , 0x100f) +CV_SYMBOL(S_LPROCMIPS_ST , 0x1010) +CV_SYMBOL(S_GPROCMIPS_ST , 0x1011) + +CV_SYMBOL(S_COMPILE2_ST , 0x1013) +CV_SYMBOL(S_MANYREG2_ST , 0x1014) +CV_SYMBOL(S_LPROCIA64_ST , 0x1015) +CV_SYMBOL(S_GPROCIA64_ST , 0x1016) +CV_SYMBOL(S_LOCALSLOT_ST , 0x1017) +CV_SYMBOL(S_PARAMSLOT_ST , 0x1018) +CV_SYMBOL(S_ANNOTATION , 0x1019) +CV_SYMBOL(S_GMANPROC_ST , 0x101a) +CV_SYMBOL(S_LMANPROC_ST , 0x101b) +CV_SYMBOL(S_RESERVED1 , 0x101c) +CV_SYMBOL(S_RESERVED2 , 0x101d) +CV_SYMBOL(S_RESERVED3 , 0x101e) +CV_SYMBOL(S_RESERVED4 , 0x101f) +CV_SYMBOL(S_LMANDATA_ST , 0x1020) +CV_SYMBOL(S_GMANDATA_ST , 0x1021) +CV_SYMBOL(S_MANFRAMEREL_ST, 0x1022) +CV_SYMBOL(S_MANREGISTER_ST, 0x1023) +CV_SYMBOL(S_MANSLOT_ST , 0x1024) +CV_SYMBOL(S_MANMANYREG_ST , 0x1025) +CV_SYMBOL(S_MANREGREL_ST , 0x1026) +CV_SYMBOL(S_MANMANYREG2_ST, 0x1027) +CV_SYMBOL(S_MANTYPREF , 0x1028) +CV_SYMBOL(S_UNAMESPACE_ST , 0x1029) + +// End of S_*_ST symbols, which do not appear to be generated by modern +// compilers. +CV_SYMBOL(S_ST_MAX , 0x1100) + + +CV_SYMBOL(S_WITH32 , 0x1104) +CV_SYMBOL(S_MANYREG , 0x110a) +CV_SYMBOL(S_LPROCMIPS , 0x1114) +CV_SYMBOL(S_GPROCMIPS , 0x1115) +CV_SYMBOL(S_MANYREG2 , 0x1117) +CV_SYMBOL(S_LPROCIA64 , 0x1118) +CV_SYMBOL(S_GPROCIA64 , 0x1119) +CV_SYMBOL(S_LOCALSLOT , 0x111a) +CV_SYMBOL(S_PARAMSLOT , 0x111b) + +// Managed code symbols. +CV_SYMBOL(S_MANFRAMEREL , 0x111e) +CV_SYMBOL(S_MANREGISTER , 0x111f) +CV_SYMBOL(S_MANSLOT , 0x1120) +CV_SYMBOL(S_MANMANYREG , 0x1121) +CV_SYMBOL(S_MANREGREL , 0x1122) +CV_SYMBOL(S_MANMANYREG2 , 0x1123) +CV_SYMBOL(S_UNAMESPACE , 0x1124) +CV_SYMBOL(S_DATAREF , 0x1126) +CV_SYMBOL(S_ANNOTATIONREF , 0x1128) +CV_SYMBOL(S_TOKENREF , 0x1129) +CV_SYMBOL(S_GMANPROC , 0x112a) +CV_SYMBOL(S_LMANPROC , 0x112b) +CV_SYMBOL(S_ATTR_FRAMEREL , 0x112e) +CV_SYMBOL(S_ATTR_REGISTER , 0x112f) +CV_SYMBOL(S_ATTR_REGREL , 0x1130) +CV_SYMBOL(S_ATTR_MANYREG , 0x1131) + + +CV_SYMBOL(S_SEPCODE , 0x1132) +CV_SYMBOL(S_LOCAL_2005 , 0x1133) +CV_SYMBOL(S_DEFRANGE_2005 , 0x1134) +CV_SYMBOL(S_DEFRANGE2_2005, 0x1135) +CV_SYMBOL(S_DISCARDED , 0x113b) + +// Current symbol types for most procedures as of this writing. +CV_SYMBOL(S_LPROCMIPS_ID , 0x1148) +CV_SYMBOL(S_GPROCMIPS_ID , 0x1149) +CV_SYMBOL(S_LPROCIA64_ID , 0x114a) +CV_SYMBOL(S_GPROCIA64_ID , 0x114b) + +CV_SYMBOL(S_DEFRANGE_HLSL , 0x1150) +CV_SYMBOL(S_GDATA_HLSL , 0x1151) +CV_SYMBOL(S_LDATA_HLSL , 0x1152) +CV_SYMBOL(S_LOCAL_DPC_GROUPSHARED, 0x1154) +CV_SYMBOL(S_DEFRANGE_DPC_PTR_TAG, 0x1157) +CV_SYMBOL(S_DPC_SYM_TAG_MAP, 0x1158) +CV_SYMBOL(S_ARMSWITCHTABLE , 0x1159) +CV_SYMBOL(S_POGODATA , 0x115c) +CV_SYMBOL(S_INLINESITE2 , 0x115d) +CV_SYMBOL(S_MOD_TYPEREF , 0x115f) +CV_SYMBOL(S_REF_MINIPDB , 0x1160) +CV_SYMBOL(S_PDBMAP , 0x1161) +CV_SYMBOL(S_GDATA_HLSL32 , 0x1162) +CV_SYMBOL(S_LDATA_HLSL32 , 0x1163) +CV_SYMBOL(S_GDATA_HLSL32_EX, 0x1164) +CV_SYMBOL(S_LDATA_HLSL32_EX, 0x1165) + +// Known symbol types +SYMBOL_RECORD(S_END , 0x0006, ScopeEndSym) +SYMBOL_RECORD_ALIAS(S_INLINESITE_END , 0x114e, InlineSiteEnd, ScopeEndSym) +SYMBOL_RECORD_ALIAS(S_PROC_ID_END , 0x114f, ProcEnd, ScopeEndSym) + +SYMBOL_RECORD(S_THUNK32 , 0x1102, Thunk32Sym) +SYMBOL_RECORD(S_TRAMPOLINE , 0x112c, TrampolineSym) +SYMBOL_RECORD(S_SECTION , 0x1136, SectionSym) +SYMBOL_RECORD(S_COFFGROUP , 0x1137, CoffGroupSym) +SYMBOL_RECORD(S_EXPORT , 0x1138, ExportSym) + +SYMBOL_RECORD(S_LPROC32 , 0x110f, ProcSym) +SYMBOL_RECORD_ALIAS(S_GPROC32 , 0x1110, GlobalProcSym, ProcSym) +SYMBOL_RECORD_ALIAS(S_LPROC32_ID , 0x1146, ProcIdSym, ProcSym) +SYMBOL_RECORD_ALIAS(S_GPROC32_ID , 0x1147, GlobalProcIdSym, ProcSym) +SYMBOL_RECORD_ALIAS(S_LPROC32_DPC , 0x1155, DPCProcSym, ProcSym) +SYMBOL_RECORD_ALIAS(S_LPROC32_DPC_ID , 0x1156, DPCProcIdSym, ProcSym) + +SYMBOL_RECORD(S_REGISTER , 0x1106, RegisterSym) +SYMBOL_RECORD(S_PUB32 , 0x110e, PublicSym32) + +SYMBOL_RECORD(S_PROCREF , 0x1125, ProcRefSym) +SYMBOL_RECORD_ALIAS(S_LPROCREF, 0x1127, LocalProcRef, ProcRefSym) + + +SYMBOL_RECORD(S_ENVBLOCK , 0x113d, EnvBlockSym) + +SYMBOL_RECORD(S_INLINESITE , 0x114d, InlineSiteSym) +SYMBOL_RECORD(S_LOCAL , 0x113e, LocalSym) +SYMBOL_RECORD(S_DEFRANGE , 0x113f, DefRangeSym) +SYMBOL_RECORD(S_DEFRANGE_SUBFIELD, 0x1140, DefRangeSubfieldSym) +SYMBOL_RECORD(S_DEFRANGE_REGISTER, 0x1141, DefRangeRegisterSym) +SYMBOL_RECORD(S_DEFRANGE_FRAMEPOINTER_REL, 0x1142, DefRangeFramePointerRelSym) +SYMBOL_RECORD(S_DEFRANGE_SUBFIELD_REGISTER, 0x1143, DefRangeSubfieldRegisterSym) +SYMBOL_RECORD(S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE, 0x1144, DefRangeFramePointerRelFullScopeSym) +SYMBOL_RECORD(S_DEFRANGE_REGISTER_REL, 0x1145, DefRangeRegisterRelSym) +SYMBOL_RECORD(S_BLOCK32 , 0x1103, BlockSym) +SYMBOL_RECORD(S_LABEL32 , 0x1105, LabelSym) +SYMBOL_RECORD(S_OBJNAME , 0x1101, ObjNameSym) +SYMBOL_RECORD(S_COMPILE2 , 0x1116, Compile2Sym) +SYMBOL_RECORD(S_COMPILE3 , 0x113c, Compile3Sym) +SYMBOL_RECORD(S_FRAMEPROC , 0x1012, FrameProcSym) +SYMBOL_RECORD(S_CALLSITEINFO , 0x1139, CallSiteInfoSym) +SYMBOL_RECORD(S_FILESTATIC , 0x1153, FileStaticSym) +SYMBOL_RECORD(S_HEAPALLOCSITE , 0x115e, HeapAllocationSiteSym) +SYMBOL_RECORD(S_FRAMECOOKIE , 0x113a, FrameCookieSym) + +SYMBOL_RECORD(S_CALLEES , 0x115a, CallerSym) +SYMBOL_RECORD_ALIAS(S_CALLERS , 0x115b, CalleeSym, CallerSym) + +SYMBOL_RECORD(S_UDT , 0x1108, UDTSym) +SYMBOL_RECORD_ALIAS(S_COBOLUDT , 0x1109, CobolUDT, UDTSym) + +SYMBOL_RECORD(S_BUILDINFO , 0x114c, BuildInfoSym) +SYMBOL_RECORD(S_BPREL32 , 0x110b, BPRelativeSym) +SYMBOL_RECORD(S_REGREL32 , 0x1111, RegRelativeSym) + +SYMBOL_RECORD(S_CONSTANT , 0x1107, ConstantSym) +SYMBOL_RECORD_ALIAS(S_MANCONSTANT , 0x112d, ManagedConstant, ConstantSym) + +SYMBOL_RECORD(S_LDATA32 , 0x110c, DataSym) +SYMBOL_RECORD_ALIAS(S_GDATA32 , 0x110d, GlobalData, DataSym) +SYMBOL_RECORD_ALIAS(S_LMANDATA , 0x111c, ManagedLocalData, DataSym) +SYMBOL_RECORD_ALIAS(S_GMANDATA , 0x111d, ManagedGlobalData, DataSym) + +SYMBOL_RECORD(S_LTHREAD32 , 0x1112, ThreadLocalDataSym) +SYMBOL_RECORD_ALIAS(S_GTHREAD32 , 0x1113, GlobalTLS, ThreadLocalDataSym) + + +#undef CV_SYMBOL +#undef SYMBOL_RECORD +#undef SYMBOL_RECORD_ALIAS diff --git a/include/llvm/DebugInfo/CodeView/CVSymbolVisitor.h b/include/llvm/DebugInfo/CodeView/CVSymbolVisitor.h new file mode 100644 index 0000000000000..7c88956c984ed --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/CVSymbolVisitor.h @@ -0,0 +1,103 @@ +//===- CVSymbolVisitor.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_CVSYMBOLVISITOR_H +#define LLVM_DEBUGINFO_CODEVIEW_CVSYMBOLVISITOR_H + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorDelegate.h" +#include "llvm/Support/ErrorOr.h" + +namespace llvm { +namespace codeview { + +template <typename Derived> class CVSymbolVisitor { +public: + CVSymbolVisitor(SymbolVisitorDelegate *Delegate) : Delegate(Delegate) {} + + bool hadError() const { return HadError; } + + template <typename T> + bool consumeObject(ArrayRef<uint8_t> &Data, const T *&Res) { + if (Data.size() < sizeof(*Res)) { + HadError = true; + return false; + } + Res = reinterpret_cast<const T *>(Data.data()); + Data = Data.drop_front(sizeof(*Res)); + return true; + } + +/// Actions to take on known symbols. By default, they do nothing. Visit methods +/// for member records take the FieldData by non-const reference and are +/// expected to consume the trailing bytes used by the field. +/// FIXME: Make the visitor interpret the trailing bytes so that clients don't +/// need to. +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + void visit##Name(SymbolRecordKind Kind, Name &Record) {} +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "CVSymbolTypes.def" + + void visitSymbolRecord(const CVRecord<SymbolKind> &Record) { + ArrayRef<uint8_t> Data = Record.Data; + auto *DerivedThis = static_cast<Derived *>(this); + DerivedThis->visitSymbolBegin(Record.Type, Data); + uint32_t RecordOffset = Delegate ? Delegate->getRecordOffset(Data) : 0; + switch (Record.Type) { + default: + DerivedThis->visitUnknownSymbol(Record.Type, Data); + break; +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + SymbolRecordKind RK = static_cast<SymbolRecordKind>(EnumName); \ + auto Result = Name::deserialize(RK, RecordOffset, Data); \ + if (Result.getError()) \ + return parseError(); \ + DerivedThis->visit##Name(Record.Type, *Result); \ + break; \ + } +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + SYMBOL_RECORD(EnumVal, EnumVal, AliasName) +#include "CVSymbolTypes.def" + } + DerivedThis->visitSymbolEnd(Record.Type, Record.Data); + } + + /// Visits the symbol records in Data. Sets the error flag on parse failures. + void visitSymbolStream(const CVSymbolArray &Symbols) { + for (const auto &I : Symbols) { + visitSymbolRecord(I); + if (hadError()) + break; + } + } + + /// Action to take on unknown symbols. By default, they are ignored. + void visitUnknownSymbol(SymbolKind Kind, ArrayRef<uint8_t> Data) {} + + /// Paired begin/end actions for all symbols. Receives all record data, + /// including the fixed-length record prefix. + void visitSymbolBegin(SymbolKind Leaf, ArrayRef<uint8_t> RecordData) {} + void visitSymbolEnd(SymbolKind Leaf, ArrayRef<uint8_t> OriginalSymData) {} + + /// Helper for returning from a void function when the stream is corrupted. + void parseError() { HadError = true; } + +private: + SymbolVisitorDelegate *Delegate; + /// Whether a symbol stream parsing error was encountered. + bool HadError = false; +}; + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_CVSYMBOLVISITOR_H diff --git a/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h b/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h new file mode 100644 index 0000000000000..930ac6930c243 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h @@ -0,0 +1,44 @@ +//===- CVTypeVisitor.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H +#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { + +class CVTypeVisitor { +public: + explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks); + + Error visitTypeRecord(const CVRecord<TypeLeafKind> &Record); + + /// Visits the type records in Data. Sets the error flag on parse failures. + Error visitTypeStream(const CVTypeArray &Types); + + Error skipPadding(ArrayRef<uint8_t> &Data); + + /// Visits individual member records of a field list record. Member records do + /// not describe their own length, and need special handling. + Error visitFieldList(const CVRecord<TypeLeafKind> &Record); + +private: + /// The interface to the class that gets notified of each visitation. + TypeVisitorCallbacks &Callbacks; +}; + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H diff --git a/include/llvm/DebugInfo/CodeView/CodeView.h b/include/llvm/DebugInfo/CodeView/CodeView.h index 7728120d68de4..1ee203b4f8fab 100644 --- a/include/llvm/DebugInfo/CodeView/CodeView.h +++ b/include/llvm/DebugInfo/CodeView/CodeView.h @@ -11,10 +11,156 @@ #define LLVM_DEBUGINFO_CODEVIEW_CODEVIEW_H #include <cinttypes> +#include <type_traits> namespace llvm { namespace codeview { +/// Distinguishes individual records in .debug$T section or PDB type stream. The +/// documentation and headers talk about this as the "leaf" type. +enum class TypeRecordKind : uint16_t { +#define TYPE_RECORD(lf_ename, value, name) name = value, +#include "TypeRecords.def" + // FIXME: Add serialization support + FieldList = 0x1203, +}; + +/// Duplicate copy of the above enum, but using the official CV names. Useful +/// for reference purposes and when dealing with unknown record types. +enum TypeLeafKind : uint16_t { +#define CV_TYPE(name, val) name = val, +#include "TypeRecords.def" +}; + +/// Distinguishes individual records in the Symbols subsection of a .debug$S +/// section. Equivalent to SYM_ENUM_e in cvinfo.h. +enum class SymbolRecordKind : uint16_t { +#define SYMBOL_RECORD(lf_ename, value, name) name = value, +#include "CVSymbolTypes.def" +}; + +/// Duplicate copy of the above enum, but using the official CV names. Useful +/// for reference purposes and when dealing with unknown record types. +enum SymbolKind : uint16_t { +#define CV_SYMBOL(name, val) name = val, +#include "CVSymbolTypes.def" +}; + +#define CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(Class) \ + inline Class operator|(Class a, Class b) { \ + return static_cast<Class>( \ + static_cast<std::underlying_type<Class>::type>(a) | \ + static_cast<std::underlying_type<Class>::type>(b)); \ + } \ + inline Class operator&(Class a, Class b) { \ + return static_cast<Class>( \ + static_cast<std::underlying_type<Class>::type>(a) & \ + static_cast<std::underlying_type<Class>::type>(b)); \ + } \ + inline Class operator~(Class a) { \ + return static_cast<Class>( \ + ~static_cast<std::underlying_type<Class>::type>(a)); \ + } \ + inline Class &operator|=(Class &a, Class b) { \ + a = a | b; \ + return a; \ + } \ + inline Class &operator&=(Class &a, Class b) { \ + a = a & b; \ + return a; \ + } + +/// These values correspond to the CV_CPU_TYPE_e enumeration, and are documented +/// here: https://msdn.microsoft.com/en-us/library/b2fc64ek.aspx +enum class CPUType : uint16_t { + Intel8080 = 0x0, + Intel8086 = 0x1, + Intel80286 = 0x2, + Intel80386 = 0x3, + Intel80486 = 0x4, + Pentium = 0x5, + PentiumPro = 0x6, + Pentium3 = 0x7, + MIPS = 0x10, + MIPS16 = 0x11, + MIPS32 = 0x12, + MIPS64 = 0x13, + MIPSI = 0x14, + MIPSII = 0x15, + MIPSIII = 0x16, + MIPSIV = 0x17, + MIPSV = 0x18, + M68000 = 0x20, + M68010 = 0x21, + M68020 = 0x22, + M68030 = 0x23, + M68040 = 0x24, + Alpha = 0x30, + Alpha21164 = 0x31, + Alpha21164A = 0x32, + Alpha21264 = 0x33, + Alpha21364 = 0x34, + PPC601 = 0x40, + PPC603 = 0x41, + PPC604 = 0x42, + PPC620 = 0x43, + PPCFP = 0x44, + PPCBE = 0x45, + SH3 = 0x50, + SH3E = 0x51, + SH3DSP = 0x52, + SH4 = 0x53, + SHMedia = 0x54, + ARM3 = 0x60, + ARM4 = 0x61, + ARM4T = 0x62, + ARM5 = 0x63, + ARM5T = 0x64, + ARM6 = 0x65, + ARM_XMAC = 0x66, + ARM_WMMX = 0x67, + ARM7 = 0x68, + Omni = 0x70, + Ia64 = 0x80, + Ia64_2 = 0x81, + CEE = 0x90, + AM33 = 0xa0, + M32R = 0xb0, + TriCore = 0xc0, + X64 = 0xd0, + EBC = 0xe0, + Thumb = 0xf0, + ARMNT = 0xf4, + D3D11_Shader = 0x100, +}; + +/// These values correspond to the CV_CFL_LANG enumeration, and are documented +/// here: https://msdn.microsoft.com/en-us/library/bw3aekw6.aspx +enum SourceLanguage : uint8_t { + C = 0x00, + Cpp = 0x01, + Fortran = 0x02, + Masm = 0x03, + Pascal = 0x04, + Basic = 0x05, + Cobol = 0x06, + Link = 0x07, + Cvtres = 0x08, + Cvtpgd = 0x09, + CSharp = 0x0a, + VB = 0x0b, + ILAsm = 0x0c, + Java = 0x0d, + JScript = 0x0e, + MSIL = 0x0f, + HLSL = 0x10 +}; + +/// These values correspond to the CV_call_e enumeration, and are documented +/// at the following locations: +/// https://msdn.microsoft.com/en-us/library/b2fc64ek.aspx +/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680207(v=vs.85).aspx +/// enum class CallingConvention : uint8_t { NearC = 0x00, // near right to left push, caller pops stack FarC = 0x01, // far right to left push, caller pops stack @@ -58,20 +204,7 @@ enum class ClassOptions : uint16_t { Sealed = 0x0400, Intrinsic = 0x2000 }; - -inline ClassOptions operator|(ClassOptions a, ClassOptions b) { - return static_cast<ClassOptions>(static_cast<uint16_t>(a) | - static_cast<uint16_t>(b)); -} - -inline ClassOptions operator&(ClassOptions a, ClassOptions b) { - return static_cast<ClassOptions>(static_cast<uint16_t>(a) & - static_cast<uint16_t>(b)); -} - -inline ClassOptions operator~(ClassOptions a) { - return static_cast<ClassOptions>(~static_cast<uint16_t>(a)); -} +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(ClassOptions) enum class FrameProcedureOptions : uint32_t { None = 0x00000000, @@ -95,22 +228,7 @@ enum class FrameProcedureOptions : uint32_t { GuardCfg = 0x00200000, GuardCfw = 0x00400000 }; - -inline FrameProcedureOptions operator|(FrameProcedureOptions a, - FrameProcedureOptions b) { - return static_cast<FrameProcedureOptions>(static_cast<uint32_t>(a) | - static_cast<uint32_t>(b)); -} - -inline FrameProcedureOptions operator&(FrameProcedureOptions a, - FrameProcedureOptions b) { - return static_cast<FrameProcedureOptions>(static_cast<uint32_t>(a) & - static_cast<uint32_t>(b)); -} - -inline FrameProcedureOptions operator~(FrameProcedureOptions a) { - return static_cast<FrameProcedureOptions>(~static_cast<uint32_t>(a)); -} +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(FrameProcedureOptions) enum class FunctionOptions : uint8_t { None = 0x00, @@ -118,20 +236,7 @@ enum class FunctionOptions : uint8_t { Constructor = 0x02, ConstructorWithVirtualBases = 0x04 }; - -inline FunctionOptions operator|(FunctionOptions a, FunctionOptions b) { - return static_cast<FunctionOptions>(static_cast<uint8_t>(a) | - static_cast<uint8_t>(b)); -} - -inline FunctionOptions operator&(FunctionOptions a, FunctionOptions b) { - return static_cast<FunctionOptions>(static_cast<uint8_t>(a) & - static_cast<uint8_t>(b)); -} - -inline FunctionOptions operator~(FunctionOptions a) { - return static_cast<FunctionOptions>(~static_cast<uint8_t>(a)); -} +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(FunctionOptions) enum class HfaKind : uint8_t { None = 0x00, @@ -140,6 +245,7 @@ enum class HfaKind : uint8_t { Other = 0x03 }; +/// Source-level access specifier. (CV_access_e) enum class MemberAccess : uint8_t { None = 0, Private = 1, @@ -147,6 +253,7 @@ enum class MemberAccess : uint8_t { Public = 3 }; +/// Part of member attribute flags. (CV_methodprop_e) enum class MethodKind : uint8_t { Vanilla = 0x00, Virtual = 0x01, @@ -157,49 +264,30 @@ enum class MethodKind : uint8_t { PureIntroducingVirtual = 0x06 }; +/// Equivalent to CV_fldattr_t bitfield. enum class MethodOptions : uint16_t { None = 0x0000, + AccessMask = 0x0003, + MethodKindMask = 0x001c, Pseudo = 0x0020, + NoInherit = 0x0040, + NoConstruct = 0x0080, CompilerGenerated = 0x0100, Sealed = 0x0200 }; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(MethodOptions) -inline MethodOptions operator|(MethodOptions a, MethodOptions b) { - return static_cast<MethodOptions>(static_cast<uint16_t>(a) | - static_cast<uint16_t>(b)); -} - -inline MethodOptions operator&(MethodOptions a, MethodOptions b) { - return static_cast<MethodOptions>(static_cast<uint16_t>(a) & - static_cast<uint16_t>(b)); -} - -inline MethodOptions operator~(MethodOptions a) { - return static_cast<MethodOptions>(~static_cast<uint16_t>(a)); -} - +/// Equivalent to CV_modifier_t. enum class ModifierOptions : uint16_t { None = 0x0000, Const = 0x0001, Volatile = 0x0002, Unaligned = 0x0004 }; - -inline ModifierOptions operator|(ModifierOptions a, ModifierOptions b) { - return static_cast<ModifierOptions>(static_cast<uint16_t>(a) | - static_cast<uint16_t>(b)); -} - -inline ModifierOptions operator&(ModifierOptions a, ModifierOptions b) { - return static_cast<ModifierOptions>(static_cast<uint16_t>(a) & - static_cast<uint16_t>(b)); -} - -inline ModifierOptions operator~(ModifierOptions a) { - return static_cast<ModifierOptions>(~static_cast<uint16_t>(a)); -} +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(ModifierOptions) enum class ModuleSubstreamKind : uint32_t { + None = 0, Symbols = 0xf1, Lines = 0xf2, StringTable = 0xf3, @@ -207,9 +295,18 @@ enum class ModuleSubstreamKind : uint32_t { FrameData = 0xf5, InlineeLines = 0xf6, CrossScopeImports = 0xf7, - CrossScopeExports = 0xf8 + CrossScopeExports = 0xf8, + + // These appear to relate to .Net assembly info. + ILLines = 0xf9, + FuncMDTokenMap = 0xfa, + TypeMDTokenMap = 0xfb, + MergedAssemblyInput = 0xfc, + + CoffSymbolRVA = 0xfd, }; +/// Equivalent to CV_ptrtype_e. enum class PointerKind : uint8_t { Near16 = 0x00, // 16 bit pointer Far16 = 0x01, // 16:16 far pointer @@ -226,6 +323,7 @@ enum class PointerKind : uint8_t { Near64 = 0x0c // 64 bit pointer }; +/// Equivalent to CV_ptrmode_e. enum class PointerMode : uint8_t { Pointer = 0x00, // "normal" pointer LValueReference = 0x01, // "old" reference @@ -234,6 +332,7 @@ enum class PointerMode : uint8_t { RValueReference = 0x04 // r-value reference }; +/// Equivalent to misc lfPointerAttr bitfields. enum class PointerOptions : uint32_t { None = 0x00000000, Flat32 = 0x00000100, @@ -243,21 +342,9 @@ enum class PointerOptions : uint32_t { Restrict = 0x00001000, WinRTSmartPointer = 0x00080000 }; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(PointerOptions) -inline PointerOptions operator|(PointerOptions a, PointerOptions b) { - return static_cast<PointerOptions>(static_cast<uint16_t>(a) | - static_cast<uint16_t>(b)); -} - -inline PointerOptions operator&(PointerOptions a, PointerOptions b) { - return static_cast<PointerOptions>(static_cast<uint16_t>(a) & - static_cast<uint16_t>(b)); -} - -inline PointerOptions operator~(PointerOptions a) { - return static_cast<PointerOptions>(~static_cast<uint16_t>(a)); -} - +/// Equivalent to CV_pmtype_e. enum class PointerToMemberRepresentation : uint16_t { Unknown = 0x00, // not specified (pre VC8) SingleInheritanceData = 0x01, // member data, single inheritance @@ -270,82 +357,7 @@ enum class PointerToMemberRepresentation : uint16_t { GeneralFunction = 0x08 // member function, most general }; -enum class TypeRecordKind : uint16_t { - None = 0, - - VirtualTableShape = 0x000a, - Label = 0x000e, - EndPrecompiledHeader = 0x0014, - - Modifier = 0x1001, - Pointer = 0x1002, - Procedure = 0x1008, - MemberFunction = 0x1009, - - Oem = 0x100f, - Oem2 = 0x1011, - - ArgumentList = 0x1201, - FieldList = 0x1203, - BitField = 0x1205, - MethodList = 0x1206, - - BaseClass = 0x1400, - VirtualBaseClass = 0x1401, - IndirectVirtualBaseClass = 0x1402, - Index = 0x1404, - VirtualFunctionTablePointer = 0x1409, - - Enumerate = 0x1502, - Array = 0x1503, - Class = 0x1504, - Structure = 0x1505, - Union = 0x1506, - Enum = 0x1507, - Alias = 0x150a, - Member = 0x150d, - StaticMember = 0x150e, - Method = 0x150f, - NestedType = 0x1510, - OneMethod = 0x1511, - VirtualFunctionTable = 0x151d, - - FunctionId = 0x1601, - MemberFunctionId = 0x1602, - BuildInfo = 0x1603, - SubstringList = 0x1604, - StringId = 0x1605, - UdtSourceLine = 0x1606, - - SByte = 0x8000, - Int16 = 0x8001, - UInt16 = 0x8002, - Int32 = 0x8003, - UInt32 = 0x8004, - Single = 0x8005, - Double = 0x8006, - Float80 = 0x8007, - Float128 = 0x8008, - Int64 = 0x8009, - UInt64 = 0x800a, - Float48 = 0x800b, - Complex32 = 0x800c, - Complex64 = 0x800d, - Complex80 = 0x800e, - Complex128 = 0x800f, - VarString = 0x8010, - - Int128 = 0x8017, - UInt128 = 0x8018, - - Decimal = 0x8019, - Date = 0x801a, - Utf8String = 0x801b, - - Float16 = 0x801c -}; - -enum class VirtualTableSlotKind : uint8_t { +enum class VFTableSlotKind : uint8_t { Near16 = 0x00, Far16 = 0x01, This = 0x02, @@ -361,6 +373,177 @@ enum class WindowsRTClassKind : uint8_t { ValueClass = 0x02, Interface = 0x03 }; + +/// Corresponds to CV_LVARFLAGS bitfield. +enum class LocalSymFlags : uint16_t { + None = 0, + IsParameter = 1 << 0, + IsAddressTaken = 1 << 1, + IsCompilerGenerated = 1 << 2, + IsAggregate = 1 << 3, + IsAggregated = 1 << 4, + IsAliased = 1 << 5, + IsAlias = 1 << 6, + IsReturnValue = 1 << 7, + IsOptimizedOut = 1 << 8, + IsEnregisteredGlobal = 1 << 9, + IsEnregisteredStatic = 1 << 10, +}; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(LocalSymFlags) + +/// Corresponds to the CV_PROCFLAGS bitfield. +enum class ProcSymFlags : uint8_t { + None = 0, + HasFP = 1 << 0, + HasIRET = 1 << 1, + HasFRET = 1 << 2, + IsNoReturn = 1 << 3, + IsUnreachable = 1 << 4, + HasCustomCallingConv = 1 << 5, + IsNoInline = 1 << 6, + HasOptimizedDebugInfo = 1 << 7, +}; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(ProcSymFlags) + +/// Corresponds to COMPILESYM2::Flags bitfield. +enum class CompileSym2Flags : uint32_t { + EC = 1 << 8, + NoDbgInfo = 1 << 9, + LTCG = 1 << 10, + NoDataAlign = 1 << 11, + ManagedPresent = 1 << 12, + SecurityChecks = 1 << 13, + HotPatch = 1 << 14, + CVTCIL = 1 << 15, + MSILModule = 1 << 16, +}; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(CompileSym2Flags) + +/// Corresponds to COMPILESYM3::Flags bitfield. +enum class CompileSym3Flags : uint32_t { + EC = 1 << 8, + NoDbgInfo = 1 << 9, + LTCG = 1 << 10, + NoDataAlign = 1 << 11, + ManagedPresent = 1 << 12, + SecurityChecks = 1 << 13, + HotPatch = 1 << 14, + CVTCIL = 1 << 15, + MSILModule = 1 << 16, + Sdl = 1 << 17, + PGO = 1 << 18, + Exp = 1 << 19, +}; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(CompileSym3Flags) + +enum class ExportFlags : uint16_t { + IsConstant = 1 << 0, + IsData = 1 << 1, + IsPrivate = 1 << 2, + HasNoName = 1 << 3, + HasExplicitOrdinal = 1 << 4, + IsForwarder = 1 << 5 +}; +CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(ExportFlags) + +// Corresponds to BinaryAnnotationOpcode enum. +enum class BinaryAnnotationsOpCode : uint32_t { + Invalid, + CodeOffset, + ChangeCodeOffsetBase, + ChangeCodeOffset, + ChangeCodeLength, + ChangeFile, + ChangeLineOffset, + ChangeLineEndDelta, + ChangeRangeKind, + ChangeColumnStart, + ChangeColumnEndDelta, + ChangeCodeOffsetAndLineOffset, + ChangeCodeLengthAndCodeOffset, + ChangeColumnEnd, +}; + +// Corresponds to CV_cookietype_e enum. +enum class FrameCookieKind : uint8_t { + Copy, + XorStackPointer, + XorFramePointer, + XorR13, +}; + +// Corresponds to CV_HREG_e enum. +enum class RegisterId : uint16_t { + Unknown = 0, + VFrame = 30006, + AL = 1, + CL = 2, + DL = 3, + BL = 4, + AH = 5, + CH = 6, + DH = 7, + BH = 8, + AX = 9, + CX = 10, + DX = 11, + BX = 12, + SP = 13, + BP = 14, + SI = 15, + DI = 16, + EAX = 17, + ECX = 18, + EDX = 19, + EBX = 20, + ESP = 21, + EBP = 22, + ESI = 23, + EDI = 24, + ES = 25, + CS = 26, + SS = 27, + DS = 28, + FS = 29, + GS = 30, + IP = 31, + RAX = 328, + RBX = 329, + RCX = 330, + RDX = 331, + RSI = 332, + RDI = 333, + RBP = 334, + RSP = 335, + R8 = 336, + R9 = 337, + R10 = 338, + R11 = 339, + R12 = 340, + R13 = 341, + R14 = 342, + R15 = 343, +}; + +/// These values correspond to the THUNK_ORDINAL enumeration. +enum class ThunkOrdinal { + Standard, + ThisAdjustor, + Vcall, + Pcode, + UnknownLoad, + TrampIncremental, + BranchIsland +}; + +enum class TrampolineType { TrampIncremental, BranchIsland }; + +// These values correspond to the CV_SourceChksum_t enumeration. +enum class FileChecksumKind : uint8_t { None, MD5, SHA1, SHA256 }; + +enum LineFlags : uint32_t { + HaveColumns = 1, // CV_LINES_HAVE_COLUMNS +}; } } diff --git a/include/llvm/DebugInfo/CodeView/CodeViewError.h b/include/llvm/DebugInfo/CodeView/CodeViewError.h new file mode 100644 index 0000000000000..69ff29aab6f11 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/CodeViewError.h @@ -0,0 +1,44 @@ +//===- CodeViewError.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_PDB_CODEVIEW_CODEVIEWERROR_H +#define LLVM_DEBUGINFO_PDB_CODEVIEW_CODEVIEWERROR_H + +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +namespace codeview { +enum class cv_error_code { + unspecified = 1, + insufficient_buffer, + operation_unsupported, + corrupt_record, +}; + +/// Base class for errors originating when parsing raw PDB files +class CodeViewError : public ErrorInfo<CodeViewError> { +public: + static char ID; + CodeViewError(cv_error_code C); + CodeViewError(const std::string &Context); + CodeViewError(cv_error_code C, const std::string &Context); + + void log(raw_ostream &OS) const override; + const std::string &getErrorMessage() const; + std::error_code convertToErrorCode() const override; + +private: + std::string ErrMsg; + cv_error_code Code; +}; +} +} +#endif diff --git a/include/llvm/DebugInfo/CodeView/EnumTables.h b/include/llvm/DebugInfo/CodeView/EnumTables.h new file mode 100644 index 0000000000000..021288e576180 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/EnumTables.h @@ -0,0 +1,42 @@ +//===- EnumTables.h 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_ENUMTABLES_H +#define LLVM_DEBUGINFO_CODEVIEW_ENUMTABLES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/Support/COFF.h" +#include "llvm/Support/ScopedPrinter.h" + +#include <stdint.h> + +namespace llvm { +namespace codeview { +ArrayRef<EnumEntry<SymbolKind>> getSymbolTypeNames(); +ArrayRef<EnumEntry<uint16_t>> getRegisterNames(); +ArrayRef<EnumEntry<uint8_t>> getProcSymFlagNames(); +ArrayRef<EnumEntry<uint16_t>> getLocalFlagNames(); +ArrayRef<EnumEntry<uint8_t>> getFrameCookieKindNames(); +ArrayRef<EnumEntry<SourceLanguage>> getSourceLanguageNames(); +ArrayRef<EnumEntry<uint32_t>> getCompileSym2FlagNames(); +ArrayRef<EnumEntry<uint32_t>> getCompileSym3FlagNames(); +ArrayRef<EnumEntry<uint32_t>> getFileChecksumNames(); +ArrayRef<EnumEntry<unsigned>> getCPUTypeNames(); +ArrayRef<EnumEntry<uint32_t>> getFrameProcSymFlagNames(); +ArrayRef<EnumEntry<uint16_t>> getExportSymFlagNames(); +ArrayRef<EnumEntry<uint32_t>> getModuleSubstreamKindNames(); +ArrayRef<EnumEntry<uint8_t>> getThunkOrdinalNames(); +ArrayRef<EnumEntry<uint16_t>> getTrampolineNames(); +ArrayRef<EnumEntry<COFF::SectionCharacteristics>> +getImageSectionCharacteristicNames(); +} // namespace codeview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_ENUMTABLES_H diff --git a/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h b/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h index 1ed62487aeccc..75a075157d228 100644 --- a/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h +++ b/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h @@ -11,6 +11,7 @@ #define LLVM_DEBUGINFO_CODEVIEW_FIELDLISTRECORDBUILDER_H #include "llvm/DebugInfo/CodeView/ListRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" namespace llvm { namespace codeview { @@ -46,31 +47,17 @@ private: public: FieldListRecordBuilder(); - void writeBaseClass(MemberAccess Access, TypeIndex Type, uint64_t Offset); - void writeEnumerate(MemberAccess Access, uint64_t Value, StringRef Name); - void writeIndirectVirtualBaseClass(MemberAccess Access, TypeIndex Type, - TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, - uint64_t SlotIndex); - void writeMember(MemberAccess Access, TypeIndex Type, uint64_t Offset, - StringRef Name); - void writeOneMethod(MemberAccess Access, MethodKind Kind, - MethodOptions Options, TypeIndex Type, - int32_t VTableSlotOffset, StringRef Name); - void writeOneMethod(const MethodInfo &Method, StringRef Name); - void writeMethod(uint16_t OverloadCount, TypeIndex MethodList, - StringRef Name); - void writeNestedType(TypeIndex Type, StringRef Name); - void writeStaticMember(MemberAccess Access, TypeIndex Type, StringRef Name); - void writeVirtualBaseClass(MemberAccess Access, TypeIndex Type, - TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, - uint64_t SlotIndex); - void writeVirtualBaseClass(TypeRecordKind Kind, MemberAccess Access, - TypeIndex Type, TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, - uint64_t SlotIndex); - void writeVirtualFunctionTablePointer(TypeIndex Type); + void reset() { ListRecordBuilder::reset(); } + + void writeBaseClass(const BaseClassRecord &Record); + void writeEnumerator(const EnumeratorRecord &Record); + void writeDataMember(const DataMemberRecord &Record); + void writeOneMethod(const OneMethodRecord &Record); + void writeOverloadedMethod(const OverloadedMethodRecord &Record); + void writeNestedType(const NestedTypeRecord &Record); + void writeStaticDataMember(const StaticDataMemberRecord &Record); + void writeVirtualBaseClass(const VirtualBaseClassRecord &Record); + void writeVFPtr(const VFPtrRecord &Type); }; } } diff --git a/include/llvm/DebugInfo/CodeView/Line.h b/include/llvm/DebugInfo/CodeView/Line.h index a7cdbdaac32ff..975b503fe30b7 100644 --- a/include/llvm/DebugInfo/CodeView/Line.h +++ b/include/llvm/DebugInfo/CodeView/Line.h @@ -10,24 +10,32 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_LINE_H #define LLVM_DEBUGINFO_CODEVIEW_LINE_H +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Endian.h" #include <cinttypes> namespace llvm { namespace codeview { +using llvm::support::ulittle32_t; + class LineInfo { public: - static const uint32_t AlwaysStepIntoLineNumber = 0xfeefee; - static const uint32_t NeverStepIntoLineNumber = 0xf00f00; + enum : uint32_t { + AlwaysStepIntoLineNumber = 0xfeefee, + NeverStepIntoLineNumber = 0xf00f00 + }; -private: - static const uint32_t StartLineMask = 0x00ffffff; - static const uint32_t EndLineDeltaMask = 0x7f000000; - static const int EndLineDeltaShift = 24; - static const uint32_t StatementFlag = 0x80000000u; + enum : int { EndLineDeltaShift = 24 }; + + enum : uint32_t { + StartLineMask = 0x00ffffff, + EndLineDeltaMask = 0x7f000000, + StatementFlag = 0x80000000u + }; -public: LineInfo(uint32_t StartLine, uint32_t EndLine, bool IsStatement); + LineInfo(uint32_t LineData) : LineData(LineData) {} uint32_t getStartLine() const { return LineData & StartLineMask; } @@ -118,7 +126,29 @@ public: bool isNeverStepInto() const { return LineInf.isNeverStepInto(); } }; -} -} + +enum class InlineeLinesSignature : uint32_t { + Normal, // CV_INLINEE_SOURCE_LINE_SIGNATURE + ExtraFiles // CV_INLINEE_SOURCE_LINE_SIGNATURE_EX +}; + +struct InlineeSourceLine { + TypeIndex Inlinee; // ID of the function that was inlined. + ulittle32_t FileID; // Offset into FileChecksums subsection. + ulittle32_t SourceLineNum; // First line of inlined code. + // If extra files present: + // ulittle32_t ExtraFileCount; + // ulittle32_t Files[]; +}; + +struct FileChecksum { + ulittle32_t FileNameOffset; // Byte offset of filename in global string table. + uint8_t ChecksumSize; // Number of bytes of checksum. + uint8_t ChecksumKind; // FileChecksumKind + // Checksum bytes follow. +}; + +} // namespace codeview +} // namespace llvm #endif diff --git a/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h b/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h index df0a2e08a418b..00bf03d417a2c 100644 --- a/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h +++ b/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h @@ -14,6 +14,7 @@ namespace llvm { namespace codeview { +class TypeTableBuilder; class ListRecordBuilder { private: @@ -28,14 +29,35 @@ protected: public: llvm::StringRef str() { return Builder.str(); } + void reset() { + Builder.reset(Kind); + ContinuationOffsets.clear(); + SubrecordStart = 0; + } + + void writeListContinuation(const ListContinuationRecord &R); + + /// Writes this list record as a possible sequence of records. + TypeIndex writeListRecord(TypeTableBuilder &Table); + protected: void finishSubRecord(); TypeRecordBuilder &getBuilder() { return Builder; } private: + size_t getLastContinuationStart() const { + return ContinuationOffsets.empty() ? 0 : ContinuationOffsets.back(); + } + size_t getLastContinuationEnd() const { return Builder.size(); } + size_t getLastContinuationSize() const { + return getLastContinuationEnd() - getLastContinuationStart(); + } + + TypeRecordKind Kind; TypeRecordBuilder Builder; SmallVector<size_t, 4> ContinuationOffsets; + size_t SubrecordStart = 0; }; } } diff --git a/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h b/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h index 5bfe2a0686722..002f885c7c5ae 100644 --- a/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h +++ b/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h @@ -10,12 +10,9 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H #define LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H -#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" -#include <functional> -#include <memory> -#include <unordered_map> #include <vector> namespace llvm { @@ -23,46 +20,29 @@ namespace codeview { class MemoryTypeTableBuilder : public TypeTableBuilder { public: - class Record { - public: - explicit Record(llvm::StringRef RData); - - const char *data() const { return Data.get(); } - uint16_t size() const { return Size; } - - private: - uint16_t Size; - std::unique_ptr<char[]> Data; - }; - -private: - class RecordHash : std::unary_function<llvm::StringRef, size_t> { - public: - size_t operator()(llvm::StringRef Val) const { - return static_cast<size_t>(llvm::hash_value(Val)); - } - }; - -public: MemoryTypeTableBuilder() {} + bool empty() const { return Records.empty(); } + template <typename TFunc> void ForEachRecord(TFunc Func) { uint32_t Index = TypeIndex::FirstNonSimpleIndex; - for (const std::unique_ptr<Record> &R : Records) { - Func(TypeIndex(Index), R.get()); + for (StringRef R : Records) { + Func(TypeIndex(Index), R); ++Index; } } -private: - virtual TypeIndex writeRecord(llvm::StringRef Data) override; +protected: + TypeIndex writeRecord(llvm::StringRef Data) override; private: - std::vector<std::unique_ptr<Record>> Records; - std::unordered_map<llvm::StringRef, TypeIndex, RecordHash> HashedRecords; + std::vector<StringRef> Records; + BumpPtrAllocator RecordStorage; + DenseMap<StringRef, TypeIndex> HashedRecords; }; -} -} -#endif +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H diff --git a/include/llvm/DebugInfo/CodeView/ModuleSubstream.h b/include/llvm/DebugInfo/CodeView/ModuleSubstream.h new file mode 100644 index 0000000000000..6affac801d4dc --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/ModuleSubstream.h @@ -0,0 +1,87 @@ +//===- ModuleSubstream.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_MODULESUBSTREAM_H +#define LLVM_DEBUGINFO_CODEVIEW_MODULESUBSTREAM_H + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/StreamArray.h" +#include "llvm/DebugInfo/CodeView/StreamRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { + +// Corresponds to the `CV_DebugSSubsectionHeader_t` structure. +struct ModuleSubsectionHeader { + support::ulittle32_t Kind; // codeview::ModuleSubstreamKind enum + support::ulittle32_t Length; // number of bytes occupied by this record. +}; + +// Corresponds to the `CV_DebugSLinesHeader_t` structure. +struct LineSubstreamHeader { + support::ulittle32_t RelocOffset; // Code offset of line contribution. + support::ulittle16_t RelocSegment; // Code segment of line contribution. + support::ulittle16_t Flags; // See LineFlags enumeration. + support::ulittle32_t CodeSize; // Code size of this line contribution. +}; + +// Corresponds to the `CV_DebugSLinesFileBlockHeader_t` structure. +struct LineFileBlockHeader { + support::ulittle32_t NameIndex; // Index in DBI name buffer of filename. + support::ulittle32_t NumLines; // Number of lines + support::ulittle32_t BlockSize; // Code size of block, in bytes. + // The following two variable length arrays appear immediately after the + // header. The structure definitions follow. + // LineNumberEntry Lines[NumLines]; + // ColumnNumberEntry Columns[NumLines]; +}; + +// Corresponds to `CV_Line_t` structure +struct LineNumberEntry { + support::ulittle32_t Offset; // Offset to start of code bytes for line number + support::ulittle32_t Flags; // Start:24, End:7, IsStatement:1 +}; + +// Corresponds to `CV_Column_t` structure +struct ColumnNumberEntry { + support::ulittle16_t StartColumn; + support::ulittle16_t EndColumn; +}; + +class ModuleSubstream { +public: + ModuleSubstream(); + ModuleSubstream(ModuleSubstreamKind Kind, StreamRef Data); + static Error initialize(StreamRef Stream, ModuleSubstream &Info); + uint32_t getRecordLength() const; + ModuleSubstreamKind getSubstreamKind() const; + StreamRef getRecordData() const; + +private: + ModuleSubstreamKind Kind; + StreamRef Data; +}; + +template <> struct VarStreamArrayExtractor<ModuleSubstream> { + Error operator()(StreamRef Stream, uint32_t &Length, + ModuleSubstream &Info) const { + if (auto EC = ModuleSubstream::initialize(Stream, Info)) + return EC; + Length = Info.getRecordLength(); + return Error::success(); + } +}; + +typedef VarStreamArray<ModuleSubstream> ModuleSubstreamArray; +} +} + +#endif // LLVM_DEBUGINFO_CODEVIEW_MODULESUBSTREAM_H diff --git a/include/llvm/DebugInfo/CodeView/ModuleSubstreamVisitor.h b/include/llvm/DebugInfo/CodeView/ModuleSubstreamVisitor.h new file mode 100644 index 0000000000000..6df2309037122 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/ModuleSubstreamVisitor.h @@ -0,0 +1,121 @@ +//===- ModuleSubstreamVisitor.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_MODULESUBSTREAMVISITOR_H +#define LLVM_DEBUGINFO_CODEVIEW_MODULESUBSTREAMVISITOR_H + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/ModuleSubstream.h" +#include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/CodeView/StreamRef.h" + +namespace llvm { +namespace codeview { + +struct LineColumnEntry { + support::ulittle32_t NameIndex; + FixedStreamArray<LineNumberEntry> LineNumbers; + FixedStreamArray<ColumnNumberEntry> Columns; +}; + +template <> class VarStreamArrayExtractor<LineColumnEntry> { +public: + VarStreamArrayExtractor(const LineSubstreamHeader *Header) : Header(Header) {} + + Error operator()(StreamRef Stream, uint32_t &Len, + LineColumnEntry &Item) const { + const LineFileBlockHeader *BlockHeader; + StreamReader Reader(Stream); + if (auto EC = Reader.readObject(BlockHeader)) + return EC; + bool HasColumn = Header->Flags & LineFlags::HaveColumns; + uint32_t LineInfoSize = + BlockHeader->NumLines * + (sizeof(LineNumberEntry) + (HasColumn ? sizeof(ColumnNumberEntry) : 0)); + if (BlockHeader->BlockSize < sizeof(LineFileBlockHeader)) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid line block record size"); + uint32_t Size = BlockHeader->BlockSize - sizeof(LineFileBlockHeader); + if (LineInfoSize > Size) + return make_error<CodeViewError>(cv_error_code::corrupt_record, + "Invalid line block record size"); + // The value recorded in BlockHeader->BlockSize includes the size of + // LineFileBlockHeader. + Len = BlockHeader->BlockSize; + Item.NameIndex = BlockHeader->NameIndex; + if (auto EC = Reader.readArray(Item.LineNumbers, BlockHeader->NumLines)) + return EC; + if (HasColumn) { + if (auto EC = Reader.readArray(Item.Columns, BlockHeader->NumLines)) + return EC; + } + return Error::success(); + } + +private: + const LineSubstreamHeader *Header; +}; + +struct FileChecksumEntry { + uint32_t FileNameOffset; // Byte offset of filename in global stringtable. + FileChecksumKind Kind; // The type of checksum. + ArrayRef<uint8_t> Checksum; // The bytes of the checksum. +}; + +template <> class VarStreamArrayExtractor<FileChecksumEntry> { +public: + Error operator()(StreamRef Stream, uint32_t &Len, + FileChecksumEntry &Item) const { + const FileChecksum *Header; + StreamReader Reader(Stream); + if (auto EC = Reader.readObject(Header)) + return EC; + Item.FileNameOffset = Header->FileNameOffset; + Item.Kind = static_cast<FileChecksumKind>(Header->ChecksumKind); + if (auto EC = Reader.readBytes(Item.Checksum, Header->ChecksumSize)) + return EC; + Len = sizeof(FileChecksum) + Header->ChecksumSize; + return Error::success(); + } +}; + +typedef VarStreamArray<LineColumnEntry> LineInfoArray; +typedef VarStreamArray<FileChecksumEntry> FileChecksumArray; + +class IModuleSubstreamVisitor { +public: + virtual ~IModuleSubstreamVisitor() {} + + virtual Error visitUnknown(ModuleSubstreamKind Kind, StreamRef Data) = 0; + virtual Error visitSymbols(StreamRef Data); + virtual Error visitLines(StreamRef Data, const LineSubstreamHeader *Header, + const LineInfoArray &Lines); + virtual Error visitStringTable(StreamRef Data); + virtual Error visitFileChecksums(StreamRef Data, + const FileChecksumArray &Checksums); + virtual Error visitFrameData(StreamRef Data); + virtual Error visitInlineeLines(StreamRef Data); + virtual Error visitCrossScopeImports(StreamRef Data); + virtual Error visitCrossScopeExports(StreamRef Data); + virtual Error visitILLines(StreamRef Data); + virtual Error visitFuncMDTokenMap(StreamRef Data); + virtual Error visitTypeMDTokenMap(StreamRef Data); + virtual Error visitMergedAssemblyInput(StreamRef Data); + virtual Error visitCoffSymbolRVA(StreamRef Data); +}; + +Error visitModuleSubstream(const ModuleSubstream &R, + IModuleSubstreamVisitor &V); + +} // namespace codeview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_MODULESUBSTREAMVISITOR_H diff --git a/include/llvm/DebugInfo/CodeView/RecordSerialization.h b/include/llvm/DebugInfo/CodeView/RecordSerialization.h new file mode 100644 index 0000000000000..84179f5f81f74 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/RecordSerialization.h @@ -0,0 +1,278 @@ +//===- RecordSerialization.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_RECORDSERIALIZATION_H +#define LLVM_DEBUGINFO_CODEVIEW_RECORDSERIALIZATION_H + +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include <cinttypes> +#include <tuple> + +namespace llvm { +namespace codeview { +using llvm::support::little32_t; +using llvm::support::ulittle16_t; +using llvm::support::ulittle32_t; + +struct RecordPrefix { + ulittle16_t RecordLen; // Record length, starting from &Leaf. + ulittle16_t RecordKind; // Record kind enum (SymRecordKind or TypeRecordKind) +}; + +/// 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 getBytesAsCharacters(ArrayRef<uint8_t> LeafData); +StringRef getBytesAsCString(ArrayRef<uint8_t> LeafData); + +/// Consumes sizeof(T) bytes from the given byte sequence. Returns an error if +/// there are not enough bytes remaining. Reinterprets the consumed bytes as a +/// T object and points 'Res' at them. +template <typename T, typename U> +inline std::error_code consumeObject(U &Data, const T *&Res) { + if (Data.size() < sizeof(*Res)) + return std::make_error_code(std::errc::illegal_byte_sequence); + Res = reinterpret_cast<const T *>(Data.data()); + Data = Data.drop_front(sizeof(*Res)); + return std::error_code(); +} + +inline std::error_code consume(ArrayRef<uint8_t> &Data) { + return std::error_code(); +} + +/// Decodes a numeric "leaf" value. These are integer literals encountered in +/// the type stream. If the value is positive and less than LF_NUMERIC (1 << +/// 15), it is emitted directly in Data. Otherwise, it has a tag like LF_CHAR +/// that indicates the bitwidth and sign of the numeric data. +std::error_code consume(ArrayRef<uint8_t> &Data, APSInt &Num); +std::error_code consume(StringRef &Data, APSInt &Num); + +/// Decodes a numeric leaf value that is known to be a particular type. +std::error_code consume_numeric(ArrayRef<uint8_t> &Data, uint64_t &Value); + +/// Decodes signed and unsigned fixed-length integers. +std::error_code consume(ArrayRef<uint8_t> &Data, uint32_t &Item); +std::error_code consume(StringRef &Data, uint32_t &Item); +std::error_code consume(ArrayRef<uint8_t> &Data, int32_t &Item); + +/// Decodes a null terminated string. +std::error_code consume(ArrayRef<uint8_t> &Data, StringRef &Item); + +/// Decodes an arbitrary object whose layout matches that of the underlying +/// byte sequence, and returns a pointer to the object. +template <typename T> +std::error_code consume(ArrayRef<uint8_t> &Data, T *&Item) { + return consumeObject(Data, Item); +} + +template <typename T, typename U> struct serialize_conditional_impl { + serialize_conditional_impl(T &Item, U Func) : Item(Item), Func(Func) {} + + std::error_code deserialize(ArrayRef<uint8_t> &Data) const { + if (!Func()) + return std::error_code(); + return consume(Data, Item); + } + + T &Item; + U Func; +}; + +template <typename T, typename U> +serialize_conditional_impl<T, U> serialize_conditional(T &Item, U Func) { + return serialize_conditional_impl<T, U>(Item, Func); +} + +template <typename T, typename U> struct serialize_array_impl { + serialize_array_impl(ArrayRef<T> &Item, U Func) : Item(Item), Func(Func) {} + + std::error_code deserialize(ArrayRef<uint8_t> &Data) const { + uint32_t N = Func(); + if (N == 0) + return std::error_code(); + + uint32_t Size = sizeof(T) * N; + + if (Size / sizeof(T) != N) + return std::make_error_code(std::errc::illegal_byte_sequence); + + if (Data.size() < Size) + return std::make_error_code(std::errc::illegal_byte_sequence); + + Item = ArrayRef<T>(reinterpret_cast<const T *>(Data.data()), N); + Data = Data.drop_front(Size); + return std::error_code(); + } + + ArrayRef<T> &Item; + U Func; +}; + +template <typename T> struct serialize_vector_tail_impl { + serialize_vector_tail_impl(std::vector<T> &Item) : Item(Item) {} + + std::error_code deserialize(ArrayRef<uint8_t> &Data) const { + T Field; + // Stop when we run out of bytes or we hit record padding bytes. + while (!Data.empty() && Data.front() < LF_PAD0) { + if (auto EC = consume(Data, Field)) + return EC; + Item.push_back(Field); + } + return std::error_code(); + } + + std::vector<T> &Item; +}; + +struct serialize_null_term_string_array_impl { + serialize_null_term_string_array_impl(std::vector<StringRef> &Item) + : Item(Item) {} + + std::error_code deserialize(ArrayRef<uint8_t> &Data) const { + if (Data.empty()) + return std::make_error_code(std::errc::illegal_byte_sequence); + + StringRef Field; + // Stop when we run out of bytes or we hit record padding bytes. + while (Data.front() != 0) { + if (auto EC = consume(Data, Field)) + return EC; + Item.push_back(Field); + if (Data.empty()) + return std::make_error_code(std::errc::illegal_byte_sequence); + } + Data = Data.drop_front(1); + return std::error_code(); + } + + std::vector<StringRef> &Item; +}; + +template <typename T> struct serialize_arrayref_tail_impl { + serialize_arrayref_tail_impl(ArrayRef<T> &Item) : Item(Item) {} + + std::error_code deserialize(ArrayRef<uint8_t> &Data) const { + uint32_t Count = Data.size() / sizeof(T); + Item = ArrayRef<T>(reinterpret_cast<const T *>(Data.begin()), Count); + return std::error_code(); + } + + ArrayRef<T> &Item; +}; + +template <typename T> struct serialize_numeric_impl { + serialize_numeric_impl(T &Item) : Item(Item) {} + + std::error_code deserialize(ArrayRef<uint8_t> &Data) const { + return consume_numeric(Data, Item); + } + + T &Item; +}; + +template <typename T, typename U> +serialize_array_impl<T, U> serialize_array(ArrayRef<T> &Item, U Func) { + return serialize_array_impl<T, U>(Item, Func); +} + +inline serialize_null_term_string_array_impl +serialize_null_term_string_array(std::vector<StringRef> &Item) { + return serialize_null_term_string_array_impl(Item); +} + +template <typename T> +serialize_vector_tail_impl<T> serialize_array_tail(std::vector<T> &Item) { + return serialize_vector_tail_impl<T>(Item); +} + +template <typename T> +serialize_arrayref_tail_impl<T> serialize_array_tail(ArrayRef<T> &Item) { + return serialize_arrayref_tail_impl<T>(Item); +} + +template <typename T> serialize_numeric_impl<T> serialize_numeric(T &Item) { + return serialize_numeric_impl<T>(Item); +} + +// This field is only present in the byte record if the condition is true. The +// condition is evaluated lazily, so it can depend on items that were +// deserialized +// earlier. +#define CV_CONDITIONAL_FIELD(I, C) \ + serialize_conditional(I, [&]() { return !!(C); }) + +// This is an array of N items, where N is evaluated lazily, so it can refer +// to a field deserialized earlier. +#define CV_ARRAY_FIELD_N(I, N) serialize_array(I, [&]() { return N; }) + +// This is an array that exhausts the remainder of the input buffer. +#define CV_ARRAY_FIELD_TAIL(I) serialize_array_tail(I) + +// This is an array that consumes null terminated strings until a double null +// is encountered. +#define CV_STRING_ARRAY_NULL_TERM(I) serialize_null_term_string_array(I) + +#define CV_NUMERIC_FIELD(I) serialize_numeric(I) + +template <typename T, typename U> +std::error_code consume(ArrayRef<uint8_t> &Data, + const serialize_conditional_impl<T, U> &Item) { + return Item.deserialize(Data); +} + +template <typename T, typename U> +std::error_code consume(ArrayRef<uint8_t> &Data, + const serialize_array_impl<T, U> &Item) { + return Item.deserialize(Data); +} + +inline std::error_code +consume(ArrayRef<uint8_t> &Data, + const serialize_null_term_string_array_impl &Item) { + return Item.deserialize(Data); +} + +template <typename T> +std::error_code consume(ArrayRef<uint8_t> &Data, + const serialize_vector_tail_impl<T> &Item) { + return Item.deserialize(Data); +} + +template <typename T> +std::error_code consume(ArrayRef<uint8_t> &Data, + const serialize_arrayref_tail_impl<T> &Item) { + return Item.deserialize(Data); +} + +template <typename T> +std::error_code consume(ArrayRef<uint8_t> &Data, + const serialize_numeric_impl<T> &Item) { + return Item.deserialize(Data); +} + +template <typename T, typename U, typename... Args> +std::error_code consume(ArrayRef<uint8_t> &Data, T &&X, U &&Y, + Args &&... Rest) { + if (auto EC = consume(Data, X)) + return EC; + return consume(Data, Y, std::forward<Args>(Rest)...); +} + +#define CV_DESERIALIZE(...) \ + if (auto EC = consume(__VA_ARGS__)) \ + return EC; +} +} + +#endif diff --git a/include/llvm/DebugInfo/CodeView/StreamArray.h b/include/llvm/DebugInfo/CodeView/StreamArray.h new file mode 100644 index 0000000000000..0b9349aac7538 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/StreamArray.h @@ -0,0 +1,275 @@ +//===- StreamArray.h - Array backed by an arbitrary stream ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_STREAMARRAY_H +#define LLVM_DEBUGINFO_CODEVIEW_STREAMARRAY_H + +#include "llvm/DebugInfo/CodeView/StreamRef.h" +#include "llvm/Support/Error.h" + +#include <functional> +#include <type_traits> + +namespace llvm { +namespace codeview { + +/// VarStreamArrayExtractor is intended to be specialized to provide customized +/// extraction logic. On input it receives a StreamRef pointing to the +/// beginning of the next record, but where the length of the record is not yet +/// known. Upon completion, it should return an appropriate Error instance if +/// a record could not be extracted, or if one could be extracted it should +/// return success and set Len to the number of bytes this record occupied in +/// the underlying stream, and it should fill out the fields of the value type +/// Item appropriately to represent the current record. +/// +/// You can specialize this template for your own custom value types to avoid +/// having to specify a second template argument to VarStreamArray (documented +/// below). +template <typename T> struct VarStreamArrayExtractor { + // Method intentionally deleted. You must provide an explicit specialization + // with the following method implemented. + Error operator()(StreamRef Stream, uint32_t &Len, T &Item) const = delete; +}; + +/// VarStreamArray represents an array of variable length records backed by a +/// stream. This could be a contiguous sequence of bytes in memory, it could +/// be a file on disk, or it could be a PDB stream where bytes are stored as +/// discontiguous blocks in a file. Usually it is desirable to treat arrays +/// as contiguous blocks of memory, but doing so with large PDB files, for +/// example, could mean allocating huge amounts of memory just to allow +/// re-ordering of stream data to be contiguous before iterating over it. By +/// abstracting this out, we need not duplicate this memory, and we can +/// iterate over arrays in arbitrarily formatted streams. Elements are parsed +/// lazily on iteration, so there is no upfront cost associated with building +/// a VarStreamArray, no matter how large it may be. +/// +/// You create a VarStreamArray by specifying a ValueType and an Extractor type. +/// If you do not specify an Extractor type, it expects you to specialize +/// VarStreamArrayExtractor<T> for your ValueType. +/// +/// By default an Extractor is default constructed in the class, but in some +/// cases you might find it useful for an Extractor to maintain state across +/// extractions. In this case you can provide your own Extractor through a +/// secondary constructor. The following examples show various ways of +/// creating a VarStreamArray. +/// +/// // Will use VarStreamArrayExtractor<MyType> as the extractor. +/// VarStreamArray<MyType> MyTypeArray; +/// +/// // Will use a default-constructed MyExtractor as the extractor. +/// VarStreamArray<MyType, MyExtractor> MyTypeArray2; +/// +/// // Will use the specific instance of MyExtractor provided. +/// // MyExtractor need not be default-constructible in this case. +/// MyExtractor E(SomeContext); +/// VarStreamArray<MyType, MyExtractor> MyTypeArray3(E); +/// +template <typename ValueType, typename Extractor> class VarStreamArrayIterator; + +template <typename ValueType, + typename Extractor = VarStreamArrayExtractor<ValueType>> +class VarStreamArray { + friend class VarStreamArrayIterator<ValueType, Extractor>; + +public: + typedef VarStreamArrayIterator<ValueType, Extractor> Iterator; + + VarStreamArray() {} + explicit VarStreamArray(const Extractor &E) : E(E) {} + + explicit VarStreamArray(StreamRef Stream) : Stream(Stream) {} + VarStreamArray(StreamRef Stream, const Extractor &E) : Stream(Stream), E(E) {} + + VarStreamArray(const VarStreamArray<ValueType, Extractor> &Other) + : Stream(Other.Stream), E(Other.E) {} + + Iterator begin(bool *HadError = nullptr) const { + return Iterator(*this, E, HadError); + } + + Iterator end() const { return Iterator(E); } + + const Extractor &getExtractor() const { return E; } + + StreamRef getUnderlyingStream() const { return Stream; } + +private: + StreamRef Stream; + Extractor E; +}; + +template <typename ValueType, typename Extractor> class VarStreamArrayIterator { + typedef VarStreamArrayIterator<ValueType, Extractor> IterType; + typedef VarStreamArray<ValueType, Extractor> ArrayType; + +public: + VarStreamArrayIterator(const ArrayType &Array, const Extractor &E, + bool *HadError = nullptr) + : IterRef(Array.Stream), Array(&Array), HadError(HadError), Extract(E) { + if (IterRef.getLength() == 0) + moveToEnd(); + else { + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } + } + } + VarStreamArrayIterator() {} + explicit VarStreamArrayIterator(const Extractor &E) : Extract(E) {} + ~VarStreamArrayIterator() {} + + bool operator==(const IterType &R) const { + if (Array && R.Array) { + // Both have a valid array, make sure they're same. + assert(Array == R.Array); + return IterRef == R.IterRef; + } + + // Both iterators are at the end. + if (!Array && !R.Array) + return true; + + // One is not at the end and one is. + return false; + } + + bool operator!=(const IterType &R) { return !(*this == R); } + + const ValueType &operator*() const { + assert(Array && !HasError); + return ThisValue; + } + + IterType &operator++() { + // We are done with the current record, discard it so that we are + // positioned at the next record. + IterRef = IterRef.drop_front(ThisLen); + if (IterRef.getLength() == 0) { + // There is nothing after the current record, we must make this an end + // iterator. + moveToEnd(); + } else { + // There is some data after the current record. + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } else if (ThisLen == 0) { + // An empty record? Make this an end iterator. + moveToEnd(); + } + } + return *this; + } + + IterType operator++(int) { + IterType Original = *this; + ++*this; + return Original; + } + +private: + void moveToEnd() { + Array = nullptr; + ThisLen = 0; + } + void markError() { + moveToEnd(); + HasError = true; + if (HadError != nullptr) + *HadError = true; + } + + ValueType ThisValue; + StreamRef IterRef; + const ArrayType *Array{nullptr}; + uint32_t ThisLen{0}; + bool HasError{false}; + bool *HadError{nullptr}; + Extractor Extract; +}; + +template <typename T> class FixedStreamArrayIterator; + +template <typename T> class FixedStreamArray { + friend class FixedStreamArrayIterator<T>; + +public: + FixedStreamArray() : Stream() {} + FixedStreamArray(StreamRef Stream) : Stream(Stream) { + assert(Stream.getLength() % sizeof(T) == 0); + } + + const T &operator[](uint32_t Index) const { + assert(Index < size()); + uint32_t Off = Index * sizeof(T); + ArrayRef<uint8_t> Data; + if (auto EC = Stream.readBytes(Off, sizeof(T), Data)) { + assert(false && "Unexpected failure reading from stream"); + // This should never happen since we asserted that the stream length was + // an exact multiple of the element size. + consumeError(std::move(EC)); + } + return *reinterpret_cast<const T *>(Data.data()); + } + + uint32_t size() const { return Stream.getLength() / sizeof(T); } + + FixedStreamArrayIterator<T> begin() const { + return FixedStreamArrayIterator<T>(*this, 0); + } + FixedStreamArrayIterator<T> end() const { + return FixedStreamArrayIterator<T>(*this, size()); + } + + StreamRef getUnderlyingStream() const { return Stream; } + +private: + StreamRef Stream; +}; + +template <typename T> class FixedStreamArrayIterator { +public: + FixedStreamArrayIterator(const FixedStreamArray<T> &Array, uint32_t Index) + : Array(Array), Index(Index) {} + + bool operator==(const FixedStreamArrayIterator<T> &R) { + assert(&Array == &R.Array); + return Index == R.Index; + } + + bool operator!=(const FixedStreamArrayIterator<T> &R) { + return !(*this == R); + } + + const T &operator*() const { return Array[Index]; } + + FixedStreamArrayIterator<T> &operator++() { + assert(Index < Array.size()); + ++Index; + return *this; + } + + FixedStreamArrayIterator<T> operator++(int) { + FixedStreamArrayIterator<T> Original = *this; + ++*this; + return Original; + } + +private: + const FixedStreamArray<T> &Array; + uint32_t Index; +}; + +} // namespace codeview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_STREAMARRAY_H diff --git a/include/llvm/DebugInfo/CodeView/StreamInterface.h b/include/llvm/DebugInfo/CodeView/StreamInterface.h new file mode 100644 index 0000000000000..241aec4578700 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/StreamInterface.h @@ -0,0 +1,55 @@ +//===- StreamInterface.h - Base interface for a stream of data --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_STREAMINTERFACE_H +#define LLVM_DEBUGINFO_CODEVIEW_STREAMINTERFACE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Error.h" +#include <cstdint> + +namespace llvm { +namespace codeview { + +/// StreamInterface abstracts the notion of a data stream. This way, an +/// implementation could implement trivial reading from a contiguous memory +/// buffer or, as in the case of PDB files, reading from a set of possibly +/// discontiguous blocks. The implementation is required to return references +/// to stable memory, so if this is not possible (for example in the case of +/// a PDB file with discontiguous blocks, it must keep its own pool of temp +/// storage. +class StreamInterface { +public: + virtual ~StreamInterface() {} + + // Given an offset into the stream and a number of bytes, attempt to read + // the bytes and set the output ArrayRef to point to a reference into the + // stream, without copying any data. + virtual Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const = 0; + + // Given an offset into the stream, read as much as possible without copying + // any data. + virtual Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const = 0; + + // Attempt to write the given bytes into the stream at the desired offset. + // This will always necessitate a copy. Cannot shrink or grow the stream, + // only writes into existing allocated space. + virtual Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const = 0; + + virtual uint32_t getLength() const = 0; + + virtual Error commit() const = 0; +}; + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_STREAMINTERFACE_H diff --git a/include/llvm/DebugInfo/CodeView/StreamReader.h b/include/llvm/DebugInfo/CodeView/StreamReader.h new file mode 100644 index 0000000000000..2f497c2c43f14 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/StreamReader.h @@ -0,0 +1,111 @@ +//===- StreamReader.h - Reads bytes and objects from a stream ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_STREAMREADER_H +#define LLVM_DEBUGINFO_CODEVIEW_STREAMREADER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/StreamArray.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +namespace codeview { + +class StreamRef; + +class StreamReader { +public: + StreamReader(StreamRef Stream); + + Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer); + Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size); + Error readInteger(uint16_t &Dest); + Error readInteger(uint32_t &Dest); + Error readZeroString(StringRef &Dest); + Error readFixedString(StringRef &Dest, uint32_t Length); + Error readStreamRef(StreamRef &Ref); + Error readStreamRef(StreamRef &Ref, uint32_t Length); + + template <typename T> Error readEnum(T &Dest) { + typename std::underlying_type<T>::type N; + if (auto EC = readInteger(N)) + return EC; + Dest = static_cast<T>(N); + return Error::success(); + } + + template <typename T> Error readObject(const T *&Dest) { + ArrayRef<uint8_t> Buffer; + if (auto EC = readBytes(Buffer, sizeof(T))) + return EC; + Dest = reinterpret_cast<const T *>(Buffer.data()); + return Error::success(); + } + + template <typename T> + Error readArray(ArrayRef<T> &Array, uint32_t NumElements) { + ArrayRef<uint8_t> Bytes; + if (NumElements == 0) { + Array = ArrayRef<T>(); + return Error::success(); + } + + if (NumElements > UINT32_MAX/sizeof(T)) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + + if (auto EC = readBytes(Bytes, NumElements * sizeof(T))) + return EC; + Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements); + return Error::success(); + } + + template <typename T, typename U> + Error readArray(VarStreamArray<T, U> &Array, uint32_t Size) { + StreamRef S; + if (auto EC = readStreamRef(S, Size)) + return EC; + Array = VarStreamArray<T, U>(S, Array.getExtractor()); + return Error::success(); + } + + template <typename T> + Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) { + if (NumItems == 0) { + Array = FixedStreamArray<T>(); + return Error::success(); + } + uint32_t Length = NumItems * sizeof(T); + if (Length / sizeof(T) != NumItems) + return make_error<CodeViewError>(cv_error_code::corrupt_record); + if (Offset + Length > Stream.getLength()) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + StreamRef View = Stream.slice(Offset, Length); + Array = FixedStreamArray<T>(View); + Offset += Length; + return Error::success(); + } + + void setOffset(uint32_t Off) { Offset = Off; } + uint32_t getOffset() const { return Offset; } + uint32_t getLength() const { return Stream.getLength(); } + uint32_t bytesRemaining() const { return getLength() - getOffset(); } + +private: + StreamRef Stream; + uint32_t Offset; +}; +} // namespace codeview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_STREAMREADER_H diff --git a/include/llvm/DebugInfo/CodeView/StreamRef.h b/include/llvm/DebugInfo/CodeView/StreamRef.h new file mode 100644 index 0000000000000..a4f244a322899 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/StreamRef.h @@ -0,0 +1,104 @@ +//===- StreamRef.h - A copyable reference to a stream -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_STREAMREF_H +#define LLVM_DEBUGINFO_CODEVIEW_STREAMREF_H + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" + +namespace llvm { +namespace codeview { + +class StreamRef { +public: + StreamRef() : Stream(nullptr), ViewOffset(0), Length(0) {} + StreamRef(const StreamInterface &Stream) + : Stream(&Stream), ViewOffset(0), Length(Stream.getLength()) {} + StreamRef(const StreamInterface &Stream, uint32_t Offset, uint32_t Length) + : Stream(&Stream), ViewOffset(Offset), Length(Length) {} + + // Use StreamRef.slice() instead. + StreamRef(const StreamRef &S, uint32_t Offset, uint32_t Length) = delete; + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const { + if (ViewOffset + Offset < Offset) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + if (Size + Offset > Length) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + return Stream->readBytes(ViewOffset + Offset, Size, Buffer); + } + + // Given an offset into the stream, read as much as possible without copying + // any data. + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const { + if (Offset >= Length) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + + if (auto EC = Stream->readLongestContiguousChunk(Offset, Buffer)) + return EC; + // This StreamRef might refer to a smaller window over a larger stream. In + // that case we will have read out more bytes than we should return, because + // we should not read past the end of the current view. + uint32_t MaxLength = Length - Offset; + if (Buffer.size() > MaxLength) + Buffer = Buffer.slice(0, MaxLength); + return Error::success(); + } + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const { + if (Data.size() + Offset > Length) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + return Stream->writeBytes(ViewOffset + Offset, Data); + } + + uint32_t getLength() const { return Length; } + + Error commit() const { return Stream->commit(); } + + StreamRef drop_front(uint32_t N) const { + if (!Stream) + return StreamRef(); + + N = std::min(N, Length); + return StreamRef(*Stream, ViewOffset + N, Length - N); + } + + StreamRef keep_front(uint32_t N) const { + if (!Stream) + return StreamRef(); + N = std::min(N, Length); + return StreamRef(*Stream, ViewOffset, N); + } + + StreamRef slice(uint32_t Offset, uint32_t Len) const { + return drop_front(Offset).keep_front(Len); + } + + bool operator==(const StreamRef &Other) const { + if (Stream != Other.Stream) + return false; + if (ViewOffset != Other.ViewOffset) + return false; + if (Length != Other.Length) + return false; + return true; + } + +private: + const StreamInterface *Stream; + uint32_t ViewOffset; + uint32_t Length; +}; +} +} + +#endif // LLVM_DEBUGINFO_CODEVIEW_STREAMREF_H diff --git a/include/llvm/DebugInfo/CodeView/StreamWriter.h b/include/llvm/DebugInfo/CodeView/StreamWriter.h new file mode 100644 index 0000000000000..4d393d2ef7905 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/StreamWriter.h @@ -0,0 +1,86 @@ +//===- StreamWriter.h - Writes bytes and objects to a stream ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_STREAMWRITER_H +#define LLVM_DEBUGINFO_CODEVIEW_STREAMWRITER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/StreamArray.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +namespace codeview { + +class StreamRef; + +class StreamWriter { +public: + StreamWriter(StreamRef Stream); + + Error writeBytes(ArrayRef<uint8_t> Buffer); + Error writeInteger(uint16_t Dest); + Error writeInteger(uint32_t Dest); + Error writeZeroString(StringRef Str); + Error writeFixedString(StringRef Str); + Error writeStreamRef(StreamRef Ref); + Error writeStreamRef(StreamRef Ref, uint32_t Size); + + template <typename T> Error writeEnum(T Num) { + return writeInteger( + static_cast<typename std::underlying_type<T>::type>(Num)); + } + + template <typename T> Error writeObject(const T &Obj) { + static_assert(!std::is_pointer<T>::value, + "writeObject should not be used with pointers, to write " + "the pointed-to value dereference the pointer before calling " + "writeObject"); + return writeBytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&Obj), sizeof(T))); + } + + template <typename T> Error writeArray(ArrayRef<T> Array) { + if (Array.size() == 0) + return Error::success(); + + if (Array.size() > UINT32_MAX / sizeof(T)) + return make_error<CodeViewError>(cv_error_code::insufficient_buffer); + + return writeBytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Array.data()), + Array.size() * sizeof(T))); + } + + template <typename T, typename U> + Error writeArray(VarStreamArray<T, U> Array) { + return writeStreamRef(Array.getUnderlyingStream()); + } + + template <typename T> Error writeArray(FixedStreamArray<T> Array) { + return writeStreamRef(Array.getUnderlyingStream()); + } + + void setOffset(uint32_t Off) { Offset = Off; } + uint32_t getOffset() const { return Offset; } + uint32_t getLength() const { return Stream.getLength(); } + uint32_t bytesRemaining() const { return getLength() - getOffset(); } + +private: + StreamRef Stream; + uint32_t Offset; +}; +} // namespace codeview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_STREAMREADER_H diff --git a/include/llvm/DebugInfo/CodeView/SymbolDumpDelegate.h b/include/llvm/DebugInfo/CodeView/SymbolDumpDelegate.h new file mode 100644 index 0000000000000..30b0a40451cb8 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/SymbolDumpDelegate.h @@ -0,0 +1,37 @@ +//===-- SymbolDumpDelegate.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_SYMBOLDUMPDELEGATE_H +#define LLVM_DEBUGINFO_CODEVIEW_SYMBOLDUMPDELEGATE_H + +#include "SymbolVisitorDelegate.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +#include <stdint.h> + +namespace llvm { + +namespace codeview { + +class SymbolDumpDelegate : public SymbolVisitorDelegate { +public: + virtual ~SymbolDumpDelegate() {} + + virtual void printRelocatedField(StringRef Label, uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym = nullptr) = 0; + virtual void printBinaryBlockWithRelocs(StringRef Label, + ArrayRef<uint8_t> Block) = 0; +}; +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_SYMBOLDUMPDELEGATE_H diff --git a/include/llvm/DebugInfo/CodeView/SymbolDumper.h b/include/llvm/DebugInfo/CodeView/SymbolDumper.h new file mode 100644 index 0000000000000..648e40f55810b --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/SymbolDumper.h @@ -0,0 +1,54 @@ +//===-- SymbolDumper.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_SYMBOLDUMPER_H +#define LLVM_DEBUGINFO_CODEVIEW_SYMBOLDUMPER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" + +namespace llvm { +class ScopedPrinter; + +namespace codeview { +class CVTypeDumper; + +/// Dumper for CodeView symbol streams found in COFF object files and PDB files. +class CVSymbolDumper { +public: + CVSymbolDumper(ScopedPrinter &W, CVTypeDumper &CVTD, + std::unique_ptr<SymbolDumpDelegate> ObjDelegate, + bool PrintRecordBytes) + : W(W), CVTD(CVTD), ObjDelegate(std::move(ObjDelegate)), + PrintRecordBytes(PrintRecordBytes) {} + + /// Dumps one type record. Returns false if there was a type parsing error, + /// and true otherwise. This should be called in order, since the dumper + /// maintains state about previous records which are necessary for cross + /// type references. + bool dump(const CVRecord<SymbolKind> &Record); + + /// Dumps the type records in Data. Returns false if there was a type stream + /// parse error, and true otherwise. + bool dump(const CVSymbolArray &Symbols); + +private: + ScopedPrinter &W; + CVTypeDumper &CVTD; + std::unique_ptr<SymbolDumpDelegate> ObjDelegate; + + bool PrintRecordBytes; +}; +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_SYMBOLDUMPER_H diff --git a/include/llvm/DebugInfo/CodeView/SymbolRecord.h b/include/llvm/DebugInfo/CodeView/SymbolRecord.h new file mode 100644 index 0000000000000..77e894fba4a9b --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/SymbolRecord.h @@ -0,0 +1,1452 @@ +//===- SymbolRecord.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_SYMBOLRECORD_H +#define LLVM_DEBUGINFO_CODEVIEW_SYMBOLRECORD_H + +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/StreamArray.h" +#include "llvm/DebugInfo/CodeView/StreamInterface.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { + +using llvm::support::ulittle16_t; +using llvm::support::ulittle32_t; +using llvm::support::little32_t; + +class SymbolRecord { +protected: + explicit SymbolRecord(SymbolRecordKind Kind) : Kind(Kind) {} + +public: + SymbolRecordKind getKind() const { return Kind; } + +private: + SymbolRecordKind Kind; +}; + +// S_GPROC32, S_LPROC32, S_GPROC32_ID, S_LPROC32_ID, S_LPROC32_DPC or +// S_LPROC32_DPC_ID +class ProcSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t PtrParent; + ulittle32_t PtrEnd; + ulittle32_t PtrNext; + ulittle32_t CodeSize; + ulittle32_t DbgStart; + ulittle32_t DbgEnd; + TypeIndex FunctionType; + ulittle32_t CodeOffset; + ulittle16_t Segment; + uint8_t Flags; // ProcSymFlags enum + // Name: The null-terminated name follows. + }; + + ProcSym(SymbolRecordKind Kind, uint32_t RecordOffset, const Hdr *H, + StringRef Name) + : SymbolRecord(Kind), RecordOffset(RecordOffset), Header(*H), Name(Name) { + } + + static ErrorOr<ProcSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return ProcSym(Kind, RecordOffset, H, Name); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, CodeOffset); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_THUNK32 +class Thunk32Sym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Parent; + ulittle32_t End; + ulittle32_t Next; + ulittle32_t Off; + ulittle16_t Seg; + ulittle16_t Len; + uint8_t Ord; // ThunkOrdinal enumeration + // Name: The null-terminated name follows. + // Variant portion of thunk + }; + + Thunk32Sym(SymbolRecordKind Kind, uint32_t RecordOffset, const Hdr *H, + StringRef Name, ArrayRef<uint8_t> VariantData) + : SymbolRecord(Kind), RecordOffset(RecordOffset), Header(*H), Name(Name), + VariantData(VariantData) {} + + static ErrorOr<Thunk32Sym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + ArrayRef<uint8_t> VariantData; + + CV_DESERIALIZE(Data, H, Name, CV_ARRAY_FIELD_TAIL(VariantData)); + + return Thunk32Sym(Kind, RecordOffset, H, Name, VariantData); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; + ArrayRef<uint8_t> VariantData; +}; + +// S_TRAMPOLINE +class TrampolineSym : public SymbolRecord { +public: + struct Hdr { + ulittle16_t Type; // TrampolineType enum + ulittle16_t Size; + ulittle32_t ThunkOff; + ulittle32_t TargetOff; + ulittle16_t ThunkSection; + ulittle16_t TargetSection; + }; + + TrampolineSym(SymbolRecordKind Kind, uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(Kind), RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<TrampolineSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + + CV_DESERIALIZE(Data, H); + + return TrampolineSym(Kind, RecordOffset, H); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_SECTION +class SectionSym : public SymbolRecord { +public: + struct Hdr { + ulittle16_t SectionNumber; + uint8_t Alignment; + uint8_t Reserved; // Must be 0 + ulittle32_t Rva; + ulittle32_t Length; + ulittle32_t Characteristics; + // Name: The null-terminated name follows. + }; + + SectionSym(SymbolRecordKind Kind, uint32_t RecordOffset, const Hdr *H, + StringRef Name) + : SymbolRecord(Kind), RecordOffset(RecordOffset), Header(*H), Name(Name) { + } + + static ErrorOr<SectionSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + + CV_DESERIALIZE(Data, H, Name); + + return SectionSym(Kind, RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_COFFGROUP +class CoffGroupSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Size; + ulittle32_t Characteristics; + ulittle32_t Offset; + ulittle16_t Segment; + // Name: The null-terminated name follows. + }; + + CoffGroupSym(SymbolRecordKind Kind, uint32_t RecordOffset, const Hdr *H, + StringRef Name) + : SymbolRecord(Kind), RecordOffset(RecordOffset), Header(*H), Name(Name) { + } + + static ErrorOr<CoffGroupSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + + CV_DESERIALIZE(Data, H, Name); + + return CoffGroupSym(Kind, RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +class ScopeEndSym : public SymbolRecord { +public: + ScopeEndSym(SymbolRecordKind Kind, uint32_t RecordOffset) + : SymbolRecord(Kind), RecordOffset(RecordOffset) {} + + static ErrorOr<ScopeEndSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + return ScopeEndSym(Kind, RecordOffset); + } + uint32_t RecordOffset; +}; + +class CallerSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Count; + }; + + CallerSym(SymbolRecordKind Kind, uint32_t RecordOffset, const Hdr *Header, + ArrayRef<TypeIndex> Indices) + : SymbolRecord(Kind), RecordOffset(RecordOffset), Header(*Header), + Indices(Indices) {} + + static ErrorOr<CallerSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *Header; + ArrayRef<TypeIndex> Indices; + + CV_DESERIALIZE(Data, Header, CV_ARRAY_FIELD_N(Indices, Header->Count)); + + return CallerSym(Kind, RecordOffset, Header, Indices); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<TypeIndex> Indices; +}; + +struct BinaryAnnotationIterator { + struct AnnotationData { + BinaryAnnotationsOpCode OpCode; + StringRef Name; + uint32_t U1; + uint32_t U2; + int32_t S1; + }; + + BinaryAnnotationIterator(ArrayRef<uint8_t> Annotations) : Data(Annotations) {} + BinaryAnnotationIterator() {} + BinaryAnnotationIterator(const BinaryAnnotationIterator &Other) + : Data(Other.Data) {} + + bool operator==(BinaryAnnotationIterator Other) const { + return Data == Other.Data; + } + + bool operator!=(BinaryAnnotationIterator Other) const { + return !(*this == Other); + } + + BinaryAnnotationIterator &operator=(const BinaryAnnotationIterator Other) { + Data = Other.Data; + return *this; + } + + BinaryAnnotationIterator &operator++() { + if (!ParseCurrentAnnotation()) { + *this = BinaryAnnotationIterator(); + return *this; + } + Data = Next; + Next = ArrayRef<uint8_t>(); + Current.reset(); + return *this; + } + + BinaryAnnotationIterator operator++(int) { + BinaryAnnotationIterator Orig(*this); + ++(*this); + return Orig; + } + + const AnnotationData &operator*() { + ParseCurrentAnnotation(); + return Current.getValue(); + } + +private: + static uint32_t GetCompressedAnnotation(ArrayRef<uint8_t> &Annotations) { + if (Annotations.empty()) + return -1; + + uint8_t FirstByte = Annotations.front(); + Annotations = Annotations.drop_front(); + + if ((FirstByte & 0x80) == 0x00) + return FirstByte; + + if (Annotations.empty()) + return -1; + + uint8_t SecondByte = Annotations.front(); + Annotations = Annotations.drop_front(); + + if ((FirstByte & 0xC0) == 0x80) + return ((FirstByte & 0x3F) << 8) | SecondByte; + + if (Annotations.empty()) + return -1; + + uint8_t ThirdByte = Annotations.front(); + Annotations = Annotations.drop_front(); + + if (Annotations.empty()) + return -1; + + uint8_t FourthByte = Annotations.front(); + Annotations = Annotations.drop_front(); + + if ((FirstByte & 0xE0) == 0xC0) + return ((FirstByte & 0x1F) << 24) | (SecondByte << 16) | + (ThirdByte << 8) | FourthByte; + + return -1; + }; + + static int32_t DecodeSignedOperand(uint32_t Operand) { + if (Operand & 1) + return -(Operand >> 1); + return Operand >> 1; + }; + + static int32_t DecodeSignedOperand(ArrayRef<uint8_t> &Annotations) { + return DecodeSignedOperand(GetCompressedAnnotation(Annotations)); + }; + + bool ParseCurrentAnnotation() { + if (Current.hasValue()) + return true; + + Next = Data; + uint32_t Op = GetCompressedAnnotation(Next); + AnnotationData Result; + Result.OpCode = static_cast<BinaryAnnotationsOpCode>(Op); + switch (Result.OpCode) { + case BinaryAnnotationsOpCode::Invalid: + Result.Name = "Invalid"; + Next = ArrayRef<uint8_t>(); + break; + case BinaryAnnotationsOpCode::CodeOffset: + Result.Name = "CodeOffset"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetBase: + Result.Name = "ChangeCodeOffsetBase"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeCodeOffset: + Result.Name = "ChangeCodeOffset"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeCodeLength: + Result.Name = "ChangeCodeLength"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeFile: + Result.Name = "ChangeFile"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeLineEndDelta: + Result.Name = "ChangeLineEndDelta"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeRangeKind: + Result.Name = "ChangeRangeKind"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeColumnStart: + Result.Name = "ChangeColumnStart"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeColumnEnd: + Result.Name = "ChangeColumnEnd"; + Result.U1 = GetCompressedAnnotation(Next); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + Result.Name = "ChangeLineOffset"; + Result.S1 = DecodeSignedOperand(Next); + break; + case BinaryAnnotationsOpCode::ChangeColumnEndDelta: + Result.Name = "ChangeColumnEndDelta"; + Result.S1 = DecodeSignedOperand(Next); + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: { + Result.Name = "ChangeCodeOffsetAndLineOffset"; + uint32_t Annotation = GetCompressedAnnotation(Next); + Result.S1 = DecodeSignedOperand(Annotation >> 4); + Result.U1 = Annotation & 0xf; + break; + } + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: { + Result.Name = "ChangeCodeLengthAndCodeOffset"; + Result.U1 = GetCompressedAnnotation(Next); + Result.U2 = GetCompressedAnnotation(Next); + break; + } + } + Current = Result; + return true; + } + + Optional<AnnotationData> Current; + ArrayRef<uint8_t> Data; + ArrayRef<uint8_t> Next; +}; + +// S_INLINESITE +class InlineSiteSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t PtrParent; + ulittle32_t PtrEnd; + TypeIndex Inlinee; + // BinaryAnnotations + }; + + InlineSiteSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<uint8_t> Annotations) + : SymbolRecord(SymbolRecordKind::InlineSiteSym), + RecordOffset(RecordOffset), Header(*H), Annotations(Annotations) {} + + static ErrorOr<InlineSiteSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<uint8_t> Annotations; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Annotations)); + + return InlineSiteSym(RecordOffset, H, Annotations); + } + + llvm::iterator_range<BinaryAnnotationIterator> annotations() const { + return llvm::make_range(BinaryAnnotationIterator(Annotations), + BinaryAnnotationIterator()); + } + + uint32_t RecordOffset; + Hdr Header; + +private: + ArrayRef<uint8_t> Annotations; +}; + +// S_PUB32 +class PublicSym32 : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Index; // Type index, or Metadata token if a managed symbol + ulittle32_t Off; + ulittle16_t Seg; + // Name: The null-terminated name follows. + }; + + PublicSym32(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::PublicSym32), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<PublicSym32> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return PublicSym32(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_REGISTER +class RegisterSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Index; // Type index or Metadata token + ulittle16_t Register; // RegisterId enumeration + // Name: The null-terminated name follows. + }; + + RegisterSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::RegisterSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<RegisterSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return RegisterSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_PROCREF, S_LPROCREF +class ProcRefSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t SumName; // SUC of the name (?) + ulittle32_t SymOffset; // Offset of actual symbol in $$Symbols + ulittle16_t Mod; // Module containing the actual symbol + // Name: The null-terminated name follows. + }; + + ProcRefSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::ProcRefSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<ProcRefSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return ProcRefSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_LOCAL +class LocalSym : public SymbolRecord { +public: + struct Hdr { + TypeIndex Type; + ulittle16_t Flags; // LocalSymFlags enum + // Name: The null-terminated name follows. + }; + + LocalSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::LocalSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<LocalSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return LocalSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +struct LocalVariableAddrRange { + ulittle32_t OffsetStart; + ulittle16_t ISectStart; + ulittle16_t Range; +}; + +struct LocalVariableAddrGap { + ulittle16_t GapStartOffset; + ulittle16_t Range; +}; + +enum : uint16_t { MaxDefRange = 0xf000 }; + +// S_DEFRANGE +class DefRangeSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Program; + LocalVariableAddrRange Range; + // LocalVariableAddrGap Gaps[]; + }; + + DefRangeSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeSym), RecordOffset(RecordOffset), + Header(*H), Gaps(Gaps) {} + + static ErrorOr<DefRangeSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<LocalVariableAddrGap> Gaps; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Gaps)); + + return DefRangeSym(RecordOffset, H, Gaps); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, Range); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<LocalVariableAddrGap> Gaps; +}; + +// S_DEFRANGE_SUBFIELD +class DefRangeSubfieldSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Program; + ulittle16_t OffsetInParent; + LocalVariableAddrRange Range; + // LocalVariableAddrGap Gaps[]; + }; + DefRangeSubfieldSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeSubfieldSym), + RecordOffset(RecordOffset), Header(*H), Gaps(Gaps) {} + + static ErrorOr<DefRangeSubfieldSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<LocalVariableAddrGap> Gaps; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Gaps)); + + return DefRangeSubfieldSym(RecordOffset, H, Gaps); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, Range); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<LocalVariableAddrGap> Gaps; +}; + +// S_DEFRANGE_REGISTER +class DefRangeRegisterSym : public SymbolRecord { +public: + struct Hdr { + ulittle16_t Register; + ulittle16_t MayHaveNoName; + LocalVariableAddrRange Range; + // LocalVariableAddrGap Gaps[]; + }; + + DefRangeRegisterSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeRegisterSym), + RecordOffset(RecordOffset), Header(*H), Gaps(Gaps) {} + + DefRangeRegisterSym(uint16_t Register, uint16_t MayHaveNoName, + uint32_t OffsetStart, uint16_t ISectStart, uint16_t Range, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeRegisterSym), RecordOffset(0), + Gaps(Gaps) { + Header.Register = Register; + Header.MayHaveNoName = MayHaveNoName; + Header.Range.OffsetStart = OffsetStart; + Header.Range.ISectStart = ISectStart; + Header.Range.Range = Range; + } + + static ErrorOr<DefRangeRegisterSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<LocalVariableAddrGap> Gaps; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Gaps)); + + return DefRangeRegisterSym(RecordOffset, H, Gaps); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, Range); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<LocalVariableAddrGap> Gaps; +}; + +// S_DEFRANGE_SUBFIELD_REGISTER +class DefRangeSubfieldRegisterSym : public SymbolRecord { +public: + struct Hdr { + ulittle16_t Register; // Register to which the variable is relative + ulittle16_t MayHaveNoName; + ulittle32_t OffsetInParent; + LocalVariableAddrRange Range; + // LocalVariableAddrGap Gaps[]; + }; + + DefRangeSubfieldRegisterSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeSubfieldRegisterSym), + RecordOffset(RecordOffset), Header(*H), Gaps(Gaps) {} + + DefRangeSubfieldRegisterSym(uint16_t Register, uint16_t MayHaveNoName, + uint32_t OffsetInParent, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeSubfieldRegisterSym), + RecordOffset(0), Gaps(Gaps) { + Header.Register = Register; + Header.MayHaveNoName = MayHaveNoName; + Header.OffsetInParent = OffsetInParent; + } + + static ErrorOr<DefRangeSubfieldRegisterSym> + deserialize(SymbolRecordKind Kind, uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<LocalVariableAddrGap> Gaps; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Gaps)); + + return DefRangeSubfieldRegisterSym(RecordOffset, H, Gaps); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, Range); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<LocalVariableAddrGap> Gaps; +}; + +// S_DEFRANGE_FRAMEPOINTER_REL +class DefRangeFramePointerRelSym : public SymbolRecord { +public: + struct Hdr { + little32_t Offset; // Offset from the frame pointer register + LocalVariableAddrRange Range; + // LocalVariableAddrGap Gaps[]; + }; + + DefRangeFramePointerRelSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeFramePointerRelSym), + RecordOffset(RecordOffset), Header(*H), Gaps(Gaps) {} + + static ErrorOr<DefRangeFramePointerRelSym> + deserialize(SymbolRecordKind Kind, uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<LocalVariableAddrGap> Gaps; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Gaps)); + + return DefRangeFramePointerRelSym(RecordOffset, H, Gaps); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, Range); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<LocalVariableAddrGap> Gaps; +}; + +// S_DEFRANGE_REGISTER_REL +class DefRangeRegisterRelSym : public SymbolRecord { +public: + struct Hdr { + ulittle16_t BaseRegister; + ulittle16_t Flags; + little32_t BasePointerOffset; + LocalVariableAddrRange Range; + // LocalVariableAddrGap Gaps[]; + }; + + DefRangeRegisterRelSym(uint32_t RecordOffset, const Hdr *H, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeRegisterRelSym), + RecordOffset(RecordOffset), Header(*H), Gaps(Gaps) {} + + DefRangeRegisterRelSym(uint16_t BaseRegister, uint16_t Flags, + int32_t BasePointerOffset, uint32_t OffsetStart, + uint16_t ISectStart, uint16_t Range, + ArrayRef<LocalVariableAddrGap> Gaps) + : SymbolRecord(SymbolRecordKind::DefRangeRegisterRelSym), RecordOffset(0), + Gaps(Gaps) { + Header.BaseRegister = BaseRegister; + Header.Flags = Flags; + Header.BasePointerOffset = BasePointerOffset; + Header.Range.OffsetStart = OffsetStart; + Header.Range.ISectStart = ISectStart; + Header.Range.Range = Range; + } + + static ErrorOr<DefRangeRegisterRelSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + ArrayRef<LocalVariableAddrGap> Gaps; + CV_DESERIALIZE(Data, H, CV_ARRAY_FIELD_TAIL(Gaps)); + + return DefRangeRegisterRelSym(RecordOffset, H, Gaps); + } + + bool hasSpilledUDTMember() const { return Header.Flags & 1; } + uint16_t offsetInParent() const { return Header.Flags >> 4; } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, Range); + } + + uint32_t RecordOffset; + Hdr Header; + ArrayRef<LocalVariableAddrGap> Gaps; +}; + +// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE +class DefRangeFramePointerRelFullScopeSym : public SymbolRecord { +public: + struct Hdr { + little32_t Offset; // Offset from the frame pointer register + }; + + DefRangeFramePointerRelFullScopeSym(uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(SymbolRecordKind::DefRangeFramePointerRelFullScopeSym), + RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<DefRangeFramePointerRelFullScopeSym> + deserialize(SymbolRecordKind Kind, uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + CV_DESERIALIZE(Data, H); + + return DefRangeFramePointerRelFullScopeSym(RecordOffset, H); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_BLOCK32 +class BlockSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t PtrParent; + ulittle32_t PtrEnd; + ulittle32_t CodeSize; + ulittle32_t CodeOffset; + ulittle16_t Segment; + // Name: The null-terminated name follows. + }; + + BlockSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::BlockSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<BlockSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return BlockSym(RecordOffset, H, Name); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, CodeOffset); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_LABEL32 +class LabelSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t CodeOffset; + ulittle16_t Segment; + uint8_t Flags; // CV_PROCFLAGS + // Name: The null-terminated name follows. + }; + + LabelSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::LabelSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<LabelSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return LabelSym(RecordOffset, H, Name); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, CodeOffset); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_OBJNAME +class ObjNameSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Signature; + // Name: The null-terminated name follows. + }; + + ObjNameSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::ObjNameSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<ObjNameSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return ObjNameSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_ENVBLOCK +class EnvBlockSym : public SymbolRecord { +public: + struct Hdr { + uint8_t Reserved; + // Sequence of zero terminated strings. + }; + + EnvBlockSym(uint32_t RecordOffset, const Hdr *H, + const std::vector<StringRef> &Fields) + : SymbolRecord(SymbolRecordKind::EnvBlockSym), RecordOffset(RecordOffset), + Header(*H), Fields(Fields) {} + + static ErrorOr<EnvBlockSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + std::vector<StringRef> Fields; + CV_DESERIALIZE(Data, H, CV_STRING_ARRAY_NULL_TERM(Fields)); + + return EnvBlockSym(RecordOffset, H, Fields); + } + + uint32_t RecordOffset; + Hdr Header; + std::vector<StringRef> Fields; +}; + +// S_EXPORT +class ExportSym : public SymbolRecord { +public: + struct Hdr { + ulittle16_t Ordinal; + ulittle16_t Flags; // ExportFlags + // Name: The null-terminated name follows. + }; + + ExportSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::ExportSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<ExportSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return ExportSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_FILESTATIC +class FileStaticSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Index; // Type Index + ulittle32_t ModFilenameOffset; // Index of mod filename in string table + ulittle16_t Flags; // LocalSymFlags enum + // Name: The null-terminated name follows. + }; + + FileStaticSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::FileStaticSym), + RecordOffset(RecordOffset), Header(*H), Name(Name) {} + + static ErrorOr<FileStaticSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return FileStaticSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_COMPILE2 +class Compile2Sym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t flags; // CompileSym2Flags enum + uint8_t getLanguage() const { return flags & 0xFF; } + unsigned short Machine; // CPUType enum + unsigned short VersionFrontendMajor; + unsigned short VersionFrontendMinor; + unsigned short VersionFrontendBuild; + unsigned short VersionBackendMajor; + unsigned short VersionBackendMinor; + unsigned short VersionBackendBuild; + // Version: The null-terminated version string follows. + // Optional block of zero terminated strings terminated with a double zero. + }; + + Compile2Sym(uint32_t RecordOffset, const Hdr *H, StringRef Version) + : SymbolRecord(SymbolRecordKind::Compile2Sym), RecordOffset(RecordOffset), + Header(*H), Version(Version) {} + + static ErrorOr<Compile2Sym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Version; + CV_DESERIALIZE(Data, H, Version); + + return Compile2Sym(RecordOffset, H, Version); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Version; +}; + +// S_COMPILE3 +class Compile3Sym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t flags; // CompileSym3Flags enum + uint8_t getLanguage() const { return flags & 0xff; } + ulittle16_t Machine; // CPUType enum + ulittle16_t VersionFrontendMajor; + ulittle16_t VersionFrontendMinor; + ulittle16_t VersionFrontendBuild; + ulittle16_t VersionFrontendQFE; + ulittle16_t VersionBackendMajor; + ulittle16_t VersionBackendMinor; + ulittle16_t VersionBackendBuild; + ulittle16_t VersionBackendQFE; + // VersionString: The null-terminated version string follows. + }; + + Compile3Sym(uint32_t RecordOffset, const Hdr *H, StringRef Version) + : SymbolRecord(SymbolRecordKind::Compile3Sym), RecordOffset(RecordOffset), + Header(*H), Version(Version) {} + + static ErrorOr<Compile3Sym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Version; + CV_DESERIALIZE(Data, H, Version); + + return Compile3Sym(RecordOffset, H, Version); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Version; +}; + +// S_FRAMEPROC +class FrameProcSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t TotalFrameBytes; + ulittle32_t PaddingFrameBytes; + ulittle32_t OffsetToPadding; + ulittle32_t BytesOfCalleeSavedRegisters; + ulittle32_t OffsetOfExceptionHandler; + ulittle16_t SectionIdOfExceptionHandler; + ulittle32_t Flags; + }; + + FrameProcSym(uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(SymbolRecordKind::FrameProcSym), + RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<FrameProcSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + CV_DESERIALIZE(Data, H); + + return FrameProcSym(RecordOffset, H); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_CALLSITEINFO +class CallSiteInfoSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t CodeOffset; + ulittle16_t Segment; + ulittle16_t Reserved; + TypeIndex Type; + }; + + CallSiteInfoSym(uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(SymbolRecordKind::CallSiteInfoSym), + RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<CallSiteInfoSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + CV_DESERIALIZE(Data, H); + + return CallSiteInfoSym(RecordOffset, H); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, CodeOffset); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_HEAPALLOCSITE +class HeapAllocationSiteSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t CodeOffset; + ulittle16_t Segment; + ulittle16_t CallInstructionSize; + TypeIndex Type; + }; + + HeapAllocationSiteSym(uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(SymbolRecordKind::HeapAllocationSiteSym), + RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<HeapAllocationSiteSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + CV_DESERIALIZE(Data, H); + + return HeapAllocationSiteSym(RecordOffset, H); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, CodeOffset); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_FRAMECOOKIE +class FrameCookieSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t CodeOffset; + ulittle16_t Register; + uint8_t CookieKind; + uint8_t Flags; + }; + + FrameCookieSym(uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(SymbolRecordKind::FrameCookieSym), + RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<FrameCookieSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + CV_DESERIALIZE(Data, H); + + return FrameCookieSym(RecordOffset, H); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, CodeOffset); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_UDT, S_COBOLUDT +class UDTSym : public SymbolRecord { +public: + struct Hdr { + TypeIndex Type; // Type of the UDT + // Name: The null-terminated name follows. + }; + + UDTSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::UDTSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<UDTSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return UDTSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_BUILDINFO +class BuildInfoSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t BuildId; + }; + + BuildInfoSym(uint32_t RecordOffset, const Hdr *H) + : SymbolRecord(SymbolRecordKind::BuildInfoSym), + RecordOffset(RecordOffset), Header(*H) {} + + static ErrorOr<BuildInfoSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + CV_DESERIALIZE(Data, H); + + return BuildInfoSym(RecordOffset, H); + } + + uint32_t RecordOffset; + Hdr Header; +}; + +// S_BPREL32 +class BPRelativeSym : public SymbolRecord { +public: + struct Hdr { + little32_t Offset; // Offset from the base pointer register + TypeIndex Type; // Type of the variable + // Name: The null-terminated name follows. + }; + + BPRelativeSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::BPRelativeSym), + RecordOffset(RecordOffset), Header(*H), Name(Name) {} + + static ErrorOr<BPRelativeSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return BPRelativeSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_REGREL32 +class RegRelativeSym : public SymbolRecord { +public: + struct Hdr { + ulittle32_t Offset; // Offset from the register + TypeIndex Type; // Type of the variable + ulittle16_t Register; // Register to which the variable is relative + // Name: The null-terminated name follows. + }; + + RegRelativeSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::RegRelativeSym), + RecordOffset(RecordOffset), Header(*H), Name(Name) {} + + static ErrorOr<RegRelativeSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return RegRelativeSym(RecordOffset, H, Name); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_CONSTANT, S_MANCONSTANT +class ConstantSym : public SymbolRecord { +public: + struct Hdr { + TypeIndex Type; + // Value: The value of the constant. + // Name: The null-terminated name follows. + }; + + ConstantSym(uint32_t RecordOffset, const Hdr *H, const APSInt &Value, + StringRef Name) + : SymbolRecord(SymbolRecordKind::ConstantSym), RecordOffset(RecordOffset), + Header(*H), Value(Value), Name(Name) {} + + static ErrorOr<ConstantSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + APSInt Value; + StringRef Name; + CV_DESERIALIZE(Data, H, Value, Name); + + return ConstantSym(RecordOffset, H, Value, Name); + } + + uint32_t RecordOffset; + Hdr Header; + APSInt Value; + StringRef Name; +}; + +// S_LDATA32, S_GDATA32, S_LMANDATA, S_GMANDATA +class DataSym : public SymbolRecord { +public: + struct Hdr { + TypeIndex Type; + ulittle32_t DataOffset; + ulittle16_t Segment; + // Name: The null-terminated name follows. + }; + + DataSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::DataSym), RecordOffset(RecordOffset), + Header(*H), Name(Name) {} + + static ErrorOr<DataSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return DataSym(RecordOffset, H, Name); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, DataOffset); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +// S_LTHREAD32, S_GTHREAD32 +class ThreadLocalDataSym : public SymbolRecord { +public: + struct Hdr { + TypeIndex Type; + ulittle32_t DataOffset; + ulittle16_t Segment; + // Name: The null-terminated name follows. + }; + + ThreadLocalDataSym(uint32_t RecordOffset, const Hdr *H, StringRef Name) + : SymbolRecord(SymbolRecordKind::ThreadLocalDataSym), + RecordOffset(RecordOffset), Header(*H), Name(Name) {} + + static ErrorOr<ThreadLocalDataSym> deserialize(SymbolRecordKind Kind, + uint32_t RecordOffset, + ArrayRef<uint8_t> &Data) { + const Hdr *H = nullptr; + StringRef Name; + CV_DESERIALIZE(Data, H, Name); + + return ThreadLocalDataSym(RecordOffset, H, Name); + } + + uint32_t getRelocationOffset() const { + return RecordOffset + offsetof(Hdr, DataOffset); + } + + uint32_t RecordOffset; + Hdr Header; + StringRef Name; +}; + +typedef CVRecord<SymbolKind> CVSymbol; +typedef VarStreamArray<CVSymbol> CVSymbolArray; + +} // namespace codeview +} // namespace llvm + +#endif diff --git a/include/llvm/DebugInfo/CodeView/SymbolVisitorDelegate.h b/include/llvm/DebugInfo/CodeView/SymbolVisitorDelegate.h new file mode 100644 index 0000000000000..a4965168c3db7 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/SymbolVisitorDelegate.h @@ -0,0 +1,33 @@ +//===-- SymbolVisitorDelegate.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_SYMBOLVISITORDELEGATE_H +#define LLVM_DEBUGINFO_CODEVIEW_SYMBOLVISITORDELEGATE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +#include <stdint.h> + +namespace llvm { + +namespace codeview { + +class SymbolVisitorDelegate { +public: + virtual ~SymbolVisitorDelegate() {} + + virtual uint32_t getRecordOffset(ArrayRef<uint8_t> Record) = 0; + virtual StringRef getFileNameForFileOffset(uint32_t FileOffset) = 0; + virtual StringRef getStringTable() = 0; +}; +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_SYMBOLVISITORDELEGATE_H diff --git a/include/llvm/DebugInfo/CodeView/TypeDumper.h b/include/llvm/DebugInfo/CodeView/TypeDumper.h new file mode 100644 index 0000000000000..ca79ab076e5eb --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/TypeDumper.h @@ -0,0 +1,105 @@ +//===-- TypeDumper.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPEDUMPER_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPEDUMPER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +namespace llvm { +class ScopedPrinter; + +namespace codeview { + +/// Dumper for CodeView type streams found in COFF object files and PDB files. +class CVTypeDumper : public TypeVisitorCallbacks { +public: + CVTypeDumper(ScopedPrinter *W, bool PrintRecordBytes) + : W(W), PrintRecordBytes(PrintRecordBytes) {} + + StringRef getTypeName(TypeIndex TI); + void printTypeIndex(StringRef FieldName, TypeIndex TI); + + /// Dumps one type record. Returns false if there was a type parsing error, + /// and true otherwise. This should be called in order, since the dumper + /// maintains state about previous records which are necessary for cross + /// type references. + Error dump(const CVRecord<TypeLeafKind> &Record); + + /// Dumps the type records in Types. Returns false if there was a type stream + /// parse error, and true otherwise. + Error dump(const CVTypeArray &Types); + + /// Dumps the type records in Data. Returns false if there was a type stream + /// parse error, and true otherwise. Use this method instead of the + /// CVTypeArray overload when type records are laid out contiguously in + /// memory. + Error dump(ArrayRef<uint8_t> Data); + + /// Gets the type index for the next type record. + unsigned getNextTypeIndex() const { + return 0x1000 + CVUDTNames.size(); + } + + /// Records the name of a type, and reserves its type index. + void recordType(StringRef Name) { CVUDTNames.push_back(Name); } + + /// Saves the name in a StringSet and creates a stable StringRef. + StringRef saveName(StringRef TypeName) { + return TypeNames.insert(TypeName).first->getKey(); + } + + void setPrinter(ScopedPrinter *P); + ScopedPrinter *getPrinter() { return W; } + + /// Action to take on unknown types. By default, they are ignored. + Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) override; + Error visitUnknownMember(const CVRecord<TypeLeafKind> &Record) override; + + /// Paired begin/end actions for all types. Receives all record data, + /// including the fixed-length record prefix. + Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override; + Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override; + +#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 "TypeRecords.def" + +private: + void printMemberAttributes(MemberAttributes Attrs); + void printMemberAttributes(MemberAccess Access, MethodKind Kind, + MethodOptions Options); + + ScopedPrinter *W; + + bool PrintRecordBytes = false; + + /// Name of the current type. Only valid before visitTypeEnd. + StringRef Name; + + /// All user defined type records in .debug$T live in here. Type indices + /// greater than 0x1000 are user defined. Subtract 0x1000 from the index to + /// index into this vector. + SmallVector<StringRef, 10> CVUDTNames; + + StringSet<> TypeNames; +}; + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_TYPEDUMPER_H diff --git a/include/llvm/DebugInfo/CodeView/TypeIndex.h b/include/llvm/DebugInfo/CodeView/TypeIndex.h index d3a541be4c626..c2ebf3848892d 100644 --- a/include/llvm/DebugInfo/CodeView/TypeIndex.h +++ b/include/llvm/DebugInfo/CodeView/TypeIndex.h @@ -10,6 +10,7 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPEINDEX_H #define LLVM_DEBUGINFO_CODEVIEW_TYPEINDEX_H +#include "llvm/Support/Endian.h" #include <cassert> #include <cinttypes> @@ -26,6 +27,8 @@ enum class SimpleTypeKind : uint32_t { UnsignedCharacter = 0x0020, // 8 bit unsigned NarrowCharacter = 0x0070, // really a char WideCharacter = 0x0071, // wide char + Character16 = 0x007a, // char16_t + Character32 = 0x007b, // char32_t SByte = 0x0068, // 8 bit signed int Byte = 0x0069, // 8 bit unsigned int @@ -41,6 +44,8 @@ enum class SimpleTypeKind : uint32_t { UInt64Quad = 0x0023, // 64 bit unsigned Int64 = 0x0076, // 64 bit signed int UInt64 = 0x0077, // 64 bit unsigned int + Int128Oct = 0x0014, // 128 bit signed int + UInt128Oct = 0x0024, // 128 bit unsigned int Int128 = 0x0078, // 128 bit signed int UInt128 = 0x0079, // 128 bit unsigned int @@ -52,15 +57,19 @@ enum class SimpleTypeKind : uint32_t { Float80 = 0x0042, // 80 bit real Float128 = 0x0043, // 128 bit real - Complex32 = 0x0050, // 32 bit complex - Complex64 = 0x0051, // 64 bit complex - Complex80 = 0x0052, // 80 bit complex - Complex128 = 0x0053, // 128 bit complex - - Boolean8 = 0x0030, // 8 bit boolean - Boolean16 = 0x0031, // 16 bit boolean - Boolean32 = 0x0032, // 32 bit boolean - Boolean64 = 0x0033 // 64 bit boolean + Complex16 = 0x0056, // 16 bit complex + Complex32 = 0x0050, // 32 bit complex + Complex32PartialPrecision = 0x0055, // 32 bit PP complex + Complex48 = 0x0054, // 48 bit complex + Complex64 = 0x0051, // 64 bit complex + Complex80 = 0x0052, // 80 bit complex + Complex128 = 0x0053, // 128 bit complex + + Boolean8 = 0x0030, // 8 bit boolean + Boolean16 = 0x0031, // 16 bit boolean + Boolean32 = 0x0032, // 32 bit boolean + Boolean64 = 0x0033, // 64 bit boolean + Boolean128 = 0x0034, // 128 bit boolean }; enum class SimpleTypeMode : uint32_t { @@ -74,6 +83,9 @@ enum class SimpleTypeMode : uint32_t { NearPointer128 = 0x00000700 // 128 bit near pointer }; +/// A 32-bit type reference. Types are indexed by their order of appearance in +/// .debug$T plus 0x1000. Type indices less than 0x1000 are "simple" types, +/// composed of a SimpleTypeMode byte followed by a SimpleTypeKind byte. class TypeIndex { public: static const uint32_t FirstNonSimpleIndex = 0x1000; @@ -91,6 +103,8 @@ public: uint32_t getIndex() const { return Index; } bool isSimple() const { return Index < FirstNonSimpleIndex; } + bool isNoneType() const { return *this == None(); } + SimpleTypeKind getSimpleKind() const { assert(isSimple()); return static_cast<SimpleTypeKind>(Index & SimpleKindMask); @@ -101,6 +115,7 @@ public: return static_cast<SimpleTypeMode>(Index & SimpleModeMask); } + static TypeIndex None() { return TypeIndex(SimpleTypeKind::None); } static TypeIndex Void() { return TypeIndex(SimpleTypeKind::Void); } static TypeIndex VoidPointer32() { return TypeIndex(SimpleTypeKind::Void, SimpleTypeMode::NearPointer32); @@ -143,33 +158,34 @@ public: static TypeIndex Float32() { return TypeIndex(SimpleTypeKind::Float32); } static TypeIndex Float64() { return TypeIndex(SimpleTypeKind::Float64); } -private: - uint32_t Index; -}; + friend inline bool operator==(const TypeIndex &A, const TypeIndex &B) { + return A.getIndex() == B.getIndex(); + } -inline bool operator==(const TypeIndex &A, const TypeIndex &B) { - return A.getIndex() == B.getIndex(); -} + friend inline bool operator!=(const TypeIndex &A, const TypeIndex &B) { + return A.getIndex() != B.getIndex(); + } -inline bool operator!=(const TypeIndex &A, const TypeIndex &B) { - return A.getIndex() != B.getIndex(); -} + friend inline bool operator<(const TypeIndex &A, const TypeIndex &B) { + return A.getIndex() < B.getIndex(); + } -inline bool operator<(const TypeIndex &A, const TypeIndex &B) { - return A.getIndex() < B.getIndex(); -} + friend inline bool operator<=(const TypeIndex &A, const TypeIndex &B) { + return A.getIndex() <= B.getIndex(); + } -inline bool operator<=(const TypeIndex &A, const TypeIndex &B) { - return A.getIndex() <= B.getIndex(); -} + friend inline bool operator>(const TypeIndex &A, const TypeIndex &B) { + return A.getIndex() > B.getIndex(); + } -inline bool operator>(const TypeIndex &A, const TypeIndex &B) { - return A.getIndex() > B.getIndex(); -} + friend inline bool operator>=(const TypeIndex &A, const TypeIndex &B) { + return A.getIndex() >= B.getIndex(); + } + +private: + support::ulittle32_t Index; +}; -inline bool operator>=(const TypeIndex &A, const TypeIndex &B) { - return A.getIndex() >= B.getIndex(); -} } } diff --git a/include/llvm/DebugInfo/CodeView/TypeRecord.h b/include/llvm/DebugInfo/CodeView/TypeRecord.h index 21755f5d9b09b..42751fbd4af11 100644 --- a/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ b/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -10,15 +10,96 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPERECORD_H #define LLVM_DEBUGINFO_CODEVIEW_TYPERECORD_H +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/Support/ErrorOr.h" #include <cinttypes> +#include <utility> namespace llvm { namespace codeview { +using llvm::support::little32_t; +using llvm::support::ulittle16_t; +using llvm::support::ulittle32_t; + +/// Equvalent to CV_fldattr_t in cvinfo.h. +struct MemberAttributes { + ulittle16_t Attrs; + enum { + MethodKindShift = 2, + }; + + /// Get the access specifier. Valid for any kind of member. + MemberAccess getAccess() const { + return MemberAccess(unsigned(Attrs) & unsigned(MethodOptions::AccessMask)); + } + + /// Indicates if a method is defined with friend, virtual, static, etc. + MethodKind getMethodKind() const { + return MethodKind( + (unsigned(Attrs) & unsigned(MethodOptions::MethodKindMask)) >> + MethodKindShift); + } + + /// Get the flags that are not included in access control or method + /// properties. + MethodOptions getFlags() const { + return MethodOptions( + unsigned(Attrs) & + ~unsigned(MethodOptions::AccessMask | MethodOptions::MethodKindMask)); + } + + /// Is this method virtual. + bool isVirtual() const { + auto MP = getMethodKind(); + return MP != MethodKind::Vanilla && MP != MethodKind::Friend && + MP != MethodKind::Static; + } + + /// Does this member introduce a new virtual method. + bool isIntroducedVirtual() const { + auto MP = getMethodKind(); + return MP == MethodKind::IntroducingVirtual || + MP == MethodKind::PureIntroducingVirtual; + } +}; + +// Does not correspond to any tag, this is the tail of an LF_POINTER record +// if it represents a member pointer. +class MemberPointerInfo { +public: + MemberPointerInfo() {} + + MemberPointerInfo(TypeIndex ContainingType, + PointerToMemberRepresentation Representation) + : ContainingType(ContainingType), Representation(Representation) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<MemberPointerInfo> deserialize(ArrayRef<uint8_t> &Data); + + TypeIndex getContainingType() const { return ContainingType; } + PointerToMemberRepresentation getRepresentation() const { + return Representation; + } + +private: + struct Layout { + TypeIndex ClassType; + ulittle16_t Representation; // PointerToMemberRepresentation + }; + + TypeIndex ContainingType; + PointerToMemberRepresentation Representation; +}; + class TypeRecord { protected: explicit TypeRecord(TypeRecordKind Kind) : Kind(Kind) {} @@ -30,20 +111,34 @@ private: TypeRecordKind Kind; }; +// LF_MODIFIER class ModifierRecord : public TypeRecord { public: - ModifierRecord(TypeIndex ModifiedType, ModifierOptions Options) + ModifierRecord(TypeIndex ModifiedType, ModifierOptions Modifiers) : TypeRecord(TypeRecordKind::Modifier), ModifiedType(ModifiedType), - Options(Options) {} + Modifiers(Modifiers) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<ModifierRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); TypeIndex getModifiedType() const { return ModifiedType; } - ModifierOptions getOptions() const { return Options; } + ModifierOptions getModifiers() const { return Modifiers; } private: + struct Layout { + TypeIndex ModifiedType; + ulittle16_t Modifiers; // ModifierOptions + }; + TypeIndex ModifiedType; - ModifierOptions Options; + ModifierOptions Modifiers; }; +// LF_PROCEDURE class ProcedureRecord : public TypeRecord { public: ProcedureRecord(TypeIndex ReturnType, CallingConvention CallConv, @@ -53,6 +148,15 @@ public: CallConv(CallConv), Options(Options), ParameterCount(ParameterCount), ArgumentList(ArgumentList) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<ProcedureRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + static uint32_t getLayoutSize() { return 2 + sizeof(Layout); } + TypeIndex getReturnType() const { return ReturnType; } CallingConvention getCallConv() const { return CallConv; } FunctionOptions getOptions() const { return Options; } @@ -60,6 +164,14 @@ public: TypeIndex getArgumentList() const { return ArgumentList; } private: + struct Layout { + TypeIndex ReturnType; + CallingConvention CallConv; + FunctionOptions Options; + ulittle16_t NumParameters; + TypeIndex ArgListType; + }; + TypeIndex ReturnType; CallingConvention CallConv; FunctionOptions Options; @@ -67,6 +179,7 @@ private: TypeIndex ArgumentList; }; +// LF_MFUNCTION class MemberFunctionRecord : public TypeRecord { public: MemberFunctionRecord(TypeIndex ReturnType, TypeIndex ClassType, @@ -79,6 +192,13 @@ public: ArgumentList(ArgumentList), ThisPointerAdjustment(ThisPointerAdjustment) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<MemberFunctionRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + TypeIndex getReturnType() const { return ReturnType; } TypeIndex getClassType() const { return ClassType; } TypeIndex getThisType() const { return ThisType; } @@ -89,6 +209,17 @@ public: int32_t getThisPointerAdjustment() const { return ThisPointerAdjustment; } private: + struct Layout { + TypeIndex ReturnType; + TypeIndex ClassType; + TypeIndex ThisType; + CallingConvention CallConv; + FunctionOptions Options; + ulittle16_t NumParameters; + TypeIndex ArgListType; + little32_t ThisAdjustment; + }; + TypeIndex ReturnType; TypeIndex ClassType; TypeIndex ThisType; @@ -99,78 +230,210 @@ private: int32_t ThisPointerAdjustment; }; -class ArgumentListRecord : public TypeRecord { +// LF_MFUNC_ID +class MemberFuncIdRecord : public TypeRecord { public: - explicit ArgumentListRecord(llvm::ArrayRef<TypeIndex> ArgumentTypes) - : TypeRecord(TypeRecordKind::ArgumentList), ArgumentTypes(ArgumentTypes) { - } + MemberFuncIdRecord(TypeIndex ClassType, TypeIndex FunctionType, + StringRef Name) + : TypeRecord(TypeRecordKind::MemberFuncId), ClassType(ClassType), + FunctionType(FunctionType), Name(Name) {} - llvm::ArrayRef<TypeIndex> getArgumentTypes() const { return ArgumentTypes; } + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<MemberFuncIdRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + TypeIndex getClassType() const { return ClassType; } + TypeIndex getFunctionType() const { return FunctionType; } + StringRef getName() const { return Name; } private: - llvm::ArrayRef<TypeIndex> ArgumentTypes; + struct Layout { + TypeIndex ClassType; + TypeIndex FunctionType; + // Name: The null-terminated name follows. + }; + TypeIndex ClassType; + TypeIndex FunctionType; + StringRef Name; }; -class PointerRecordBase : public TypeRecord { +// LF_ARGLIST, LF_SUBSTR_LIST +class ArgListRecord : public TypeRecord { public: - PointerRecordBase(TypeIndex ReferentType, PointerKind Kind, PointerMode Mode, - PointerOptions Options, uint8_t Size) + ArgListRecord(TypeRecordKind Kind, ArrayRef<TypeIndex> Indices) + : TypeRecord(Kind), StringIndices(Indices) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<ArgListRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + ArrayRef<TypeIndex> getIndices() const { return StringIndices; } + + static uint32_t getLayoutSize() { return 2 + sizeof(Layout); } + +private: + struct Layout { + ulittle32_t NumArgs; // Number of arguments + // ArgTypes[]: Type indicies of arguments + }; + + std::vector<TypeIndex> StringIndices; +}; + +// LF_POINTER +class PointerRecord : public TypeRecord { +public: + static const uint32_t PointerKindShift = 0; + static const uint32_t PointerKindMask = 0x1F; + + static const uint32_t PointerModeShift = 5; + static const uint32_t PointerModeMask = 0x07; + + static const uint32_t PointerSizeShift = 13; + static const uint32_t PointerSizeMask = 0xFF; + + PointerRecord(TypeIndex ReferentType, PointerKind Kind, PointerMode Mode, + PointerOptions Options, uint8_t Size) + : PointerRecord(ReferentType, Kind, Mode, Options, Size, + MemberPointerInfo()) {} + + PointerRecord(TypeIndex ReferentType, PointerKind Kind, PointerMode Mode, + PointerOptions Options, uint8_t Size, + const MemberPointerInfo &Member) : TypeRecord(TypeRecordKind::Pointer), ReferentType(ReferentType), - PtrKind(Kind), Mode(Mode), Options(Options), Size(Size) {} + PtrKind(Kind), Mode(Mode), Options(Options), Size(Size), + MemberInfo(Member) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<PointerRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); TypeIndex getReferentType() const { return ReferentType; } PointerKind getPointerKind() const { return PtrKind; } PointerMode getMode() const { return Mode; } PointerOptions getOptions() const { return Options; } uint8_t getSize() const { return Size; } + MemberPointerInfo getMemberInfo() const { return MemberInfo; } + + bool isPointerToMember() const { + return Mode == PointerMode::PointerToDataMember || + Mode == PointerMode::PointerToMemberFunction; + } + bool isFlat() const { + return !!(uint32_t(Options) & uint32_t(PointerOptions::Flat32)); + } + bool isConst() const { + return !!(uint32_t(Options) & uint32_t(PointerOptions::Const)); + } + bool isVolatile() const { + return !!(uint32_t(Options) & uint32_t(PointerOptions::Volatile)); + } + bool isUnaligned() const { + return !!(uint32_t(Options) & uint32_t(PointerOptions::Unaligned)); + } private: + struct Layout { + TypeIndex PointeeType; + ulittle32_t Attrs; // pointer attributes + // if pointer to member: + // PointerToMemberTail + PointerKind getPtrKind() const { + return PointerKind(Attrs & PointerKindMask); + } + PointerMode getPtrMode() const { + return PointerMode((Attrs >> PointerModeShift) & PointerModeMask); + } + uint8_t getPtrSize() const { + return (Attrs >> PointerSizeShift) & PointerSizeMask; + } + bool isFlat() const { return Attrs & (1 << 8); } + bool isVolatile() const { return Attrs & (1 << 9); } + bool isConst() const { return Attrs & (1 << 10); } + bool isUnaligned() const { return Attrs & (1 << 11); } + + bool isPointerToDataMember() const { + return getPtrMode() == PointerMode::PointerToDataMember; + } + bool isPointerToMemberFunction() const { + return getPtrMode() == PointerMode::PointerToMemberFunction; + } + bool isPointerToMember() const { + return isPointerToMemberFunction() || isPointerToDataMember(); + } + }; + TypeIndex ReferentType; PointerKind PtrKind; PointerMode Mode; PointerOptions Options; uint8_t Size; + MemberPointerInfo MemberInfo; }; -class PointerRecord : public PointerRecordBase { +// LF_NESTTYPE +class NestedTypeRecord : public TypeRecord { public: - PointerRecord(TypeIndex ReferentType, PointerKind Kind, PointerMode Mode, - PointerOptions Options, uint8_t Size) - : PointerRecordBase(ReferentType, Kind, Mode, Options, Size) {} -}; + NestedTypeRecord(TypeIndex Type, StringRef Name) + : TypeRecord(TypeRecordKind::NestedType), Type(Type), Name(Name) {} -class PointerToMemberRecord : public PointerRecordBase { -public: - PointerToMemberRecord(TypeIndex ReferentType, PointerKind Kind, - PointerMode Mode, PointerOptions Options, uint8_t Size, - TypeIndex ContainingType, - PointerToMemberRepresentation Representation) - : PointerRecordBase(ReferentType, Kind, Mode, Options, Size), - ContainingType(ContainingType), Representation(Representation) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); - TypeIndex getContainingType() const { return ContainingType; } - PointerToMemberRepresentation getRepresentation() const { - return Representation; - } + static ErrorOr<NestedTypeRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getNestedType() const { return Type; } + StringRef getName() const { return Name; } private: - TypeIndex ContainingType; - PointerToMemberRepresentation Representation; + struct Layout { + ulittle16_t Pad0; // Should be zero + TypeIndex Type; // Type index of nested type + // Name: Null-terminated string + }; + + TypeIndex Type; + StringRef Name; }; +// LF_ARRAY class ArrayRecord : public TypeRecord { public: ArrayRecord(TypeIndex ElementType, TypeIndex IndexType, uint64_t Size, - llvm::StringRef Name) + StringRef Name) : TypeRecord(TypeRecordKind::Array), ElementType(ElementType), IndexType(IndexType), Size(Size), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<ArrayRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + TypeIndex getElementType() const { return ElementType; } TypeIndex getIndexType() const { return IndexType; } uint64_t getSize() const { return Size; } llvm::StringRef getName() const { return Name; } private: + struct Layout { + TypeIndex ElementType; + TypeIndex IndexType; + // SizeOf: LF_NUMERIC encoded size in bytes. Not element count! + // Name: The null-terminated name follows. + }; + TypeIndex ElementType; TypeIndex IndexType; uint64_t Size; @@ -185,6 +448,15 @@ protected: FieldList(FieldList), Name(Name), UniqueName(UniqueName) {} public: + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static const int HfaKindShift = 11; + static const int HfaKindMask = 0x1800; + static const int WinRTKindShift = 14; + static const int WinRTKindMask = 0xC000; + uint16_t getMemberCount() const { return MemberCount; } ClassOptions getOptions() const { return Options; } TypeIndex getFieldList() const { return FieldList; } @@ -199,17 +471,24 @@ private: StringRef UniqueName; }; -class AggregateRecord : public TagRecord { +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE +class ClassRecord : public TagRecord { public: - AggregateRecord(TypeRecordKind Kind, uint16_t MemberCount, - ClassOptions Options, HfaKind Hfa, - WindowsRTClassKind WinRTKind, TypeIndex FieldList, - TypeIndex DerivationList, TypeIndex VTableShape, - uint64_t Size, StringRef Name, StringRef UniqueName) + ClassRecord(TypeRecordKind Kind, uint16_t MemberCount, ClassOptions Options, + HfaKind Hfa, WindowsRTClassKind WinRTKind, TypeIndex FieldList, + TypeIndex DerivationList, TypeIndex VTableShape, uint64_t Size, + StringRef Name, StringRef UniqueName) : TagRecord(Kind, MemberCount, Options, FieldList, Name, UniqueName), Hfa(Hfa), WinRTKind(WinRTKind), DerivationList(DerivationList), VTableShape(VTableShape), Size(Size) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<ClassRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + HfaKind getHfa() const { return Hfa; } WindowsRTClassKind getWinRTKind() const { return WinRTKind; } TypeIndex getDerivationList() const { return DerivationList; } @@ -217,6 +496,21 @@ public: uint64_t getSize() const { return Size; } private: + struct Layout { + ulittle16_t MemberCount; // Number of members in FieldList. + ulittle16_t Properties; // ClassOptions bitset + TypeIndex FieldList; // LF_FIELDLIST: List of all kinds of members + TypeIndex DerivedFrom; // LF_DERIVED: List of known derived classes + TypeIndex VShape; // LF_VTSHAPE: Shape of the vftable + // SizeOf: The 'sizeof' the UDT in bytes is encoded as an LF_NUMERIC + // integer. + // Name: The null-terminated name follows. + + bool hasUniqueName() const { + return Properties & uint16_t(ClassOptions::HasUniqueName); + } + }; + HfaKind Hfa; WindowsRTClassKind WinRTKind; TypeIndex DerivationList; @@ -224,6 +518,40 @@ private: uint64_t Size; }; +// LF_UNION +struct UnionRecord : public TagRecord { + UnionRecord(uint16_t MemberCount, ClassOptions Options, HfaKind Hfa, + TypeIndex FieldList, uint64_t Size, StringRef Name, + StringRef UniqueName) + : TagRecord(TypeRecordKind::Union, MemberCount, Options, FieldList, Name, + UniqueName), + Hfa(Hfa), Size(Size) {} + + static ErrorOr<UnionRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + HfaKind getHfa() const { return Hfa; } + uint64_t getSize() const { return Size; } + +private: + struct Layout { + ulittle16_t MemberCount; // Number of members in FieldList. + ulittle16_t Properties; // ClassOptions bitset + TypeIndex FieldList; // LF_FIELDLIST: List of all kinds of members + // SizeOf: The 'sizeof' the UDT in bytes is encoded as an LF_NUMERIC + // integer. + // Name: The null-terminated name follows. + + bool hasUniqueName() const { + return Properties & uint16_t(ClassOptions::HasUniqueName); + } + }; + + HfaKind Hfa; + uint64_t Size; +}; + +// LF_ENUM class EnumRecord : public TagRecord { public: EnumRecord(uint16_t MemberCount, ClassOptions Options, TypeIndex FieldList, @@ -232,38 +560,642 @@ public: UniqueName), UnderlyingType(UnderlyingType) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<EnumRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + TypeIndex getUnderlyingType() const { return UnderlyingType; } private: + struct Layout { + ulittle16_t NumEnumerators; // Number of enumerators + ulittle16_t Properties; + TypeIndex UnderlyingType; + TypeIndex FieldListType; + // Name: The null-terminated name follows. + + bool hasUniqueName() const { + return Properties & uint16_t(ClassOptions::HasUniqueName); + } + }; + TypeIndex UnderlyingType; }; -class BitFieldRecord : TypeRecord { +// LF_BITFIELD +class BitFieldRecord : public TypeRecord { public: BitFieldRecord(TypeIndex Type, uint8_t BitSize, uint8_t BitOffset) : TypeRecord(TypeRecordKind::BitField), Type(Type), BitSize(BitSize), BitOffset(BitOffset) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<BitFieldRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + TypeIndex getType() const { return Type; } uint8_t getBitOffset() const { return BitOffset; } uint8_t getBitSize() const { return BitSize; } private: + struct Layout { + TypeIndex Type; + uint8_t BitSize; + uint8_t BitOffset; + }; + TypeIndex Type; uint8_t BitSize; uint8_t BitOffset; }; -class VirtualTableShapeRecord : TypeRecord { +// LF_VTSHAPE +class VFTableShapeRecord : public TypeRecord { +public: + explicit VFTableShapeRecord(ArrayRef<VFTableSlotKind> Slots) + : TypeRecord(TypeRecordKind::VFTableShape), SlotsRef(Slots) {} + explicit VFTableShapeRecord(std::vector<VFTableSlotKind> Slots) + : TypeRecord(TypeRecordKind::VFTableShape), Slots(std::move(Slots)) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<VFTableShapeRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + ArrayRef<VFTableSlotKind> getSlots() const { + if (!SlotsRef.empty()) + return SlotsRef; + return Slots; + } + uint32_t getEntryCount() const { return getSlots().size(); } + +private: + struct Layout { + // Number of vftable entries. Each method may have more than one entry due + // to + // things like covariant return types. + ulittle16_t VFEntryCount; + // Descriptors[]: 4-bit virtual method descriptors of type CV_VTS_desc_e. + }; + +private: + ArrayRef<VFTableSlotKind> SlotsRef; + std::vector<VFTableSlotKind> Slots; +}; + +// LF_TYPESERVER2 +class TypeServer2Record : public TypeRecord { +public: + TypeServer2Record(StringRef Guid, uint32_t Age, StringRef Name) + : TypeRecord(TypeRecordKind::TypeServer2), Guid(Guid), Age(Age), + Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<TypeServer2Record> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + StringRef getGuid() const { return Guid; } + + uint32_t getAge() const { return Age; } + + StringRef getName() const { return Name; } + +private: + struct Layout { + char Guid[16]; // GUID + ulittle32_t Age; + // Name: Name of the PDB as a null-terminated string + }; + + StringRef Guid; + uint32_t Age; + StringRef Name; +}; + +// LF_STRING_ID +class StringIdRecord : public TypeRecord { +public: + StringIdRecord(TypeIndex Id, StringRef String) + : TypeRecord(TypeRecordKind::StringId), Id(Id), String(String) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<StringIdRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getId() const { return Id; } + + StringRef getString() const { return String; } + +private: + struct Layout { + TypeIndex id; + // Name: Name of the PDB as a null-terminated string + }; + + TypeIndex Id; + StringRef String; +}; + +// LF_FUNC_ID +class FuncIdRecord : public TypeRecord { +public: + FuncIdRecord(TypeIndex ParentScope, TypeIndex FunctionType, StringRef Name) + : TypeRecord(TypeRecordKind::FuncId), ParentScope(ParentScope), + FunctionType(FunctionType), Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<FuncIdRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getParentScope() const { return ParentScope; } + + TypeIndex getFunctionType() const { return FunctionType; } + + StringRef getName() const { return Name; } + +private: + struct Layout { + TypeIndex ParentScope; + TypeIndex FunctionType; + // Name: The null-terminated name follows. + }; + + TypeIndex ParentScope; + TypeIndex FunctionType; + StringRef Name; +}; + +// LF_UDT_SRC_LINE +class UdtSourceLineRecord : public TypeRecord { public: - explicit VirtualTableShapeRecord(ArrayRef<VirtualTableSlotKind> Slots) - : TypeRecord(TypeRecordKind::VirtualTableShape), Slots(Slots) {} + UdtSourceLineRecord(TypeIndex UDT, TypeIndex SourceFile, uint32_t LineNumber) + : TypeRecord(TypeRecordKind::UdtSourceLine), UDT(UDT), + SourceFile(SourceFile), LineNumber(LineNumber) {} - ArrayRef<VirtualTableSlotKind> getSlots() const { return Slots; } + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<UdtSourceLineRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getUDT() const { return UDT; } + TypeIndex getSourceFile() const { return SourceFile; } + uint32_t getLineNumber() const { return LineNumber; } private: - ArrayRef<VirtualTableSlotKind> Slots; + struct Layout { + TypeIndex UDT; // The user-defined type + TypeIndex SourceFile; // StringID containing the source filename + ulittle32_t LineNumber; + }; + + TypeIndex UDT; + TypeIndex SourceFile; + uint32_t LineNumber; }; + +// LF_UDT_MOD_SRC_LINE +class UdtModSourceLineRecord : public TypeRecord { +public: + UdtModSourceLineRecord(TypeIndex UDT, TypeIndex SourceFile, + uint32_t LineNumber, uint16_t Module) + : TypeRecord(TypeRecordKind::UdtSourceLine), UDT(UDT), + SourceFile(SourceFile), LineNumber(LineNumber), Module(Module) {} + + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<UdtModSourceLineRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data) { + const Layout *L = nullptr; + CV_DESERIALIZE(Data, L); + + return UdtModSourceLineRecord(L->UDT, L->SourceFile, L->LineNumber, + L->Module); + } + + TypeIndex getUDT() const { return UDT; } + TypeIndex getSourceFile() const { return SourceFile; } + uint32_t getLineNumber() const { return LineNumber; } + uint16_t getModule() const { return Module; } + +private: + struct Layout { + TypeIndex UDT; // The user-defined type + TypeIndex SourceFile; // StringID containing the source filename + ulittle32_t LineNumber; + ulittle16_t Module; // Module that contributes this UDT definition + }; + + TypeIndex UDT; + TypeIndex SourceFile; + uint32_t LineNumber; + uint16_t Module; +}; + +// LF_BUILDINFO +class BuildInfoRecord : public TypeRecord { +public: + BuildInfoRecord(ArrayRef<TypeIndex> ArgIndices) + : TypeRecord(TypeRecordKind::BuildInfo), + ArgIndices(ArgIndices.begin(), ArgIndices.end()) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<BuildInfoRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + ArrayRef<TypeIndex> getArgs() const { return ArgIndices; } + +private: + struct Layout { + ulittle16_t NumArgs; // Number of arguments + // ArgTypes[]: Type indicies of arguments + }; + SmallVector<TypeIndex, 4> ArgIndices; +}; + +// LF_VFTABLE +class VFTableRecord : public TypeRecord { +public: + VFTableRecord(TypeIndex CompleteClass, TypeIndex OverriddenVFTable, + uint32_t VFPtrOffset, StringRef Name, + ArrayRef<StringRef> Methods) + : TypeRecord(TypeRecordKind::VFTable), + CompleteClass(CompleteClass), OverriddenVFTable(OverriddenVFTable), + VFPtrOffset(VFPtrOffset), Name(Name), MethodNamesRef(Methods) {} + VFTableRecord(TypeIndex CompleteClass, TypeIndex OverriddenVFTable, + uint32_t VFPtrOffset, StringRef Name, + const std::vector<StringRef> &Methods) + : TypeRecord(TypeRecordKind::VFTable), + CompleteClass(CompleteClass), OverriddenVFTable(OverriddenVFTable), + VFPtrOffset(VFPtrOffset), Name(Name), MethodNames(Methods) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<VFTableRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getCompleteClass() const { return CompleteClass; } + TypeIndex getOverriddenVTable() const { return OverriddenVFTable; } + uint32_t getVFPtrOffset() const { return VFPtrOffset; } + StringRef getName() const { return Name; } + ArrayRef<StringRef> getMethodNames() const { + if (!MethodNamesRef.empty()) + return MethodNamesRef; + return MethodNames; + } + +private: + struct Layout { + TypeIndex CompleteClass; // Class that owns this vftable. + TypeIndex OverriddenVFTable; // VFTable that this overrides. + ulittle32_t VFPtrOffset; // VFPtr offset in CompleteClass + ulittle32_t NamesLen; // Length of subsequent names array in bytes. + // Names: A sequence of null-terminated strings. First string is vftable + // names. + }; + + TypeIndex CompleteClass; + TypeIndex OverriddenVFTable; + ulittle32_t VFPtrOffset; + StringRef Name; + ArrayRef<StringRef> MethodNamesRef; + std::vector<StringRef> MethodNames; +}; + +// LF_ONEMETHOD +class OneMethodRecord : public TypeRecord { +public: + OneMethodRecord(TypeIndex Type, MethodKind Kind, MethodOptions Options, + MemberAccess Access, int32_t VFTableOffset, StringRef Name) + : TypeRecord(TypeRecordKind::OneMethod), Type(Type), Kind(Kind), + Options(Options), Access(Access), VFTableOffset(VFTableOffset), + Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<OneMethodRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getType() const { return Type; } + MethodKind getKind() const { return Kind; } + MethodOptions getOptions() const { return Options; } + MemberAccess getAccess() const { return Access; } + int32_t getVFTableOffset() const { return VFTableOffset; } + StringRef getName() const { return Name; } + + bool isIntroducingVirtual() const { + return Kind == MethodKind::IntroducingVirtual || + Kind == MethodKind::PureIntroducingVirtual; + } + +private: + struct Layout { + MemberAttributes Attrs; + TypeIndex Type; + // If is introduced virtual method: + // VFTableOffset: int32_t offset in vftable + // Name: Null-terminated string + }; + + TypeIndex Type; + MethodKind Kind; + MethodOptions Options; + MemberAccess Access; + int32_t VFTableOffset; + StringRef Name; +}; + +// LF_METHODLIST +class MethodOverloadListRecord : public TypeRecord { +public: + MethodOverloadListRecord(ArrayRef<OneMethodRecord> Methods) + : TypeRecord(TypeRecordKind::MethodOverloadList), Methods(Methods) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<MethodOverloadListRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + ArrayRef<OneMethodRecord> getMethods() const { return Methods; } + +private: + struct Layout { + MemberAttributes Attrs; + ulittle16_t Padding; + + TypeIndex Type; + // If is introduced virtual method: + // VFTableOffset: int32_t offset in vftable + }; + + std::vector<OneMethodRecord> Methods; +}; + +/// For method overload sets. LF_METHOD +class OverloadedMethodRecord : public TypeRecord { +public: + OverloadedMethodRecord(uint16_t NumOverloads, TypeIndex MethodList, + StringRef Name) + : TypeRecord(TypeRecordKind::OverloadedMethod), + NumOverloads(NumOverloads), MethodList(MethodList), Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<OverloadedMethodRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + uint16_t getNumOverloads() const { return NumOverloads; } + TypeIndex getMethodList() const { return MethodList; } + StringRef getName() const { return Name; } + +private: + struct Layout { + ulittle16_t MethodCount; // Size of overload set + TypeIndex MethList; // Type index of methods in overload set + // Name: Null-terminated string + }; + + uint16_t NumOverloads; + TypeIndex MethodList; + StringRef Name; +}; + +// LF_MEMBER +class DataMemberRecord : public TypeRecord { +public: + DataMemberRecord(MemberAccess Access, TypeIndex Type, uint64_t Offset, + StringRef Name) + : TypeRecord(TypeRecordKind::DataMember), Access(Access), Type(Type), + FieldOffset(Offset), Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<DataMemberRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + MemberAccess getAccess() const { return Access; } + TypeIndex getType() const { return Type; } + uint64_t getFieldOffset() const { return FieldOffset; } + StringRef getName() const { return Name; } + +private: + struct Layout { + MemberAttributes Attrs; // Access control attributes, etc + TypeIndex Type; + // FieldOffset: LF_NUMERIC encoded byte offset + // Name: Null-terminated string + }; + + MemberAccess Access; + TypeIndex Type; + uint64_t FieldOffset; + StringRef Name; +}; + +// LF_STMEMBER +class StaticDataMemberRecord : public TypeRecord { +public: + StaticDataMemberRecord(MemberAccess Access, TypeIndex Type, StringRef Name) + : TypeRecord(TypeRecordKind::StaticDataMember), Access(Access), + Type(Type), Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<StaticDataMemberRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + MemberAccess getAccess() const { return Access; } + TypeIndex getType() const { return Type; } + StringRef getName() const { return Name; } + +private: + struct Layout { + MemberAttributes Attrs; // Access control attributes, etc + TypeIndex Type; + // Name: Null-terminated string + }; + + MemberAccess Access; + TypeIndex Type; + StringRef Name; +}; + +// LF_ENUMERATE +class EnumeratorRecord : public TypeRecord { +public: + EnumeratorRecord(MemberAccess Access, APSInt Value, StringRef Name) + : TypeRecord(TypeRecordKind::Enumerator), Access(Access), + Value(std::move(Value)), Name(Name) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<EnumeratorRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + MemberAccess getAccess() const { return Access; } + APSInt getValue() const { return Value; } + StringRef getName() const { return Name; } + +private: + struct Layout { + MemberAttributes Attrs; // Access control attributes, etc + // EnumValue: LF_NUMERIC encoded enumerator value + // Name: Null-terminated string + }; + + MemberAccess Access; + APSInt Value; + StringRef Name; +}; + +// LF_VFUNCTAB +class VFPtrRecord : public TypeRecord { +public: + VFPtrRecord(TypeIndex Type) + : TypeRecord(TypeRecordKind::VFPtr), Type(Type) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<VFPtrRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + TypeIndex getType() const { return Type; } + +private: + struct Layout { + ulittle16_t Pad0; + TypeIndex Type; // Type of vfptr + }; + TypeIndex Type; +}; + +// LF_BCLASS, LF_BINTERFACE +class BaseClassRecord : public TypeRecord { +public: + BaseClassRecord(MemberAccess Access, TypeIndex Type, uint64_t Offset) + : TypeRecord(TypeRecordKind::BaseClass), Access(Access), Type(Type), + Offset(Offset) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<BaseClassRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + MemberAccess getAccess() const { return Access; } + TypeIndex getBaseType() const { return Type; } + uint64_t getBaseOffset() const { return Offset; } + +private: + struct Layout { + MemberAttributes Attrs; // Access control attributes, etc + TypeIndex BaseType; // Base class type + // BaseOffset: LF_NUMERIC encoded byte offset of base from derived. + }; + MemberAccess Access; + TypeIndex Type; + uint64_t Offset; +}; + +// LF_VBCLASS, LF_IVBCLASS +class VirtualBaseClassRecord : public TypeRecord { +public: + VirtualBaseClassRecord(MemberAccess Access, TypeIndex BaseType, + TypeIndex VBPtrType, uint64_t Offset, uint64_t Index) + : TypeRecord(TypeRecordKind::VirtualBaseClass), Access(Access), + BaseType(BaseType), VBPtrType(VBPtrType), VBPtrOffset(Offset), + VTableIndex(Index) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<VirtualBaseClassRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + + MemberAccess getAccess() const { return Access; } + TypeIndex getBaseType() const { return BaseType; } + TypeIndex getVBPtrType() const { return VBPtrType; } + uint64_t getVBPtrOffset() const { return VBPtrOffset; } + uint64_t getVTableIndex() const { return VTableIndex; } + +private: + struct Layout { + MemberAttributes Attrs; // Access control attributes, etc. + TypeIndex BaseType; // Base class type + TypeIndex VBPtrType; // Virtual base pointer type + // VBPtrOffset: Offset of vbptr from vfptr encoded as LF_NUMERIC. + // VBTableIndex: Index of vbase within vbtable encoded as LF_NUMERIC. + }; + MemberAccess Access; + TypeIndex BaseType; + TypeIndex VBPtrType; + uint64_t VBPtrOffset; + uint64_t VTableIndex; +}; + +/// LF_INDEX - Used to chain two large LF_FIELDLIST or LF_METHODLIST records +/// together. The first will end in an LF_INDEX record that points to the next. +class ListContinuationRecord : public TypeRecord { +public: + ListContinuationRecord(TypeIndex ContinuationIndex) + : TypeRecord(TypeRecordKind::ListContinuation), + ContinuationIndex(ContinuationIndex) {} + + TypeIndex getContinuationIndex() const { return ContinuationIndex; } + + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + + static ErrorOr<ListContinuationRecord> deserialize(TypeRecordKind Kind, + ArrayRef<uint8_t> &Data); + +private: + struct Layout { + ulittle16_t Pad0; + TypeIndex ContinuationIndex; + }; + TypeIndex ContinuationIndex; +}; + +typedef CVRecord<TypeLeafKind> CVType; +typedef VarStreamArray<CVType> CVTypeArray; } } diff --git a/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h b/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h index 1f48cf70666df..eb7993baab894 100644 --- a/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h +++ b/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h @@ -10,9 +10,10 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPERECORDBUILDER_H #define LLVM_DEBUGINFO_CODEVIEW_TYPERECORDBUILDER_H +#include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" -#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/raw_ostream.h" @@ -39,13 +40,25 @@ public: void writeEncodedInteger(int64_t Value); void writeEncodedSignedInteger(int64_t Value); void writeEncodedUnsignedInteger(uint64_t Value); - void writeNullTerminatedString(const char *Value); void writeNullTerminatedString(StringRef Value); + void writeGuid(StringRef Guid); + void writeBytes(StringRef Value) { Stream << Value; } llvm::StringRef str(); uint64_t size() const { return Stream.tell(); } + void truncate(uint64_t Size) { + // This works because raw_svector_ostream is not buffered. + assert(Size < Buffer.size()); + Buffer.resize(Size); + } + + void reset(TypeRecordKind K) { + Buffer.clear(); + writeTypeRecordKind(K); + } + private: llvm::SmallVector<char, 256> Buffer; llvm::raw_svector_ostream Stream; diff --git a/include/llvm/DebugInfo/CodeView/TypeRecords.def b/include/llvm/DebugInfo/CodeView/TypeRecords.def new file mode 100644 index 0000000000000..0959f4bf19c71 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/TypeRecords.def @@ -0,0 +1,252 @@ + +//===-- CVLeafTypes.def - All CodeView leaf types ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// See LEAF_ENUM_e in cvinfo.h. This should match the constants there. +// +//===----------------------------------------------------------------------===// + +// If the type is known, then we have a record describing it in TypeRecord.h. + +#ifndef CV_TYPE +#define CV_TYPE(lf_ename, value) +#endif + +// If the type is known, then we have a record describing it in TypeRecord.h. +#ifndef TYPE_RECORD +#define TYPE_RECORD(lf_ename, value, name) CV_TYPE(lf_ename, value) +#endif + +#ifndef TYPE_RECORD_ALIAS +#define TYPE_RECORD_ALIAS(lf_ename, value, name, alias_name) \ + TYPE_RECORD(lf_ename, value, name) +#endif + +#ifndef MEMBER_RECORD +#define MEMBER_RECORD(lf_ename, value, name) TYPE_RECORD(lf_ename, value, name) +#endif + +#ifndef MEMBER_RECORD_ALIAS +#define MEMBER_RECORD_ALIAS(lf_ename, value, name, alias_name) \ + MEMBER_RECORD(lf_ename, value, name) +#endif + +TYPE_RECORD(LF_POINTER, 0x1002, Pointer) +TYPE_RECORD(LF_MODIFIER, 0x1001, Modifier) +TYPE_RECORD(LF_PROCEDURE, 0x1008, Procedure) +TYPE_RECORD(LF_MFUNCTION, 0x1009, MemberFunction) +TYPE_RECORD(LF_ARGLIST, 0x1201, ArgList) + +TYPE_RECORD(LF_ARRAY, 0x1503, Array) +TYPE_RECORD(LF_CLASS, 0x1504, Class) +TYPE_RECORD_ALIAS(LF_STRUCTURE, 0x1505, Struct, Class) +TYPE_RECORD_ALIAS(LF_INTERFACE, 0x1519, Interface, Class) +TYPE_RECORD(LF_UNION, 0x1506, Union) +TYPE_RECORD(LF_ENUM, 0x1507, Enum) +TYPE_RECORD(LF_TYPESERVER2, 0x1515, TypeServer2) +TYPE_RECORD(LF_VFTABLE, 0x151d, VFTable) +TYPE_RECORD(LF_VTSHAPE, 0x000a, VFTableShape) + +TYPE_RECORD(LF_BITFIELD, 0x1205, BitField) + +// Member type records. These are generally not length prefixed, and appear +// inside of a field list record. +MEMBER_RECORD(LF_BCLASS, 0x1400, BaseClass) +MEMBER_RECORD_ALIAS(LF_BINTERFACE, 0x151a, BaseInterface, BaseClass) + +MEMBER_RECORD(LF_VBCLASS, 0x1401, VirtualBaseClass) +MEMBER_RECORD_ALIAS(LF_IVBCLASS, 0x1402, IndirectVirtualBaseClass, + VirtualBaseClass) + +MEMBER_RECORD(LF_VFUNCTAB, 0x1409, VFPtr) +MEMBER_RECORD(LF_STMEMBER, 0x150e, StaticDataMember) +MEMBER_RECORD(LF_METHOD, 0x150f, OverloadedMethod) +MEMBER_RECORD(LF_MEMBER, 0x150d, DataMember) +MEMBER_RECORD(LF_NESTTYPE, 0x1510, NestedType) +MEMBER_RECORD(LF_ONEMETHOD, 0x1511, OneMethod) +MEMBER_RECORD(LF_ENUMERATE, 0x1502, Enumerator) +MEMBER_RECORD(LF_INDEX, 0x1404, ListContinuation) + +// ID leaf records. Subsequent leaf types may be referenced from .debug$S. +TYPE_RECORD(LF_FUNC_ID, 0x1601, FuncId) +TYPE_RECORD(LF_MFUNC_ID, 0x1602, MemberFuncId) +TYPE_RECORD(LF_BUILDINFO, 0x1603, BuildInfo) +// FIXME: We reuse the structure of ArgListRecord for substring lists, but it +// makes for confusing dumper output. +TYPE_RECORD_ALIAS(LF_SUBSTR_LIST, 0x1604, StringList, ArgList) +TYPE_RECORD(LF_STRING_ID, 0x1605, StringId) +TYPE_RECORD(LF_UDT_SRC_LINE, 0x1606, UdtSourceLine) +TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine) + + +TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList) + + +// 16 bit type records. +CV_TYPE(LF_MODIFIER_16t, 0x0001) +CV_TYPE(LF_POINTER_16t, 0x0002) +CV_TYPE(LF_ARRAY_16t, 0x0003) +CV_TYPE(LF_CLASS_16t, 0x0004) +CV_TYPE(LF_STRUCTURE_16t, 0x0005) +CV_TYPE(LF_UNION_16t, 0x0006) +CV_TYPE(LF_ENUM_16t, 0x0007) +CV_TYPE(LF_PROCEDURE_16t, 0x0008) +CV_TYPE(LF_MFUNCTION_16t, 0x0009) +CV_TYPE(LF_COBOL0_16t, 0x000b) +CV_TYPE(LF_COBOL1, 0x000c) +CV_TYPE(LF_BARRAY_16t, 0x000d) +CV_TYPE(LF_LABEL, 0x000e) +CV_TYPE(LF_NULLLEAF, 0x000f) // LF_NULL +CV_TYPE(LF_NOTTRAN, 0x0010) +CV_TYPE(LF_DIMARRAY_16t, 0x0011) +CV_TYPE(LF_VFTPATH_16t, 0x0012) +CV_TYPE(LF_PRECOMP_16t, 0x0013) +CV_TYPE(LF_ENDPRECOMP, 0x0014) +CV_TYPE(LF_OEM_16t, 0x0015) +CV_TYPE(LF_TYPESERVER_ST, 0x0016) + +CV_TYPE(LF_SKIP_16t, 0x0200) +CV_TYPE(LF_ARGLIST_16t, 0x0201) +CV_TYPE(LF_DEFARG_16t, 0x0202) +CV_TYPE(LF_LIST, 0x0203) +CV_TYPE(LF_FIELDLIST_16t, 0x0204) +CV_TYPE(LF_DERIVED_16t, 0x0205) +CV_TYPE(LF_BITFIELD_16t, 0x0206) +CV_TYPE(LF_METHODLIST_16t, 0x0207) +CV_TYPE(LF_DIMCONU_16t, 0x0208) +CV_TYPE(LF_DIMCONLU_16t, 0x0209) +CV_TYPE(LF_DIMVARU_16t, 0x020a) +CV_TYPE(LF_DIMVARLU_16t, 0x020b) +CV_TYPE(LF_REFSYM, 0x020c) + +// 16 bit member types. Generally not length prefixed. +CV_TYPE(LF_BCLASS_16t, 0x0400) +CV_TYPE(LF_VBCLASS_16t, 0x0401) +CV_TYPE(LF_IVBCLASS_16t, 0x0402) +CV_TYPE(LF_ENUMERATE_ST, 0x0403) +CV_TYPE(LF_FRIENDFCN_16t, 0x0404) +CV_TYPE(LF_INDEX_16t, 0x0405) +CV_TYPE(LF_MEMBER_16t, 0x0406) +CV_TYPE(LF_STMEMBER_16t, 0x0407) +CV_TYPE(LF_METHOD_16t, 0x0408) +CV_TYPE(LF_NESTTYPE_16t, 0x0409) +CV_TYPE(LF_VFUNCTAB_16t, 0x040a) +CV_TYPE(LF_FRIENDCLS_16t, 0x040b) +CV_TYPE(LF_ONEMETHOD_16t, 0x040c) +CV_TYPE(LF_VFUNCOFF_16t, 0x040d) + +CV_TYPE(LF_TI16_MAX, 0x1000) + +CV_TYPE(LF_ARRAY_ST, 0x1003) +CV_TYPE(LF_CLASS_ST, 0x1004) +CV_TYPE(LF_STRUCTURE_ST, 0x1005) +CV_TYPE(LF_UNION_ST, 0x1006) +CV_TYPE(LF_ENUM_ST, 0x1007) +CV_TYPE(LF_COBOL0, 0x100a) +CV_TYPE(LF_BARRAY, 0x100b) +CV_TYPE(LF_DIMARRAY_ST, 0x100c) +CV_TYPE(LF_VFTPATH, 0x100d) +CV_TYPE(LF_PRECOMP_ST, 0x100e) +CV_TYPE(LF_OEM, 0x100f) +CV_TYPE(LF_ALIAS_ST, 0x1010) +CV_TYPE(LF_OEM2, 0x1011) + +CV_TYPE(LF_SKIP, 0x1200) +CV_TYPE(LF_DEFARG_ST, 0x1202) +CV_TYPE(LF_FIELDLIST, 0x1203) +CV_TYPE(LF_DERIVED, 0x1204) +CV_TYPE(LF_DIMCONU, 0x1207) +CV_TYPE(LF_DIMCONLU, 0x1208) +CV_TYPE(LF_DIMVARU, 0x1209) +CV_TYPE(LF_DIMVARLU, 0x120a) + +// Member type records. These are generally not length prefixed, and appear +// inside of a field list record. +CV_TYPE(LF_FRIENDFCN_ST, 0x1403) +CV_TYPE(LF_MEMBER_ST, 0x1405) +CV_TYPE(LF_STMEMBER_ST, 0x1406) +CV_TYPE(LF_METHOD_ST, 0x1407) +CV_TYPE(LF_NESTTYPE_ST, 0x1408) +CV_TYPE(LF_FRIENDCLS, 0x140a) +CV_TYPE(LF_ONEMETHOD_ST, 0x140b) +CV_TYPE(LF_VFUNCOFF, 0x140c) +CV_TYPE(LF_NESTTYPEEX_ST, 0x140d) +CV_TYPE(LF_MEMBERMODIFY_ST, 0x140e) +CV_TYPE(LF_MANAGED_ST, 0x140f) + +CV_TYPE(LF_ST_MAX, 0x1500) +CV_TYPE(LF_TYPESERVER, 0x1501) +CV_TYPE(LF_DIMARRAY, 0x1508) +CV_TYPE(LF_PRECOMP, 0x1509) +CV_TYPE(LF_ALIAS, 0x150a) +CV_TYPE(LF_DEFARG, 0x150b) +CV_TYPE(LF_FRIENDFCN, 0x150c) +CV_TYPE(LF_NESTTYPEEX, 0x1512) +CV_TYPE(LF_MEMBERMODIFY, 0x1513) +CV_TYPE(LF_MANAGED, 0x1514) +CV_TYPE(LF_STRIDED_ARRAY, 0x1516) +CV_TYPE(LF_HLSL, 0x1517) +CV_TYPE(LF_MODIFIER_EX, 0x1518) +CV_TYPE(LF_VECTOR, 0x151b) +CV_TYPE(LF_MATRIX, 0x151c) + +// ID leaf records. Subsequent leaf types may be referenced from .debug$S. + +// Numeric leaf types. These are generally contained in other records, and not +// encountered in the main type stream. + +CV_TYPE(LF_NUMERIC, 0x8000) +CV_TYPE(LF_CHAR, 0x8000) +CV_TYPE(LF_SHORT, 0x8001) +CV_TYPE(LF_USHORT, 0x8002) +CV_TYPE(LF_LONG, 0x8003) +CV_TYPE(LF_ULONG, 0x8004) +CV_TYPE(LF_REAL32, 0x8005) +CV_TYPE(LF_REAL64, 0x8006) +CV_TYPE(LF_REAL80, 0x8007) +CV_TYPE(LF_REAL128, 0x8008) +CV_TYPE(LF_QUADWORD, 0x8009) +CV_TYPE(LF_UQUADWORD, 0x800a) +CV_TYPE(LF_REAL48, 0x800b) +CV_TYPE(LF_COMPLEX32, 0x800c) +CV_TYPE(LF_COMPLEX64, 0x800d) +CV_TYPE(LF_COMPLEX80, 0x800e) +CV_TYPE(LF_COMPLEX128, 0x800f) +CV_TYPE(LF_VARSTRING, 0x8010) +CV_TYPE(LF_OCTWORD, 0x8017) +CV_TYPE(LF_UOCTWORD, 0x8018) +CV_TYPE(LF_DECIMAL, 0x8019) +CV_TYPE(LF_DATE, 0x801a) +CV_TYPE(LF_UTF8STRING, 0x801b) +CV_TYPE(LF_REAL16, 0x801c) + +// Padding bytes. These are emitted into alignment bytes in the type stream. + +CV_TYPE(LF_PAD0, 0xf0) +CV_TYPE(LF_PAD1, 0xf1) +CV_TYPE(LF_PAD2, 0xf2) +CV_TYPE(LF_PAD3, 0xf3) +CV_TYPE(LF_PAD4, 0xf4) +CV_TYPE(LF_PAD5, 0xf5) +CV_TYPE(LF_PAD6, 0xf6) +CV_TYPE(LF_PAD7, 0xf7) +CV_TYPE(LF_PAD8, 0xf8) +CV_TYPE(LF_PAD9, 0xf9) +CV_TYPE(LF_PAD10, 0xfa) +CV_TYPE(LF_PAD11, 0xfb) +CV_TYPE(LF_PAD12, 0xfc) +CV_TYPE(LF_PAD13, 0xfd) +CV_TYPE(LF_PAD14, 0xfe) +CV_TYPE(LF_PAD15, 0xff) + +#undef CV_TYPE +#undef TYPE_RECORD +#undef TYPE_RECORD_ALIAS +#undef MEMBER_RECORD +#undef MEMBER_RECORD_ALIAS diff --git a/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h b/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h new file mode 100644 index 0000000000000..af396c79d0741 --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -0,0 +1,26 @@ +//===- TypeStreamMerger.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESTREAMMERGER_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPESTREAMMERGER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" + +namespace llvm { +namespace codeview { + +/// Merges one type stream into another. Returns true on success. +bool mergeTypeStreams(TypeTableBuilder &DestStream, const CVTypeArray &Types); + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_TYPESTREAMMERGER_H diff --git a/include/llvm/DebugInfo/CodeView/TypeSymbolEmitter.h b/include/llvm/DebugInfo/CodeView/TypeSymbolEmitter.h index 9de110e8236fd..dfba83d62fcee 100644 --- a/include/llvm/DebugInfo/CodeView/TypeSymbolEmitter.h +++ b/include/llvm/DebugInfo/CodeView/TypeSymbolEmitter.h @@ -10,11 +10,12 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESYMBOLEMITTER_H #define LLVM_DEBUGINFO_CODEVIEW_TYPESYMBOLEMITTER_H -#include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" namespace llvm { +class StringRef; + namespace codeview { class TypeSymbolEmitter { diff --git a/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h b/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h index 2c950e8af792b..5b2aa61861470 100644 --- a/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h +++ b/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h @@ -10,13 +10,15 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H #define LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H -#include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/Compiler.h" namespace llvm { + +class StringRef; + namespace codeview { class FieldListRecordBuilder; @@ -38,20 +40,28 @@ public: TypeIndex writeModifier(const ModifierRecord &Record); TypeIndex writeProcedure(const ProcedureRecord &Record); TypeIndex writeMemberFunction(const MemberFunctionRecord &Record); - TypeIndex writeArgumentList(const ArgumentListRecord &Record); - TypeIndex writeRecord(TypeRecordBuilder &builder); + TypeIndex writeArgList(const ArgListRecord &Record); TypeIndex writePointer(const PointerRecord &Record); - TypeIndex writePointerToMember(const PointerToMemberRecord &Record); TypeIndex writeArray(const ArrayRecord &Record); - TypeIndex writeAggregate(const AggregateRecord &Record); + TypeIndex writeClass(const ClassRecord &Record); + TypeIndex writeUnion(const UnionRecord &Record); TypeIndex writeEnum(const EnumRecord &Record); TypeIndex writeBitField(const BitFieldRecord &Record); - TypeIndex writeVirtualTableShape(const VirtualTableShapeRecord &Record); + TypeIndex writeVFTableShape(const VFTableShapeRecord &Record); + TypeIndex writeStringId(const StringIdRecord &Record); + TypeIndex writeVFTable(const VFTableRecord &Record); + TypeIndex writeUdtSourceLine(const UdtSourceLineRecord &Record); + TypeIndex writeUdtModSourceLine(const UdtModSourceLineRecord &Record); + TypeIndex writeFuncId(const FuncIdRecord &Record); + TypeIndex writeMemberFuncId(const MemberFuncIdRecord &Record); + TypeIndex writeBuildInfo(const BuildInfoRecord &Record); + TypeIndex writeMethodOverloadList(const MethodOverloadListRecord &Record); + TypeIndex writeTypeServer2(const TypeServer2Record &Record); TypeIndex writeFieldList(FieldListRecordBuilder &FieldList); - TypeIndex writeMethodList(MethodListRecordBuilder &MethodList); -private: + TypeIndex writeRecord(TypeRecordBuilder &builder); + virtual TypeIndex writeRecord(llvm::StringRef record) = 0; }; } diff --git a/include/llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h b/include/llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h new file mode 100644 index 0000000000000..310847ec5d2dc --- /dev/null +++ b/include/llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h @@ -0,0 +1,63 @@ +//===- TypeVisitorCallbacks.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPEVISITORCALLBACKS_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPEVISITORCALLBACKS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { +class TypeVisitorCallbacks { + friend class CVTypeVisitor; + +public: + virtual ~TypeVisitorCallbacks() {} + + /// Action to take on unknown types. By default, they are ignored. + virtual Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) { + return Error::success(); + } + virtual Error visitUnknownMember(const CVRecord<TypeLeafKind> &Record) { + return Error::success(); + } + + /// Paired begin/end actions for all types. Receives all record data, + /// including the fixed-length record prefix. + virtual Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) { + return Error::success(); + } + virtual Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) { + return Error::success(); + } + + virtual Error visitFieldListBegin(const CVRecord<TypeLeafKind> &Record) { + return Error::success(); + } + + virtual Error visitFieldListEnd(const CVRecord<TypeLeafKind> &Record) { + return Error::success(); + } + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + virtual Error visit##Name(Name##Record &Record) { return Error::success(); } +#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 "TypeRecords.def" +}; +} +} + +#endif |