[readobj] Expand CodeView dumping functionality

This rewrites and expands the existing codeview dumping functionality in
llvm-readobj using techniques similar to those in lib/Object. This defines a
number of new records and enums useful for reading memory mapped codeview
sections in COFF objects.

The dumper is intended as a testing tool for LLVM as it grows more codeview
output capabilities.

Reviewers: majnemer

Differential Revision: http://reviews.llvm.org/D16104

llvm-svn: 257658
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index d44da0d..13e03bf 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -14,6 +14,7 @@
 
 #include "llvm-readobj.h"
 #include "ARMWinEHPrinter.h"
+#include "CodeView.h"
 #include "Error.h"
 #include "ObjDumper.h"
 #include "StackMapPrinter.h"
@@ -22,6 +23,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/COFF.h"
@@ -39,6 +41,7 @@
 
 using namespace llvm;
 using namespace llvm::object;
+using namespace llvm::codeview;
 using namespace llvm::Win64EH;
 
 namespace {
@@ -72,12 +75,18 @@
   void printBaseOfDataField(const pe32_header *Hdr);
   void printBaseOfDataField(const pe32plus_header *Hdr);
 
-  void printCodeViewSection(const SectionRef &Section);
+  void printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section);
+  void printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section);
+  void printCodeViewFieldList(StringRef FieldData);
+  StringRef getTypeName(TypeIndex Ty);
+  void printTypeIndex(StringRef FieldName, TypeIndex TI);
 
   void printCodeViewSymbolsSubsection(StringRef Subsection,
                                       const SectionRef &Section,
                                       uint32_t Offset);
 
+  void printMemberAttributes(MemberAttributes Attrs);
+
   void cacheRelocations();
 
   std::error_code resolveSymbol(const coff_section *Section, uint64_t Offset,
@@ -96,6 +105,13 @@
   RelocMapTy RelocMap;
   StringRef CVFileIndexToStringOffsetTable;
   StringRef CVStringTable;
+
+  /// 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;
 };
 
 } // namespace
@@ -331,6 +347,353 @@
   { "Alias"    , COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS     }
 };
 
+static const EnumEntry<CompileSym3::Flags> CompileSym3Flags[] = {
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, EC),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, NoDbgInfo),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, LTCG),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, NoDataAlign),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, ManagedPresent),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, SecurityChecks),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, HotPatch),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, CVTCIL),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, MSILModule),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, Sdl),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, PGO),
+    LLVM_READOBJ_ENUM_ENT(CompileSym3, Exp),
+};
+
+static const EnumEntry<codeview::SourceLanguage> SourceLanguages[] = {
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, C),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Cpp),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Fortran),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Masm),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Pascal),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Basic),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Cobol),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Link),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Cvtres),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Cvtpgd),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, CSharp),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, VB),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, ILAsm),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, Java),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, JScript),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, MSIL),
+    LLVM_READOBJ_ENUM_ENT(SourceLanguage, HLSL),
+};
+
+static const EnumEntry<uint32_t> SubSectionTypes[] = {
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, Symbols),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, Lines),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, StringTable),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, FileChecksums),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, FrameData),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, InlineeLines),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeImports),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeExports),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, ILLines),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, FuncMDTokenMap),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, TypeMDTokenMap),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, MergedAssemblyInput),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, CoffSymbolRVA),
+};
+
+static const EnumEntry<unsigned> CPUTypeNames[] = {
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Intel8080),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Intel8086),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Intel80286),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Intel80386),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Intel80486),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Pentium),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PentiumPro),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Pentium3),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPS),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPS16),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPS32),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPS64),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPSI),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPSII),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPSIII),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPSIV),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, MIPSV),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, M68000),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, M68010),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, M68020),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, M68030),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, M68040),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Alpha),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Alpha21164),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Alpha21164A),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Alpha21264),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Alpha21364),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PPC601),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PPC603),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PPC604),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PPC620),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PPCFP),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, PPCBE),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, SH3),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, SH3E),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, SH3DSP),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, SH4),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, SHMedia),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM3),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM4),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM4T),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM5),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM5T),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM6),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM_XMAC),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM_WMMX),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARM7),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Omni),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Ia64),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Ia64_2),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, CEE),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, AM33),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, M32R),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, TriCore),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, X64),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, EBC),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, Thumb),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, ARMNT),
+  LLVM_READOBJ_ENUM_CLASS_ENT(CPUType, D3D11_Shader),
+};
+
+static const EnumEntry<uint8_t> ProcSymFlags[] = {
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, HasFP),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, HasIRET),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, HasFRET),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, IsNoReturn),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, IsUnreachable),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, HasCustomCallingConv),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, IsNoInline),
+    LLVM_READOBJ_ENUM_ENT(ProcFlags, HasOptimizedDebugInfo),
+};
+
+static const EnumEntry<uint32_t> FrameProcSymFlags[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, HasAlloca),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, HasSetJmp),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, HasLongJmp),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, HasInlineAssembly),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, HasExceptionHandling),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, MarkedInline),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions,
+                                HasStructuredExceptionHandling),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, Naked),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, SecurityChecks),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions,
+                                AsynchronousExceptionHandling),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions,
+                                NoStackOrderingForSecurityChecks),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, Inlined),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, StrictSecurityChecks),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, SafeBuffers),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions,
+                                ProfileGuidedOptimization),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, ValidProfileCounts),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, OptimizedForSpeed),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, GuardCfg),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FrameProcedureOptions, GuardCfw),
+};
+
+static const EnumEntry<uint32_t> FrameDataFlags[] = {
+    LLVM_READOBJ_ENUM_ENT(FrameData, HasSEH),
+    LLVM_READOBJ_ENUM_ENT(FrameData, HasEH),
+    LLVM_READOBJ_ENUM_ENT(FrameData, IsFunctionStart),
+};
+
+static const EnumEntry<uint16_t> LocalFlags[] = {
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsParameter),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsAddressTaken),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsCompilerGenerated),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsAggregate),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsAggregated),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsAliased),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsAlias),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsReturnValue),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsOptimizedOut),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsEnregisteredGlobal),
+    LLVM_READOBJ_ENUM_ENT(LocalSym, IsEnregisteredStatic),
+};
+
+static const EnumEntry<uint16_t> FrameCookieKinds[] = {
+    LLVM_READOBJ_ENUM_ENT(FrameCookieSym, Copy),
+    LLVM_READOBJ_ENUM_ENT(FrameCookieSym, XorStackPointer),
+    LLVM_READOBJ_ENUM_ENT(FrameCookieSym, XorFramePointer),
+    LLVM_READOBJ_ENUM_ENT(FrameCookieSym, XorR13),
+};
+
+static const EnumEntry<uint16_t> ClassOptionNames[] = {
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, Packed),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, HasConstructorOrDestructor),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, HasOverloadedOperator),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, Nested),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, ContainsNestedClass),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, HasOverloadedAssignmentOperator),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, HasConversionOperator),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, ForwardReference),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, Scoped),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, HasUniqueName),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, Sealed),
+  LLVM_READOBJ_ENUM_CLASS_ENT(ClassOptions, Intrinsic),
+};
+
+static const EnumEntry<uint8_t> MemberAccessNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(MemberAccess, None),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MemberAccess, Private),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MemberAccess, Protected),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MemberAccess, Public),
+};
+
+static const EnumEntry<uint16_t> MethodOptionNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodOptions, Pseudo),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodOptions, NoInherit),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodOptions, NoConstruct),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodOptions, CompilerGenerated),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodOptions, Sealed),
+};
+
+static const EnumEntry<uint16_t> MemberKindNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, Vanilla),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, Virtual),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, Static),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, Friend),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, IntroducingVirtual),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, PureVirtual),
+    LLVM_READOBJ_ENUM_CLASS_ENT(MethodKind, PureIntroducingVirtual),
+};
+
+/// The names here all end in "*". If the simple type is a pointer type, we
+/// return the whole name. Otherwise we lop off the last character in our
+/// StringRef.
+static const EnumEntry<SimpleTypeKind> SimpleTypeNames[] = {
+    {"void*", SimpleTypeKind::Void},
+    {"<not translated>*", SimpleTypeKind::NotTranslated},
+    {"HRESULT*", SimpleTypeKind::HResult},
+    {"signed char*", SimpleTypeKind::SignedCharacter},
+    {"unsigned char*", SimpleTypeKind::UnsignedCharacter},
+    {"char*", SimpleTypeKind::NarrowCharacter},
+    {"wchar_t*", SimpleTypeKind::WideCharacter},
+    {"__int8*", SimpleTypeKind::SByte},
+    {"unsigned __int8*", SimpleTypeKind::Byte},
+    {"short*", SimpleTypeKind::Int16Short},
+    {"unsigned short*", SimpleTypeKind::UInt16Short},
+    {"__int16*", SimpleTypeKind::Int16},
+    {"unsigned __int16*", SimpleTypeKind::UInt16},
+    {"long*", SimpleTypeKind::Int32Long},
+    {"unsigned long*", SimpleTypeKind::UInt32Long},
+    {"int*", SimpleTypeKind::Int32},
+    {"unsigned*", SimpleTypeKind::UInt32},
+    {"__int64*", SimpleTypeKind::Int64Quad},
+    {"unsigned __int64*", SimpleTypeKind::UInt64Quad},
+    {"__int64*", SimpleTypeKind::Int64},
+    {"unsigned __int64*", SimpleTypeKind::UInt64},
+    {"__int128*", SimpleTypeKind::Int128},
+    {"unsigned __int128*", SimpleTypeKind::UInt128},
+    {"__half*", SimpleTypeKind::Float16},
+    {"float*", SimpleTypeKind::Float32},
+    {"float*", SimpleTypeKind::Float32PartialPrecision},
+    {"__float48*", SimpleTypeKind::Float48},
+    {"double*", SimpleTypeKind::Float64},
+    {"long double*", SimpleTypeKind::Float80},
+    {"__float128*", SimpleTypeKind::Float128},
+    {"_Complex float*", SimpleTypeKind::Complex32},
+    {"_Complex double*", SimpleTypeKind::Complex64},
+    {"_Complex long double*", SimpleTypeKind::Complex80},
+    {"_Complex __float128*", SimpleTypeKind::Complex128},
+    {"bool*", SimpleTypeKind::Boolean8},
+    {"__bool16*", SimpleTypeKind::Boolean16},
+    {"__bool32*", SimpleTypeKind::Boolean32},
+    {"__bool64*", SimpleTypeKind::Boolean64},
+};
+
+static const EnumEntry<LeafType> LeafTypeNames[] = {
+#define LEAF_TYPE(name, val) LLVM_READOBJ_ENUM_ENT(LeafType, name),
+#include "CVLeafTypes.def"
+};
+
+static const EnumEntry<uint8_t> PtrKindNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, Near16),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, Far16),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, Huge16),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnSegment),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnValue),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnSegmentValue),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnAddress),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnSegmentAddress),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnType),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, BasedOnSelf),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, Near32),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, Far32),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerKind, Near64),
+};
+
+static const EnumEntry<uint8_t> PtrModeNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerMode, Pointer),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerMode, LValueReference),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerMode, PointerToDataMember),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerMode, PointerToMemberFunction),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerMode, RValueReference),
+};
+
+static const EnumEntry<uint16_t> PtrMemberRepNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation, Unknown),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation,
+                                SingleInheritanceData),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation,
+                                MultipleInheritanceData),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation,
+                                VirtualInheritanceData),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation, GeneralData),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation,
+                                SingleInheritanceFunction),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation,
+                                MultipleInheritanceFunction),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation,
+                                VirtualInheritanceFunction),
+    LLVM_READOBJ_ENUM_CLASS_ENT(PointerToMemberRepresentation, GeneralFunction),
+};
+
+static const EnumEntry<uint16_t> TypeModifierNames[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(ModifierOptions, Const),
+    LLVM_READOBJ_ENUM_CLASS_ENT(ModifierOptions, Volatile),
+    LLVM_READOBJ_ENUM_CLASS_ENT(ModifierOptions, Unaligned),
+};
+
+static const EnumEntry<uint8_t> CallingConventions[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, NearC),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, FarC),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, NearPascal),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, FarPascal),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, NearFast),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, FarFast),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, NearStdCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, FarStdCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, NearSysCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, FarSysCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, ThisCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, MipsCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, Generic),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, AlphaCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, PpcCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, SHCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, ArmCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, AM33Call),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, TriCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, SH5Call),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, M32RCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, ClrCall),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, Inline),
+    LLVM_READOBJ_ENUM_CLASS_ENT(CallingConvention, NearVector),
+};
+
+static const EnumEntry<uint8_t> FunctionOptionEnum[] = {
+    LLVM_READOBJ_ENUM_CLASS_ENT(FunctionOptions, CxxReturnUdt),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FunctionOptions, Constructor),
+    LLVM_READOBJ_ENUM_CLASS_ENT(FunctionOptions, ConstructorWithVirtualBases),
+};
+
 template <typename T>
 static std::error_code getSymbolAuxData(const COFFObjectFile *Obj,
                                         COFFSymbolRef Symbol,
@@ -476,110 +839,165 @@
 void COFFDumper::printBaseOfDataField(const pe32plus_header *) {}
 
 void COFFDumper::printCodeViewDebugInfo() {
+  // Print types first to build CVUDTNames, then print symbols.
   for (const SectionRef &S : Obj->sections()) {
-    StringRef SecName;
-    error(S.getName(SecName));
-    if (SecName == ".debug$S")
-      printCodeViewSection(S);
+    StringRef SectionName;
+    error(S.getName(SectionName));
+    if (SectionName == ".debug$T")
+      printCodeViewTypeSection(SectionName, S);
+  }
+  for (const SectionRef &S : Obj->sections()) {
+    StringRef SectionName;
+    error(S.getName(SectionName));
+    if (SectionName == ".debug$S")
+      printCodeViewSymbolSection(SectionName, S);
   }
 }
 
-void COFFDumper::printCodeViewSection(const SectionRef &Section) {
-  StringRef Data;
-  error(Section.getContents(Data));
+/// 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>
+static std::error_code consumeObject(StringRef &Data, const T *&Res) {
+  if (Data.size() < sizeof(*Res))
+    return object_error::parse_failed;
+  Res = reinterpret_cast<const T *>(Data.data());
+  Data = Data.drop_front(sizeof(*Res));
+  return std::error_code();
+}
+
+static std::error_code consumeUInt32(StringRef &Data, uint32_t &Res) {
+  const ulittle32_t *IntPtr;
+  if (auto EC = consumeObject(Data, IntPtr))
+    return EC;
+  Res = *IntPtr;
+  return std::error_code();
+}
+
+void COFFDumper::printCodeViewSymbolSection(StringRef SectionName,
+                                            const SectionRef &Section) {
+  StringRef SectionContents;
+  error(Section.getContents(SectionContents));
+  StringRef Data = SectionContents;
 
   SmallVector<StringRef, 10> FunctionNames;
   StringMap<StringRef> FunctionLineTables;
+  std::map<StringRef, const FrameData *> FunctionFrameData;
 
   ListScope D(W, "CodeViewDebugInfo");
-  {
-    // FIXME: Add more offset correctness checks.
-    DataExtractor DE(Data, true, 4);
-    uint32_t Offset = 0,
-             Magic = DE.getU32(&Offset);
-    W.printHex("Magic", Magic);
-    if (Magic != COFF::DEBUG_SECTION_MAGIC) {
-      error(object_error::parse_failed);
-      return;
-    }
+  // Print the section to allow correlation with printSections.
+  W.printNumber("Section", SectionName, Obj->getSectionID(Section));
 
-    bool Finished = false;
-    while (DE.isValidOffset(Offset) && !Finished) {
-      // The section consists of a number of subsection in the following format:
-      // |Type|PayloadSize|Payload...|
-      uint32_t SubSectionType = DE.getU32(&Offset),
-               PayloadSize = DE.getU32(&Offset);
-      ListScope S(W, "Subsection");
-      W.printHex("Type", SubSectionType);
-      W.printHex("PayloadSize", PayloadSize);
-      if (PayloadSize > Data.size() - Offset) {
+  uint32_t Magic;
+  error(consumeUInt32(Data, Magic));
+  W.printHex("Magic", Magic);
+  if (Magic != COFF::DEBUG_SECTION_MAGIC)
+    return error(object_error::parse_failed);
+
+  while (!Data.empty()) {
+    // The section consists of a number of subsection in the following format:
+    // |SubSectionType|SubSectionSize|Contents...|
+    uint32_t SubType, SubSectionSize;
+    error(consumeUInt32(Data, SubType));
+    error(consumeUInt32(Data, SubSectionSize));
+
+    ListScope S(W, "Subsection");
+    W.printEnum("SubSectionType", SubType, makeArrayRef(SubSectionTypes));
+    W.printHex("SubSectionSize", SubSectionSize);
+
+    // Get the contents of the subsection.
+    if (SubSectionSize > Data.size())
+      return error(object_error::parse_failed);
+    StringRef Contents = Data.substr(0, SubSectionSize);
+
+    // Add SubSectionSize to the current offset and align that offset to find
+    // the next subsection.
+    size_t SectionOffset = Data.data() - SectionContents.data();
+    size_t NextOffset = SectionOffset + SubSectionSize;
+    NextOffset = RoundUpToAlignment(NextOffset, 4);
+    Data = SectionContents.drop_front(NextOffset);
+
+    // Optionally print the subsection bytes in case our parsing gets confused
+    // later.
+    if (opts::CodeViewSubsectionBytes)
+      W.printBinaryBlock("SubSectionContents", Contents);
+
+    switch (ModuleSubstreamKind(SubType)) {
+    case ModuleSubstreamKind::Symbols:
+      printCodeViewSymbolsSubsection(Contents, Section, SectionOffset);
+      break;
+    case ModuleSubstreamKind::Lines: {
+      // Holds a PC to file:line table.  Some data to parse this subsection is
+      // stored in the other subsections, so just check sanity and store the
+      // pointers for deferred processing.
+
+      if (SubSectionSize < 12) {
+        // There should be at least three words to store two function
+        // relocations and size of the code.
         error(object_error::parse_failed);
         return;
       }
 
-      StringRef Contents = Data.substr(Offset, PayloadSize);
-      if (opts::CodeViewSubsectionBytes) {
-        // Print the raw contents to simplify debugging if anything goes wrong
-        // afterwards.
-        W.printBinaryBlock("Contents", Contents);
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section), SectionOffset,
+                              LinkageName));
+      W.printString("LinkageName", LinkageName);
+      if (FunctionLineTables.count(LinkageName) != 0) {
+        // Saw debug info for this function already?
+        error(object_error::parse_failed);
+        return;
       }
 
-      switch (SubSectionType) {
-      case COFF::DEBUG_SYMBOL_SUBSECTION:
-        printCodeViewSymbolsSubsection(Contents, Section, Offset);
-        break;
-      case COFF::DEBUG_LINE_TABLE_SUBSECTION: {
-        // Holds a PC to file:line table.  Some data to parse this subsection is
-        // stored in the other subsections, so just check sanity and store the
-        // pointers for deferred processing.
-
-        if (PayloadSize < 12) {
-          // There should be at least three words to store two function
-          // relocations and size of the code.
-          error(object_error::parse_failed);
-          return;
-        }
-
-        StringRef LinkageName;
-        error(resolveSymbolName(Obj->getCOFFSection(Section), Offset,
-                                LinkageName));
-        W.printString("LinkageName", LinkageName);
-        if (FunctionLineTables.count(LinkageName) != 0) {
-          // Saw debug info for this function already?
-          error(object_error::parse_failed);
-          return;
-        }
-
-        FunctionLineTables[LinkageName] = Contents;
-        FunctionNames.push_back(LinkageName);
-        break;
+      FunctionLineTables[LinkageName] = Contents;
+      FunctionNames.push_back(LinkageName);
+      break;
+    }
+    case ModuleSubstreamKind::StringTable:
+      if (SubSectionSize == 0 || CVStringTable.data() != nullptr ||
+          Contents.back() != '\0') {
+        // Empty or duplicate or non-null-terminated subsection.
+        error(object_error::parse_failed);
+        return;
       }
-      case COFF::DEBUG_STRING_TABLE_SUBSECTION:
-        if (PayloadSize == 0 || CVStringTable.data() != nullptr ||
-            Contents.back() != '\0') {
-          // Empty or duplicate or non-null-terminated subsection.
-          error(object_error::parse_failed);
-          return;
-        }
-        CVStringTable = Contents;
-        break;
-      case COFF::DEBUG_INDEX_SUBSECTION:
-        // Holds the translation table from file indices
-        // to offsets in the string table.
+      CVStringTable = Contents;
+      break;
+    case ModuleSubstreamKind::FileChecksums:
+      // Holds the translation table from file indices
+      // to offsets in the string table.
 
-        if (PayloadSize == 0 ||
-            CVFileIndexToStringOffsetTable.data() != nullptr) {
-          // Empty or duplicate subsection.
-          error(object_error::parse_failed);
-          return;
-        }
-        CVFileIndexToStringOffsetTable = Contents;
-        break;
+      if (SubSectionSize == 0 ||
+          CVFileIndexToStringOffsetTable.data() != nullptr) {
+        // Empty or duplicate subsection.
+        error(object_error::parse_failed);
+        return;
       }
-      Offset += PayloadSize;
+      CVFileIndexToStringOffsetTable = Contents;
+      break;
+    case ModuleSubstreamKind::FrameData: {
+      const size_t RelocationSize = 4;
+      if (SubSectionSize != sizeof(FrameData) + RelocationSize) {
+        // There should be exactly one relocation followed by the FrameData
+        // contents.
+        error(object_error::parse_failed);
+        return;
+      }
 
-      // Align the reading pointer by 4.
-      Offset += (-Offset) % 4;
+      const auto *FD = reinterpret_cast<const FrameData *>(
+          Contents.drop_front(RelocationSize).data());
+
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section), SectionOffset,
+                              LinkageName));
+      if (!FunctionFrameData.emplace(LinkageName, FD).second) {
+        error(object_error::parse_failed);
+        return;
+      }
+      break;
+    }
+
+    // Do nothing for unrecognized subsections.
+    default:
+      break;
     }
   }
 
@@ -674,89 +1092,1099 @@
       }
     }
   }
+
+  for (auto FrameDataPair : FunctionFrameData) {
+    StringRef LinkageName = FrameDataPair.first;
+    const FrameData *FD = FrameDataPair.second;
+    ListScope S(W, "FunctionFrameData");
+    W.printString("LinkageName", LinkageName);
+    W.printHex("RvaStart", FD->RvaStart);
+    W.printHex("CodeSize", FD->CodeSize);
+    W.printHex("LocalSize", FD->LocalSize);
+    W.printHex("ParamsSize", FD->ParamsSize);
+    W.printHex("MaxStackSize", FD->MaxStackSize);
+    W.printString("FrameFunc", StringRef(CVStringTable.data() + FD->FrameFunc));
+    W.printHex("PrologSize", FD->PrologSize);
+    W.printHex("SavedRegsSize", FD->SavedRegsSize);
+    W.printFlags("Flags", FD->Flags, makeArrayRef(FrameDataFlags));
+  }
+}
+
+static std::error_code decodeNumerictLeaf(StringRef &Data, APSInt &Num) {
+  // Used to avoid overload ambiguity on APInt construtor.
+  bool FalseVal = false;
+  if (Data.size() < 2)
+    return object_error::parse_failed;
+  uint16_t Short = *reinterpret_cast<const ulittle16_t *>(Data.data());
+  Data = Data.drop_front(2);
+  if (Short < LF_NUMERIC) {
+    Num = APSInt(APInt(/*numBits=*/16, Short, /*isSigned=*/false),
+                 /*isUnsigned=*/true);
+    return std::error_code();
+  }
+  switch (Short) {
+  case LF_CHAR:
+    Num = APSInt(APInt(/*numBits=*/8,
+                       *reinterpret_cast<const int8_t *>(Data.data()),
+                       /*isSigned=*/true),
+                 /*isUnsigned=*/false);
+    Data = Data.drop_front(1);
+    return std::error_code();
+  case LF_SHORT:
+    Num = APSInt(APInt(/*numBits=*/16,
+                       *reinterpret_cast<const little16_t *>(Data.data()),
+                       /*isSigned=*/true),
+                 /*isUnsigned=*/false);
+    Data = Data.drop_front(2);
+    return std::error_code();
+  case LF_USHORT:
+    Num = APSInt(APInt(/*numBits=*/16,
+                       *reinterpret_cast<const ulittle16_t *>(Data.data()),
+                       /*isSigned=*/false),
+                 /*isUnsigned=*/true);
+    Data = Data.drop_front(2);
+    return std::error_code();
+  case LF_LONG:
+    Num = APSInt(APInt(/*numBits=*/32,
+                       *reinterpret_cast<const little32_t *>(Data.data()),
+                       /*isSigned=*/true),
+                 /*isUnsigned=*/false);
+    Data = Data.drop_front(4);
+    return std::error_code();
+  case LF_ULONG:
+    Num = APSInt(APInt(/*numBits=*/32,
+                       *reinterpret_cast<const ulittle32_t *>(Data.data()),
+                       /*isSigned=*/FalseVal),
+                 /*isUnsigned=*/true);
+    Data = Data.drop_front(4);
+    return std::error_code();
+  case LF_QUADWORD:
+    Num = APSInt(APInt(/*numBits=*/64,
+                       *reinterpret_cast<const little64_t *>(Data.data()),
+                       /*isSigned=*/true),
+                 /*isUnsigned=*/false);
+    Data = Data.drop_front(8);
+    return std::error_code();
+  case LF_UQUADWORD:
+    Num = APSInt(APInt(/*numBits=*/64,
+                       *reinterpret_cast<const ulittle64_t *>(Data.data()),
+                       /*isSigned=*/false),
+                 /*isUnsigned=*/true);
+    Data = Data.drop_front(8);
+    return std::error_code();
+  }
+  return object_error::parse_failed;
+}
+
+/// Decode an unsigned integer numeric leaf value.
+std::error_code decodeUIntLeaf(StringRef &Data, uint64_t &Num) {
+  APSInt N;
+  if (std::error_code err = decodeNumerictLeaf(Data, N))
+    return err;
+  if (N.isSigned() || !N.isIntN(64))
+    return object_error::parse_failed;
+  Num = N.getLimitedValue();
+  return std::error_code();
 }
 
 void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
                                                 const SectionRef &Section,
                                                 uint32_t OffsetInSection) {
-  if (Subsection.size() == 0) {
-    error(object_error::parse_failed);
-    return;
-  }
-  DataExtractor DE(Subsection, true, 4);
-  uint32_t Offset = 0;
+  if (Subsection.size() < sizeof(SymRecord))
+    return error(object_error::parse_failed);
 
-  // Function-level subsections have "procedure start" and "procedure end"
-  // commands that should come in pairs and surround relevant info.
+  // This holds the remaining data to parse.
+  StringRef Data = Subsection;
+
   bool InFunctionScope = false;
-  while (DE.isValidOffset(Offset)) {
-    // Read subsection segments one by one.
-    uint16_t Size = DE.getU16(&Offset);
-    // The section size includes the size of the type identifier.
-    if (Size < 2 || !DE.isValidOffsetForDataOfSize(Offset, Size)) {
-      error(object_error::parse_failed);
-      return;
-    }
-    Size -= 2;
-    uint16_t Type = DE.getU16(&Offset);
+  while (!Data.empty()) {
+    const SymRecord *Rec;
+    error(consumeObject(Data, Rec));
+
+    StringRef SymData = Data.substr(0, Rec->RecordLength - 2);
+
+    Data = Data.drop_front(Rec->RecordLength - 2);
+
+    SymType Type = static_cast<SymType>(uint16_t(Rec->RecordType));
     switch (Type) {
-    case COFF::DEBUG_SYMBOL_TYPE_PROC_START: {
+    case S_LPROC32:
+    case S_GPROC32:
+    case S_GPROC32_ID:
+    case S_LPROC32_ID:
+    case S_LPROC32_DPC:
+    case S_LPROC32_DPC_ID: {
       DictScope S(W, "ProcStart");
-      if (InFunctionScope || Size < 36) {
-        error(object_error::parse_failed);
-        return;
-      }
+      const ProcSym *Proc;
+      error(consumeObject(SymData, Proc));
+      if (InFunctionScope)
+        return error(object_error::parse_failed);
       InFunctionScope = true;
 
-      // We're currently interested in a limited subset of fields in this
-      // segment, just ignore the rest of the fields for now.
-      uint8_t Unused[12];
-      DE.getU8(&Offset, Unused, 12);
-      uint32_t CodeSize = DE.getU32(&Offset);
-      DE.getU8(&Offset, Unused, 12);
-      StringRef SectionName;
+      // In a COFF object file, the CodeOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfCodeOffset =
+          reinterpret_cast<const char *>(&Proc->CodeOffset) - Subsection.data();
+      StringRef LinkageName;
       error(resolveSymbolName(Obj->getCOFFSection(Section),
-                              OffsetInSection + Offset, SectionName));
-      Offset += 4;
-      DE.getU8(&Offset, Unused, 3);
-      StringRef DisplayName = DE.getCStr(&Offset);
-      if (!DE.isValidOffset(Offset)) {
-        error(object_error::parse_failed);
-        return;
-      }
-      W.printString("DisplayName", DisplayName);
-      W.printString("Section", SectionName);
-      W.printHex("CodeSize", CodeSize);
+                              OffsetInSection + SecOffsetOfCodeOffset,
+                              LinkageName));
 
+      StringRef DisplayName = SymData.split('\0').first;
+      W.printHex("PtrParent", Proc->PtrParent);
+      W.printHex("PtrEnd", Proc->PtrEnd);
+      W.printHex("PtrNext", Proc->PtrNext);
+      W.printHex("CodeSize", Proc->CodeSize);
+      W.printHex("DbgStart", Proc->DbgStart);
+      W.printHex("DbgEnd", Proc->DbgEnd);
+      printTypeIndex("FunctionType", Proc->FunctionType);
+      W.printHex("CodeOffset", Proc->CodeOffset);
+      W.printHex("Segment", Proc->Segment);
+      W.printFlags("Flags", Proc->Flags, makeArrayRef(ProcSymFlags));
+      W.printString("DisplayName", DisplayName);
+      W.printString("LinkageName", LinkageName);
       break;
     }
-    case COFF::DEBUG_SYMBOL_TYPE_PROC_END: {
+
+    case S_PROC_ID_END: {
       W.startLine() << "ProcEnd\n";
-      if (!InFunctionScope || Size > 0) {
-        error(object_error::parse_failed);
-        return;
-      }
       InFunctionScope = false;
       break;
     }
-    default: {
-      if (opts::CodeViewSubsectionBytes) {
-        ListScope S(W, "Record");
-        W.printHex("Size", Size);
-        W.printHex("Type", Type);
 
-        StringRef Contents = DE.getData().substr(Offset, Size);
-        W.printBinaryBlock("Contents", Contents);
+    case S_BLOCK32: {
+      DictScope S(W, "BlockStart");
+      const BlockSym *Block;
+      error(consumeObject(SymData, Block));
+
+      // In a COFF object file, the CodeOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfCodeOffset =
+          reinterpret_cast<const char *>(&Block->CodeOffset) - Subsection.data();
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section),
+                              OffsetInSection + SecOffsetOfCodeOffset,
+                              LinkageName));
+
+      StringRef BlockName = SymData.split('\0').first;
+      W.printHex("PtrParent", Block->PtrParent);
+      W.printHex("PtrEnd", Block->PtrEnd);
+      W.printHex("CodeSize", Block->CodeSize);
+      W.printHex("CodeOffset", Block->CodeOffset);
+      W.printHex("Segment", Block->Segment);
+      W.printString("BlockName", BlockName);
+      W.printString("LinkageName", LinkageName);
+      break;
+    }
+
+    case S_END: {
+      W.startLine() << "BlockEnd\n";
+      InFunctionScope = false;
+      break;
+    }
+
+    case S_LABEL32: {
+      DictScope S(W, "Label");
+      const LabelSym *Label;
+      error(consumeObject(SymData, Label));
+
+      // In a COFF object file, the CodeOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfCodeOffset =
+          reinterpret_cast<const char *>(&Label->CodeOffset) - Subsection.data();
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section),
+                              OffsetInSection + SecOffsetOfCodeOffset,
+                              LinkageName));
+
+      StringRef DisplayName = SymData.split('\0').first;
+      W.printHex("CodeOffset", Label->CodeOffset);
+      W.printHex("Segment", Label->Segment);
+      W.printHex("Flags", Label->Flags);
+      W.printFlags("Flags", Label->Flags, makeArrayRef(ProcSymFlags));
+      W.printString("DisplayName", DisplayName);
+      W.printString("LinkageName", LinkageName);
+      break;
+    }
+
+    case S_INLINESITE: {
+      DictScope S(W, "InlineSite");
+      const InlineSiteSym *InlineSite;
+      error(consumeObject(SymData, InlineSite));
+      W.printHex("PtrParent", InlineSite->PtrParent);
+      W.printHex("PtrEnd", InlineSite->PtrEnd);
+      printTypeIndex("Inlinee", InlineSite->Inlinee);
+      W.printBinaryBlock("BinaryAnnotations", SymData);
+      break;
+    }
+
+    case S_INLINESITE_END: {
+      DictScope S(W, "InlineSiteEnd");
+      break;
+    }
+
+    case S_LOCAL: {
+      DictScope S(W, "Local");
+      const LocalSym *Local;
+      error(consumeObject(SymData, Local));
+      printTypeIndex("Type", Local->Type);
+      W.printFlags("Flags", uint16_t(Local->Flags), makeArrayRef(LocalFlags));
+      StringRef VarName = SymData.split('\0').first;
+      W.printString("VarName", VarName);
+      break;
+    }
+
+    case S_CALLSITEINFO: {
+      DictScope S(W, "CallSiteInfo");
+      const CallSiteInfoSym *CallSiteInfo;
+      error(consumeObject(SymData, CallSiteInfo));
+
+      // In a COFF object file, the CodeOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfCodeOffset =
+          reinterpret_cast<const char *>(&CallSiteInfo->CodeOffset) - Subsection.data();
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section),
+                              OffsetInSection + SecOffsetOfCodeOffset,
+                              LinkageName));
+      W.printHex("CodeOffset", CallSiteInfo->CodeOffset);
+      W.printHex("Segment", CallSiteInfo->Segment);
+      W.printHex("Reserved", CallSiteInfo->Reserved);
+      printTypeIndex("Type", CallSiteInfo->Type);
+      W.printString("LinkageName", LinkageName);
+      break;
+    }
+
+    case S_HEAPALLOCSITE: {
+      DictScope S(W, "HeapAllocationSite");
+      const HeapAllocationSiteSym *HeapAllocationSite;
+      error(consumeObject(SymData, HeapAllocationSite));
+
+      // In a COFF object file, the CodeOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfCodeOffset =
+          reinterpret_cast<const char *>(&HeapAllocationSite->CodeOffset) -
+          Subsection.data();
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section),
+                              OffsetInSection + SecOffsetOfCodeOffset,
+                              LinkageName));
+      W.printHex("CodeOffset", HeapAllocationSite->CodeOffset);
+      W.printHex("Segment", HeapAllocationSite->Segment);
+      W.printHex("CallInstructionSize",
+                 HeapAllocationSite->CallInstructionSize);
+      printTypeIndex("Type", HeapAllocationSite->Type);
+      W.printString("LinkageName", LinkageName);
+      break;
+    }
+
+    case S_FRAMECOOKIE: {
+      DictScope S(W, "FrameCookie");
+      const FrameCookieSym *FrameCookie;
+      error(consumeObject(SymData, FrameCookie));
+      W.printHex("CodeOffset", FrameCookie->CodeOffset);
+      W.printHex("Register", FrameCookie->Register);
+      W.printEnum("CookieKind", uint16_t(FrameCookie->CookieKind),
+                  makeArrayRef(FrameCookieKinds));
+      break;
+    }
+
+    case S_LDATA32:
+    case S_GDATA32:
+    case S_LMANDATA:
+    case S_GMANDATA: {
+      DictScope S(W, "DataSym");
+      const DataSym *Data;
+      error(consumeObject(SymData, Data));
+
+      // In a COFF object file, the DataOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfDataOffset =
+          reinterpret_cast<const char *>(&Data->DataOffset) - Subsection.data();
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section),
+                              OffsetInSection + SecOffsetOfDataOffset,
+                              LinkageName));
+      StringRef DisplayName = SymData.split('\0').first;
+      W.printHex("DataOffset", Data->DataOffset);
+      printTypeIndex("Type", Data->Type);
+      W.printString("DisplayName", DisplayName);
+      W.printString("LinkageName", LinkageName);
+      break;
+    }
+    case S_LTHREAD32:
+    case S_GTHREAD32: {
+      DictScope S(W, "ThreadLocalDataSym");
+      const DataSym *Data;
+      error(consumeObject(SymData, Data));
+
+      // In a COFF object file, the DataOffset field is typically zero and has a
+      // relocation applied to it. Go and look up the symbol for that
+      // relocation.
+      ptrdiff_t SecOffsetOfDataOffset =
+          reinterpret_cast<const char *>(&Data->DataOffset) - Subsection.data();
+      StringRef LinkageName;
+      error(resolveSymbolName(Obj->getCOFFSection(Section),
+                              OffsetInSection + SecOffsetOfDataOffset,
+                              LinkageName));
+      StringRef DisplayName = SymData.split('\0').first;
+      W.printHex("DataOffset", Data->DataOffset);
+      printTypeIndex("Type", Data->Type);
+      W.printString("DisplayName", DisplayName);
+      W.printString("LinkageName", LinkageName);
+      break;
+    }
+
+    case S_OBJNAME: {
+      DictScope S(W, "ObjectName");
+      const ObjNameSym *ObjName;
+      error(consumeObject(SymData, ObjName));
+      W.printHex("Signature", ObjName->Signature);
+      StringRef ObjectName = SymData.split('\0').first;
+      W.printString("ObjectName", ObjectName);
+      break;
+    }
+
+    case S_COMPILE3: {
+      DictScope S(W, "CompilerFlags");
+      const CompileSym3 *CompFlags;
+      error(consumeObject(SymData, CompFlags));
+      W.printEnum("Language", CompFlags->getLanguage(),
+                  makeArrayRef(SourceLanguages));
+      W.printFlags("Flags", CompFlags->flags & ~0xff,
+                   makeArrayRef(CompileSym3Flags));
+      W.printEnum("Machine", unsigned(CompFlags->Machine),
+                  makeArrayRef(CPUTypeNames));
+      std::string FrontendVersion;
+      {
+        raw_string_ostream Out(FrontendVersion);
+        Out << CompFlags->VersionFrontendMajor << '.'
+            << CompFlags->VersionFrontendMinor << '.'
+            << CompFlags->VersionFrontendBuild << '.'
+            << CompFlags->VersionFrontendQFE;
       }
+      std::string BackendVersion;
+      {
+        raw_string_ostream Out(BackendVersion);
+        Out << CompFlags->VersionBackendMajor << '.'
+            << CompFlags->VersionBackendMinor << '.'
+            << CompFlags->VersionBackendBuild << '.'
+            << CompFlags->VersionBackendQFE;
+      }
+      W.printString("FrontendVersion", FrontendVersion);
+      W.printString("BackendVersion", BackendVersion);
+      StringRef VersionName = SymData.split('\0').first;
+      W.printString("VersionName", VersionName);
+      break;
+    }
 
-      Offset += Size;
+    case S_FRAMEPROC: {
+      DictScope S(W, "FrameProc");
+      const FrameProcSym *FrameProc;
+      error(consumeObject(SymData, FrameProc));
+      W.printHex("TotalFrameBytes", FrameProc->TotalFrameBytes);
+      W.printHex("PaddingFrameBytes", FrameProc->PaddingFrameBytes);
+      W.printHex("OffsetToPadding", FrameProc->OffsetToPadding);
+      W.printHex("BytesOfCalleeSavedRegisters", FrameProc->BytesOfCalleeSavedRegisters);
+      W.printHex("OffsetOfExceptionHandler", FrameProc->OffsetOfExceptionHandler);
+      W.printHex("SectionIdOfExceptionHandler", FrameProc->SectionIdOfExceptionHandler);
+      W.printFlags("Flags", FrameProc->Flags, makeArrayRef(FrameProcSymFlags));
+      break;
+    }
+
+    case S_UDT:
+    case S_COBOLUDT: {
+      DictScope S(W, "UDT");
+      const UDTSym *UDT;
+      error(consumeObject(SymData, UDT));
+      printTypeIndex("Type", UDT->Type);
+      StringRef UDTName = SymData.split('\0').first;
+      W.printString("UDTName", UDTName);
+      break;
+    }
+
+    case S_BPREL32: {
+      DictScope S(W, "BPRelativeSym");
+      const BPRelativeSym *BPRel;
+      error(consumeObject(SymData, BPRel));
+      W.printHex("Offset", BPRel->Offset);
+      printTypeIndex("Type", BPRel->Type);
+      StringRef VarName = SymData.split('\0').first;
+      W.printString("VarName", VarName);
+      break;
+    }
+
+    case S_REGREL32: {
+      DictScope S(W, "RegRelativeSym");
+      const RegRelativeSym *RegRel;
+      error(consumeObject(SymData, RegRel));
+      W.printHex("Offset", RegRel->Offset);
+      printTypeIndex("Type", RegRel->Type);
+      W.printHex("Register", RegRel->Register);
+      StringRef VarName = SymData.split('\0').first;
+      W.printString("VarName", VarName);
+      break;
+    }
+
+    case S_BUILDINFO: {
+      DictScope S(W, "BuildInfo");
+      const BuildInfoSym *BuildInfo;
+      error(consumeObject(SymData, BuildInfo));
+      W.printNumber("BuildId", BuildInfo->BuildId);
+      break;
+    }
+
+    case S_CONSTANT:
+    case S_MANCONSTANT: {
+      DictScope S(W, "Constant");
+      const ConstantSym *Constant;
+      error(consumeObject(SymData, Constant));
+      printTypeIndex("Type", Constant->Type);
+      APSInt Value;
+      error(decodeNumerictLeaf(SymData, Value));
+      W.printNumber("Value", Value);
+      StringRef Name = SymData.split('\0').first;
+      W.printString("Name", Name);
+      break;
+    }
+
+    default: {
+      DictScope S(W, "UnknownSym");
+      W.printHex("Type", unsigned(Type));
+      W.printHex("Size", Rec->RecordLength);
+      W.printBinaryBlock("SymData", SymData);
       break;
     }
     }
   }
+}
 
-  if (InFunctionScope)
-    error(object_error::parse_failed);
+StringRef getRemainingTypeBytes(const TypeRecord *Rec, const char *Start) {
+  ptrdiff_t StartOffset = Start - reinterpret_cast<const char *>(Rec);
+  size_t RecSize = Rec->Len + 2;
+  assert(StartOffset >= 0 && "negative start-offset!");
+  assert(static_cast<size_t>(StartOffset) <= RecSize &&
+         "Start beyond the end of Rec");
+  return StringRef(Start, RecSize - StartOffset);
+}
+
+StringRef getRemainingBytesAsString(const TypeRecord *Rec, const char *Start) {
+  StringRef Remaining = getRemainingTypeBytes(Rec, Start);
+  StringRef Leading, Trailing;
+  std::tie(Leading, Trailing) = Remaining.split('\0');
+  return Leading;
+}
+
+StringRef COFFDumper::getTypeName(TypeIndex TI) {
+  if (TI.isNoType())
+    return "<no type>";
+
+  if (TI.isSimple()) {
+    // This is a simple type.
+    for (const auto &SimpleTypeName : SimpleTypeNames) {
+      if (SimpleTypeName.Value == TI.getSimpleKind()) {
+        if (TI.getSimpleMode() == SimpleTypeMode::Direct)
+          return SimpleTypeName.Name.drop_back(1);
+        // Otherwise, this is a pointer type. We gloss over the distinction
+        // between near, far, 64, 32, etc, and just give a pointer type.
+        return SimpleTypeName.Name;
+      }
+    }
+    return "<unknown simple type>";
+  }
+
+  // User-defined type.
+  StringRef UDTName;
+  unsigned UDTIndex = TI.getIndex() - 0x1000;
+  if (UDTIndex < CVUDTNames.size())
+    return CVUDTNames[UDTIndex];
+
+  return "<unknown UDT>";
+}
+
+void COFFDumper::printTypeIndex(StringRef FieldName, TypeIndex TI) {
+  StringRef TypeName;
+  if (!TI.isNoType())
+    TypeName = getTypeName(TI);
+  if (!TypeName.empty())
+    W.printHex(FieldName, TypeName, TI.getIndex());
+  else
+    W.printHex(FieldName, TI.getIndex());
+}
+
+static StringRef getLeafTypeName(LeafType LT) {
+  switch (LT) {
+  case LF_STRING_ID: return "StringId";
+  case LF_FIELDLIST: return "FieldList";
+  case LF_ARGLIST:
+  case LF_SUBSTR_LIST: return "ArgList";
+  case LF_CLASS:
+  case LF_STRUCTURE:
+  case LF_INTERFACE: return "ClassType";
+  case LF_UNION: return "UnionType";
+  case LF_ENUM: return "EnumType";
+  case LF_ARRAY: return "ArrayType";
+  case LF_VFTABLE: return "VFTableType";
+  case LF_MFUNC_ID: return "MemberFuncId";
+  case LF_PROCEDURE: return "ProcedureType";
+  case LF_MFUNCTION: return "MemberFunctionType";
+  case LF_METHODLIST: return "MethodListEntry";
+  case LF_FUNC_ID: return "FuncId";
+  case LF_TYPESERVER2: return "TypeServer2";
+  case LF_POINTER: return "PointerType";
+  case LF_MODIFIER: return "TypeModifier";
+  case LF_VTSHAPE: return "VTableShape";
+  case LF_UDT_SRC_LINE: return "UDTSrcLine";
+  case LF_BUILDINFO: return "BuildInfo";
+  default: break;
+  }
+  return "UnknownLeaf";
+}
+
+void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
+                                          const SectionRef &Section) {
+  ListScope D(W, "CodeViewTypes");
+  W.printNumber("Section", SectionName, Obj->getSectionID(Section));
+  StringRef Data;
+  error(Section.getContents(Data));
+  W.printBinaryBlock("Data", Data);
+
+  unsigned Magic = *reinterpret_cast<const ulittle32_t *>(Data.data());
+  W.printHex("Magic", Magic);
+
+  Data = Data.drop_front(4);
+
+  while (!Data.empty()) {
+    const TypeRecord *Rec;
+    error(consumeObject(Data, Rec));
+    auto Leaf = static_cast<LeafType>(uint16_t(Rec->Leaf));
+
+    // This record is 'Len - 2' bytes, and the next one starts immediately
+    // afterwards.
+    StringRef LeafData = Data.substr(0, Rec->Len - 2);
+    StringRef RemainingData = Data.drop_front(LeafData.size());
+
+    // Find the name of this leaf type.
+    StringRef LeafName = getLeafTypeName(Leaf);
+    DictScope S(W, LeafName);
+    unsigned NextTypeIndex = 0x1000 + CVUDTNames.size();
+    W.printEnum("LeafType", unsigned(Leaf), makeArrayRef(LeafTypeNames));
+    W.printHex("TypeIndex", NextTypeIndex);
+
+    // Fill this in inside the switch to get something in CVUDTNames.
+    StringRef Name;
+
+    switch (Leaf) {
+    default: {
+      W.printHex("Size", Rec->Len);
+      if (opts::CodeViewSubsectionBytes)
+        W.printBinaryBlock("LeafData", LeafData);
+      break;
+    }
+
+    case LF_STRING_ID: {
+      const StringId *String;
+      error(consumeObject(LeafData, String));
+      W.printHex("Id", String->id.getIndex());
+      StringRef StringData = getRemainingBytesAsString(Rec, LeafData.data());
+      W.printString("StringData", StringData);
+      // Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE.
+      Name = StringData;
+      break;
+    }
+
+    case LF_FIELDLIST: {
+      W.printHex("Size", Rec->Len);
+      // FieldList has no fixed prefix that can be described with a struct. All
+      // the bytes must be interpreted as more records.
+      printCodeViewFieldList(LeafData);
+      break;
+    }
+
+    case LF_ARGLIST:
+    case LF_SUBSTR_LIST: {
+      const ArgList *Args;
+      error(consumeObject(LeafData, Args));
+      W.printNumber("NumArgs", Args->NumArgs);
+      ListScope Arguments(W, "Arguments");
+      SmallString<256> TypeName("(");
+      for (uint32_t ArgI = 0; ArgI != Args->NumArgs; ++ArgI) {
+        const TypeIndex *Type;
+        error(consumeObject(LeafData, Type));
+        printTypeIndex("ArgType", *Type);
+        StringRef ArgTypeName = getTypeName(*Type);
+        TypeName.append(ArgTypeName);
+        if (ArgI + 1 != Args->NumArgs)
+          TypeName.append(", ");
+      }
+      TypeName.push_back(')');
+      Name = TypeNames.insert(TypeName).first->getKey();
+      break;
+    }
+
+    case LF_CLASS:
+    case LF_STRUCTURE:
+    case LF_INTERFACE: {
+      const ClassType *Class;
+      error(consumeObject(LeafData, Class));
+      W.printNumber("MemberCount", Class->MemberCount);
+      uint16_t Props = Class->Properties;
+      W.printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
+      printTypeIndex("FieldList", Class->FieldList);
+      printTypeIndex("DerivedFrom", Class->DerivedFrom);
+      printTypeIndex("VShape", Class->VShape);
+      uint64_t SizeOf;
+      error(decodeUIntLeaf(LeafData, SizeOf));
+      W.printNumber("SizeOf", SizeOf);
+      StringRef LinkageName;
+      std::tie(Name, LinkageName) = LeafData.split('\0');
+      W.printString("Name", Name);
+      if (Props & uint16_t(ClassOptions::HasUniqueName)) {
+        LinkageName = getRemainingBytesAsString(Rec, LinkageName.data());
+        if (LinkageName.empty())
+          return error(object_error::parse_failed);
+        W.printString("LinkageName", LinkageName);
+      }
+      break;
+    }
+
+    case LF_UNION: {
+      const UnionType *Union;
+      error(consumeObject(LeafData, Union));
+      W.printNumber("MemberCount", Union->MemberCount);
+      uint16_t Props = Union->Properties;
+      W.printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
+      printTypeIndex("FieldList", Union->FieldList);
+      uint64_t SizeOf;
+      error(decodeUIntLeaf(LeafData, SizeOf));
+      W.printNumber("SizeOf", SizeOf);
+      StringRef LinkageName;
+      std::tie(Name, LinkageName) = LeafData.split('\0');
+      W.printString("Name", Name);
+      if (Props & uint16_t(ClassOptions::HasUniqueName)) {
+        LinkageName = getRemainingBytesAsString(Rec, LinkageName.data());
+        if (LinkageName.empty())
+          return error(object_error::parse_failed);
+        W.printString("LinkageName", LinkageName);
+      }
+      break;
+    }
+
+    case LF_ENUM: {
+      const EnumType *Enum;
+      error(consumeObject(LeafData, Enum));
+      W.printNumber("NumEnumerators", Enum->NumEnumerators);
+      W.printFlags("Properties", uint16_t(Enum->Properties),
+                   makeArrayRef(ClassOptionNames));
+      printTypeIndex("UnderlyingType", Enum->UnderlyingType);
+      printTypeIndex("FieldListType", Enum->FieldListType);
+      Name = LeafData.split('\0').first;
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_ARRAY: {
+      const ArrayType *AT;
+      error(consumeObject(LeafData, AT));
+      printTypeIndex("ElementType", AT->ElementType);
+      printTypeIndex("IndexType", AT->IndexType);
+      uint64_t SizeOf;
+      error(decodeUIntLeaf(LeafData, SizeOf));
+      W.printNumber("SizeOf", SizeOf);
+      Name = LeafData.split('\0').first;
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_VFTABLE: {
+      const VFTableType *VFT;
+      error(consumeObject(LeafData, VFT));
+      printTypeIndex("CompleteClass", VFT->CompleteClass);
+      printTypeIndex("OverriddenVFTable", VFT->OverriddenVFTable);
+      W.printHex("VFPtrOffset", VFT->VFPtrOffset);
+      StringRef NamesData = LeafData.substr(0, VFT->NamesLen);
+      std::tie(Name, NamesData) = NamesData.split('\0');
+      W.printString("VFTableName", Name);
+      while (!NamesData.empty()) {
+        StringRef MethodName;
+        std::tie(MethodName, NamesData) = NamesData.split('\0');
+        W.printString("MethodName", MethodName);
+      }
+      break;
+    }
+
+    case LF_MFUNC_ID: {
+      const MemberFuncId *Id;
+      error(consumeObject(LeafData, Id));
+      printTypeIndex("ClassType", Id->ClassType);
+      printTypeIndex("FunctionType", Id->FunctionType);
+      Name = LeafData.split('\0').first;
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_PROCEDURE: {
+      const ProcedureType *Proc;
+      error(consumeObject(LeafData, Proc));
+      printTypeIndex("ReturnType", Proc->ReturnType);
+      W.printEnum("CallingConvention", uint8_t(Proc->CallConv),
+                  makeArrayRef(CallingConventions));
+      W.printFlags("FunctionOptions", uint8_t(Proc->Options),
+                   makeArrayRef(FunctionOptionEnum));
+      W.printNumber("NumParameters", Proc->NumParameters);
+      printTypeIndex("ArgListType", Proc->ArgListType);
+
+      StringRef ReturnTypeName = getTypeName(Proc->ReturnType);
+      StringRef ArgListTypeName = getTypeName(Proc->ArgListType);
+      SmallString<256> TypeName(ReturnTypeName);
+      TypeName.push_back(' ');
+      TypeName.append(ArgListTypeName);
+      Name = TypeNames.insert(TypeName).first->getKey();
+      break;
+    }
+
+    case LF_MFUNCTION: {
+      const MemberFunctionType *MemberFunc;
+      error(consumeObject(LeafData, MemberFunc));
+      printTypeIndex("ReturnType", MemberFunc->ReturnType);
+      printTypeIndex("ClassType", MemberFunc->ClassType);
+      printTypeIndex("ThisType", MemberFunc->ThisType);
+      W.printEnum("CallingConvention", uint8_t(MemberFunc->CallConv),
+                  makeArrayRef(CallingConventions));
+      W.printFlags("FunctionOptions", uint8_t(MemberFunc->Options),
+                   makeArrayRef(FunctionOptionEnum));
+      W.printNumber("NumParameters", MemberFunc->NumParameters);
+      printTypeIndex("ArgListType", MemberFunc->ArgListType);
+      W.printNumber("ThisAdjustment", MemberFunc->ThisAdjustment);
+
+      StringRef ReturnTypeName = getTypeName(MemberFunc->ReturnType);
+      StringRef ClassTypeName = getTypeName(MemberFunc->ClassType);
+      StringRef ArgListTypeName = getTypeName(MemberFunc->ArgListType);
+      SmallString<256> TypeName(ReturnTypeName);
+      TypeName.push_back(' ');
+      TypeName.append(ClassTypeName);
+      TypeName.append("::");
+      TypeName.append(ArgListTypeName);
+      Name = TypeNames.insert(TypeName).first->getKey();
+      break;
+    }
+
+    case LF_METHODLIST: {
+      while (!LeafData.empty()) {
+        const MethodListEntry *Method;
+        error(consumeObject(LeafData, Method));
+        ListScope S(W, "Method");
+        printMemberAttributes(Method->Attrs);
+        printTypeIndex("Type", Method->Type);
+        if (Method->isIntroducedVirtual()) {
+          const little32_t *VFTOffsetPtr;
+          error(consumeObject(LeafData, VFTOffsetPtr));
+          W.printHex("VFTableOffset", *VFTOffsetPtr);
+        }
+      }
+      break;
+    }
+
+    case LF_FUNC_ID: {
+      const FuncId *Func;
+      error(consumeObject(LeafData, Func));
+      printTypeIndex("ParentScope", Func->ParentScope);
+      printTypeIndex("FunctionType", Func->FunctionType);
+      StringRef Name, Null;
+      std::tie(Name, Null) = LeafData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_TYPESERVER2: {
+      const TypeServer2 *TypeServer;
+      error(consumeObject(LeafData, TypeServer));
+      W.printBinary("Signature", StringRef(TypeServer->Signature, 16));
+      W.printNumber("Age", TypeServer->Age);
+      Name = LeafData.split('\0').first;
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_POINTER: {
+      const PointerType *Ptr;
+      error(consumeObject(LeafData, Ptr));
+      printTypeIndex("PointeeType", Ptr->PointeeType);
+      W.printHex("PointerAttributes", Ptr->Attrs);
+      W.printEnum("PtrType", unsigned(Ptr->getPtrKind()),
+                  makeArrayRef(PtrKindNames));
+      W.printEnum("PtrMode", unsigned(Ptr->getPtrMode()),
+                  makeArrayRef(PtrModeNames));
+      W.printNumber("IsFlat", Ptr->isFlat());
+      W.printNumber("IsConst", Ptr->isConst());
+      W.printNumber("IsVolatile", Ptr->isVolatile());
+      W.printNumber("IsUnaligned", Ptr->isUnaligned());
+
+      if (Ptr->isPointerToMember()) {
+        const PointerToMemberTail *PMT;
+        error(consumeObject(LeafData, PMT));
+        printTypeIndex("ClassType", PMT->ClassType);
+        W.printEnum("Representation", PMT->Representation,
+                    makeArrayRef(PtrMemberRepNames));
+
+        StringRef PointeeName = getTypeName(Ptr->PointeeType);
+        StringRef ClassName = getTypeName(PMT->ClassType);
+        SmallString<256> TypeName(PointeeName);
+        TypeName.push_back(' ');
+        TypeName.append(ClassName);
+        TypeName.append("::*");
+        Name = TypeNames.insert(TypeName).first->getKey();
+      } else {
+        W.printBinaryBlock("TailData", LeafData);
+
+        SmallString<256> TypeName;
+        if (Ptr->isConst())
+          TypeName.append("const ");
+        if (Ptr->isVolatile())
+          TypeName.append("volatile ");
+        if (Ptr->isUnaligned())
+          TypeName.append("__unaligned ");
+
+        TypeName.append(getTypeName(Ptr->PointeeType));
+
+        if (Ptr->getPtrMode() == PointerMode::LValueReference)
+          TypeName.append("&");
+        else if (Ptr->getPtrMode() == PointerMode::RValueReference)
+          TypeName.append("&&");
+        else if (Ptr->getPtrMode() == PointerMode::Pointer)
+          TypeName.append("*");
+
+        Name = TypeNames.insert(TypeName).first->getKey();
+      }
+      break;
+    }
+
+    case LF_MODIFIER: {
+      const TypeModifier *Mod;
+      error(consumeObject(LeafData, Mod));
+      printTypeIndex("ModifiedType", Mod->ModifiedType);
+      W.printFlags("Modifiers", Mod->Modifiers,
+                   makeArrayRef(TypeModifierNames));
+
+      StringRef ModifiedName = getTypeName(Mod->ModifiedType);
+      SmallString<256> TypeName;
+      if (Mod->Modifiers & uint16_t(ModifierOptions::Const))
+        TypeName.append("const ");
+      if (Mod->Modifiers & uint16_t(ModifierOptions::Volatile))
+        TypeName.append("volatile ");
+      if (Mod->Modifiers & uint16_t(ModifierOptions::Unaligned))
+        TypeName.append("__unaligned ");
+      TypeName.append(ModifiedName);
+      Name = TypeNames.insert(TypeName).first->getKey();
+      break;
+    }
+
+    case LF_VTSHAPE: {
+      const VTableShape *Shape;
+      error(consumeObject(LeafData, Shape));
+      unsigned VFEntryCount = Shape->VFEntryCount;
+      W.printNumber("VFEntryCount", VFEntryCount);
+      // We could print out whether the methods are near or far, but in practice
+      // today everything is CV_VTS_near32, so it's just noise.
+      break;
+    }
+
+    case LF_UDT_SRC_LINE: {
+      const UDTSrcLine *Line;
+      error(consumeObject(LeafData, Line));
+      printTypeIndex("UDT", Line->UDT);
+      printTypeIndex("SourceFile", Line->SourceFile);
+      W.printNumber("LineNumber", Line->LineNumber);
+      break;
+    }
+
+    case LF_BUILDINFO: {
+      const BuildInfo *Args;
+      error(consumeObject(LeafData, Args));
+      W.printNumber("NumArgs", Args->NumArgs);
+
+      ListScope Arguments(W, "Arguments");
+      for (uint32_t ArgI = 0; ArgI != Args->NumArgs; ++ArgI) {
+        const TypeIndex *Type;
+        error(consumeObject(LeafData, Type));
+        printTypeIndex("ArgType", *Type);
+      }
+      break;
+    }
+    }
+
+    CVUDTNames.push_back(Name);
+
+    Data = RemainingData;
+    // FIXME: The stream contains LF_PAD bytes that we need to ignore, but those
+    // are typically included in LeafData. We may need to call skipPadding() if
+    // we ever find a record that doesn't count those bytes.
+  }
+}
+
+static StringRef skipPadding(StringRef Data) {
+  if (Data.empty())
+    return Data;
+  uint8_t Leaf = Data.front();
+  if (Leaf < LF_PAD0)
+    return Data;
+  // Leaf is greater than 0xf0. We should advance by the number of bytes in the
+  // low 4 bits.
+  return Data.drop_front(Leaf & 0x0F);
+}
+
+void COFFDumper::printMemberAttributes(MemberAttributes Attrs) {
+  W.printEnum("AccessSpecifier", uint8_t(Attrs.getAccess()),
+              makeArrayRef(MemberAccessNames));
+  auto MK = Attrs.getMethodKind();
+  // Data members will be vanilla. Don't try to print a method kind for them.
+  if (MK != MethodKind::Vanilla)
+    W.printEnum("MethodKind", unsigned(MK), makeArrayRef(MemberKindNames));
+  if (Attrs.getFlags() != MethodOptions::None) {
+    W.printFlags("MethodOptions", unsigned(Attrs.getFlags()),
+                 makeArrayRef(MethodOptionNames));
+  }
+}
+
+void COFFDumper::printCodeViewFieldList(StringRef FieldData) {
+  while (!FieldData.empty()) {
+    const ulittle16_t *LeafPtr;
+    error(consumeObject(FieldData, LeafPtr));
+    uint16_t Leaf = *LeafPtr;
+    switch (Leaf) {
+    default:
+      W.printHex("UnknownMember", Leaf);
+      // We can't advance once we hit an unknown field. The size is not encoded.
+      return;
+
+    case LF_NESTTYPE: {
+      const NestedType *Nested;
+      error(consumeObject(FieldData, Nested));
+      DictScope S(W, "NestedType");
+      printTypeIndex("Type", Nested->Type);
+      StringRef Name;
+      std::tie(Name, FieldData) = FieldData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_ONEMETHOD: {
+      const OneMethod *Method;
+      error(consumeObject(FieldData, Method));
+      DictScope S(W, "OneMethod");
+      printMemberAttributes(Method->Attrs);
+      printTypeIndex("Type", Method->Type);
+      // If virtual, then read the vftable offset.
+      if (Method->isIntroducedVirtual()) {
+        const little32_t *VFTOffsetPtr;
+        error(consumeObject(FieldData, VFTOffsetPtr));
+        W.printHex("VFTableOffset", *VFTOffsetPtr);
+      }
+      StringRef Name;
+      std::tie(Name, FieldData) = FieldData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_METHOD: {
+      const OverloadedMethod *Method;
+      error(consumeObject(FieldData, Method));
+      DictScope S(W, "OverloadedMethod");
+      W.printHex("MethodCount", Method->MethodCount);
+      W.printHex("MethodListIndex", Method->MethList.getIndex());
+      StringRef Name;
+      std::tie(Name, FieldData) = FieldData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_MEMBER: {
+      const DataMember *Field;
+      error(consumeObject(FieldData, Field));
+      DictScope S(W, "DataMember");
+      printMemberAttributes(Field->Attrs);
+      printTypeIndex("Type", Field->Type);
+      uint64_t FieldOffset;
+      error(decodeUIntLeaf(FieldData, FieldOffset));
+      W.printHex("FieldOffset", FieldOffset);
+      StringRef Name;
+      std::tie(Name, FieldData) = FieldData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_STMEMBER: {
+      const StaticDataMember *Field;
+      error(consumeObject(FieldData, Field));
+      DictScope S(W, "StaticDataMember");
+      printMemberAttributes(Field->Attrs);
+      printTypeIndex("Type", Field->Type);
+      StringRef Name;
+      std::tie(Name, FieldData) = FieldData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_VFUNCTAB: {
+      const VirtualFunctionPointer *VFTable;
+      error(consumeObject(FieldData, VFTable));
+      DictScope S(W, "VirtualFunctionPointer");
+      printTypeIndex("Type", VFTable->Type);
+      break;
+    }
+
+    case LF_ENUMERATE: {
+      const Enumerator *Enum;
+      error(consumeObject(FieldData, Enum));
+      DictScope S(W, "Enumerator");
+      printMemberAttributes(Enum->Attrs);
+      APSInt EnumValue;
+      error(decodeNumerictLeaf(FieldData, EnumValue));
+      W.printNumber("EnumValue", EnumValue);
+      StringRef Name;
+      std::tie(Name, FieldData) = FieldData.split('\0');
+      W.printString("Name", Name);
+      break;
+    }
+
+    case LF_BCLASS:
+    case LF_BINTERFACE: {
+      const BaseClass *Base;
+      error(consumeObject(FieldData, Base));
+      DictScope S(W, "BaseClass");
+      printMemberAttributes(Base->Attrs);
+      printTypeIndex("BaseType", Base->BaseType);
+      uint64_t BaseOffset;
+      error(decodeUIntLeaf(FieldData, BaseOffset));
+      W.printHex("BaseOffset", BaseOffset);
+      break;
+    }
+
+    case LF_VBCLASS:
+    case LF_IVBCLASS: {
+      const VirtualBaseClass *Base;
+      error(consumeObject(FieldData, Base));
+      DictScope S(W, "VirtualBaseClass");
+      printMemberAttributes(Base->Attrs);
+      printTypeIndex("BaseType",  Base->BaseType);
+      printTypeIndex("VBPtrType", Base->VBPtrType);
+      uint64_t VBPtrOffset, VBTableIndex;
+      error(decodeUIntLeaf(FieldData, VBPtrOffset));
+      error(decodeUIntLeaf(FieldData, VBTableIndex));
+      W.printHex("VBPtrOffset", VBPtrOffset);
+      W.printHex("VBTableIndex", VBTableIndex);
+      break;
+    }
+    }
+
+    // Handle padding.
+    FieldData = skipPadding(FieldData);
+  }
 }
 
 void COFFDumper::printSections() {