1*0b57cec5SDimitry Andric //===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric 9*0b57cec5SDimitry Andric #include "ExplainOutputStyle.h" 10*0b57cec5SDimitry Andric 11*0b57cec5SDimitry Andric #include "FormatUtil.h" 12*0b57cec5SDimitry Andric #include "InputFile.h" 13*0b57cec5SDimitry Andric #include "StreamUtil.h" 14*0b57cec5SDimitry Andric #include "llvm-pdbutil.h" 15*0b57cec5SDimitry Andric 16*0b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/Formatters.h" 17*0b57cec5SDimitry Andric #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 18*0b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/DbiStream.h" 19*0b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/InfoStream.h" 20*0b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 21*0b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawTypes.h" 22*0b57cec5SDimitry Andric #include "llvm/Support/BinaryByteStream.h" 23*0b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamArray.h" 24*0b57cec5SDimitry Andric #include "llvm/Support/Error.h" 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric using namespace llvm; 27*0b57cec5SDimitry Andric using namespace llvm::codeview; 28*0b57cec5SDimitry Andric using namespace llvm::msf; 29*0b57cec5SDimitry Andric using namespace llvm::pdb; 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric ExplainOutputStyle::ExplainOutputStyle(InputFile &File, uint64_t FileOffset) 32*0b57cec5SDimitry Andric : File(File), FileOffset(FileOffset), P(2, false, outs()) {} 33*0b57cec5SDimitry Andric 34*0b57cec5SDimitry Andric Error ExplainOutputStyle::dump() { 35*0b57cec5SDimitry Andric P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset, 36*0b57cec5SDimitry Andric File.getFilePath()); 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric if (File.isPdb()) 39*0b57cec5SDimitry Andric return explainPdbFile(); 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric return explainBinaryFile(); 42*0b57cec5SDimitry Andric } 43*0b57cec5SDimitry Andric 44*0b57cec5SDimitry Andric Error ExplainOutputStyle::explainPdbFile() { 45*0b57cec5SDimitry Andric bool IsAllocated = explainPdbBlockStatus(); 46*0b57cec5SDimitry Andric if (!IsAllocated) 47*0b57cec5SDimitry Andric return Error::success(); 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric AutoIndent Indent(P); 50*0b57cec5SDimitry Andric if (isPdbSuperBlock()) 51*0b57cec5SDimitry Andric explainPdbSuperBlockOffset(); 52*0b57cec5SDimitry Andric else if (isPdbFpmBlock()) 53*0b57cec5SDimitry Andric explainPdbFpmBlockOffset(); 54*0b57cec5SDimitry Andric else if (isPdbBlockMapBlock()) 55*0b57cec5SDimitry Andric explainPdbBlockMapOffset(); 56*0b57cec5SDimitry Andric else if (isPdbStreamDirectoryBlock()) 57*0b57cec5SDimitry Andric explainPdbStreamDirectoryOffset(); 58*0b57cec5SDimitry Andric else if (auto Index = getPdbBlockStreamIndex()) 59*0b57cec5SDimitry Andric explainPdbStreamOffset(*Index); 60*0b57cec5SDimitry Andric else 61*0b57cec5SDimitry Andric explainPdbUnknownBlock(); 62*0b57cec5SDimitry Andric return Error::success(); 63*0b57cec5SDimitry Andric } 64*0b57cec5SDimitry Andric 65*0b57cec5SDimitry Andric Error ExplainOutputStyle::explainBinaryFile() { 66*0b57cec5SDimitry Andric std::unique_ptr<BinaryByteStream> Stream = 67*0b57cec5SDimitry Andric llvm::make_unique<BinaryByteStream>(File.unknown().getBuffer(), 68*0b57cec5SDimitry Andric llvm::support::little); 69*0b57cec5SDimitry Andric switch (opts::explain::InputType) { 70*0b57cec5SDimitry Andric case opts::explain::InputFileType::DBIStream: { 71*0b57cec5SDimitry Andric DbiStream Dbi(std::move(Stream)); 72*0b57cec5SDimitry Andric if (auto EC = Dbi.reload(nullptr)) 73*0b57cec5SDimitry Andric return EC; 74*0b57cec5SDimitry Andric explainStreamOffset(Dbi, FileOffset); 75*0b57cec5SDimitry Andric break; 76*0b57cec5SDimitry Andric } 77*0b57cec5SDimitry Andric case opts::explain::InputFileType::PDBStream: { 78*0b57cec5SDimitry Andric InfoStream Info(std::move(Stream)); 79*0b57cec5SDimitry Andric if (auto EC = Info.reload()) 80*0b57cec5SDimitry Andric return EC; 81*0b57cec5SDimitry Andric explainStreamOffset(Info, FileOffset); 82*0b57cec5SDimitry Andric break; 83*0b57cec5SDimitry Andric } 84*0b57cec5SDimitry Andric default: 85*0b57cec5SDimitry Andric llvm_unreachable("Invalid input file type!"); 86*0b57cec5SDimitry Andric } 87*0b57cec5SDimitry Andric return Error::success(); 88*0b57cec5SDimitry Andric } 89*0b57cec5SDimitry Andric 90*0b57cec5SDimitry Andric uint32_t ExplainOutputStyle::pdbBlockIndex() const { 91*0b57cec5SDimitry Andric return FileOffset / File.pdb().getBlockSize(); 92*0b57cec5SDimitry Andric } 93*0b57cec5SDimitry Andric 94*0b57cec5SDimitry Andric uint32_t ExplainOutputStyle::pdbBlockOffset() const { 95*0b57cec5SDimitry Andric uint64_t BlockStart = pdbBlockIndex() * File.pdb().getBlockSize(); 96*0b57cec5SDimitry Andric assert(FileOffset >= BlockStart); 97*0b57cec5SDimitry Andric return FileOffset - BlockStart; 98*0b57cec5SDimitry Andric } 99*0b57cec5SDimitry Andric 100*0b57cec5SDimitry Andric bool ExplainOutputStyle::isPdbSuperBlock() const { 101*0b57cec5SDimitry Andric return pdbBlockIndex() == 0; 102*0b57cec5SDimitry Andric } 103*0b57cec5SDimitry Andric 104*0b57cec5SDimitry Andric bool ExplainOutputStyle::isPdbFpm1() const { 105*0b57cec5SDimitry Andric return ((pdbBlockIndex() - 1) % File.pdb().getBlockSize() == 0); 106*0b57cec5SDimitry Andric } 107*0b57cec5SDimitry Andric bool ExplainOutputStyle::isPdbFpm2() const { 108*0b57cec5SDimitry Andric return ((pdbBlockIndex() - 2) % File.pdb().getBlockSize() == 0); 109*0b57cec5SDimitry Andric } 110*0b57cec5SDimitry Andric 111*0b57cec5SDimitry Andric bool ExplainOutputStyle::isPdbFpmBlock() const { 112*0b57cec5SDimitry Andric return isPdbFpm1() || isPdbFpm2(); 113*0b57cec5SDimitry Andric } 114*0b57cec5SDimitry Andric 115*0b57cec5SDimitry Andric bool ExplainOutputStyle::isPdbBlockMapBlock() const { 116*0b57cec5SDimitry Andric return pdbBlockIndex() == File.pdb().getBlockMapIndex(); 117*0b57cec5SDimitry Andric } 118*0b57cec5SDimitry Andric 119*0b57cec5SDimitry Andric bool ExplainOutputStyle::isPdbStreamDirectoryBlock() const { 120*0b57cec5SDimitry Andric const auto &Layout = File.pdb().getMsfLayout(); 121*0b57cec5SDimitry Andric return llvm::is_contained(Layout.DirectoryBlocks, pdbBlockIndex()); 122*0b57cec5SDimitry Andric } 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric Optional<uint32_t> ExplainOutputStyle::getPdbBlockStreamIndex() const { 125*0b57cec5SDimitry Andric const auto &Layout = File.pdb().getMsfLayout(); 126*0b57cec5SDimitry Andric for (const auto &Entry : enumerate(Layout.StreamMap)) { 127*0b57cec5SDimitry Andric if (!llvm::is_contained(Entry.value(), pdbBlockIndex())) 128*0b57cec5SDimitry Andric continue; 129*0b57cec5SDimitry Andric return Entry.index(); 130*0b57cec5SDimitry Andric } 131*0b57cec5SDimitry Andric return None; 132*0b57cec5SDimitry Andric } 133*0b57cec5SDimitry Andric 134*0b57cec5SDimitry Andric bool ExplainOutputStyle::explainPdbBlockStatus() { 135*0b57cec5SDimitry Andric if (FileOffset >= File.pdb().getFileSize()) { 136*0b57cec5SDimitry Andric P.formatLine("Address {0} is not in the file (file size = {1}).", 137*0b57cec5SDimitry Andric FileOffset, File.pdb().getFileSize()); 138*0b57cec5SDimitry Andric return false; 139*0b57cec5SDimitry Andric } 140*0b57cec5SDimitry Andric P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, pdbBlockOffset(), 141*0b57cec5SDimitry Andric pdbBlockIndex()); 142*0b57cec5SDimitry Andric 143*0b57cec5SDimitry Andric bool IsFree = File.pdb().getMsfLayout().FreePageMap[pdbBlockIndex()]; 144*0b57cec5SDimitry Andric P.formatLine("Address is in block {0} ({1}allocated).", pdbBlockIndex(), 145*0b57cec5SDimitry Andric IsFree ? "un" : ""); 146*0b57cec5SDimitry Andric return !IsFree; 147*0b57cec5SDimitry Andric } 148*0b57cec5SDimitry Andric 149*0b57cec5SDimitry Andric #define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field)) 150*0b57cec5SDimitry Andric 151*0b57cec5SDimitry Andric void ExplainOutputStyle::explainPdbSuperBlockOffset() { 152*0b57cec5SDimitry Andric P.formatLine("This corresponds to offset {0} of the MSF super block, ", 153*0b57cec5SDimitry Andric pdbBlockOffset()); 154*0b57cec5SDimitry Andric if (pdbBlockOffset() < endof(SuperBlock, MagicBytes)) 155*0b57cec5SDimitry Andric P.printLine("which is part of the MSF file magic."); 156*0b57cec5SDimitry Andric else if (pdbBlockOffset() < endof(SuperBlock, BlockSize)) { 157*0b57cec5SDimitry Andric P.printLine("which contains the block size of the file."); 158*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", 159*0b57cec5SDimitry Andric uint32_t(File.pdb().getMsfLayout().SB->BlockSize)); 160*0b57cec5SDimitry Andric } else if (pdbBlockOffset() < endof(SuperBlock, FreeBlockMapBlock)) { 161*0b57cec5SDimitry Andric P.printLine("which contains the index of the FPM block (e.g. 1 or 2)."); 162*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", 163*0b57cec5SDimitry Andric uint32_t(File.pdb().getMsfLayout().SB->FreeBlockMapBlock)); 164*0b57cec5SDimitry Andric } else if (pdbBlockOffset() < endof(SuperBlock, NumBlocks)) { 165*0b57cec5SDimitry Andric P.printLine("which contains the number of blocks in the file."); 166*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", 167*0b57cec5SDimitry Andric uint32_t(File.pdb().getMsfLayout().SB->NumBlocks)); 168*0b57cec5SDimitry Andric } else if (pdbBlockOffset() < endof(SuperBlock, NumDirectoryBytes)) { 169*0b57cec5SDimitry Andric P.printLine("which contains the number of bytes in the stream directory."); 170*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", 171*0b57cec5SDimitry Andric uint32_t(File.pdb().getMsfLayout().SB->NumDirectoryBytes)); 172*0b57cec5SDimitry Andric } else if (pdbBlockOffset() < endof(SuperBlock, Unknown1)) { 173*0b57cec5SDimitry Andric P.printLine("whose purpose is unknown."); 174*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", 175*0b57cec5SDimitry Andric uint32_t(File.pdb().getMsfLayout().SB->Unknown1)); 176*0b57cec5SDimitry Andric } else if (pdbBlockOffset() < endof(SuperBlock, BlockMapAddr)) { 177*0b57cec5SDimitry Andric P.printLine("which contains the file offset of the block map."); 178*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", 179*0b57cec5SDimitry Andric uint32_t(File.pdb().getMsfLayout().SB->BlockMapAddr)); 180*0b57cec5SDimitry Andric } else { 181*0b57cec5SDimitry Andric assert(pdbBlockOffset() > sizeof(SuperBlock)); 182*0b57cec5SDimitry Andric P.printLine( 183*0b57cec5SDimitry Andric "which is outside the range of valid data for the super block."); 184*0b57cec5SDimitry Andric } 185*0b57cec5SDimitry Andric } 186*0b57cec5SDimitry Andric 187*0b57cec5SDimitry Andric static std::string toBinaryString(uint8_t Byte) { 188*0b57cec5SDimitry Andric char Result[9] = {0}; 189*0b57cec5SDimitry Andric for (int I = 0; I < 8; ++I) { 190*0b57cec5SDimitry Andric char C = (Byte & 1) ? '1' : '0'; 191*0b57cec5SDimitry Andric Result[I] = C; 192*0b57cec5SDimitry Andric Byte >>= 1; 193*0b57cec5SDimitry Andric } 194*0b57cec5SDimitry Andric return std::string(Result); 195*0b57cec5SDimitry Andric } 196*0b57cec5SDimitry Andric 197*0b57cec5SDimitry Andric void ExplainOutputStyle::explainPdbFpmBlockOffset() { 198*0b57cec5SDimitry Andric const MSFLayout &Layout = File.pdb().getMsfLayout(); 199*0b57cec5SDimitry Andric uint32_t MainFpm = Layout.mainFpmBlock(); 200*0b57cec5SDimitry Andric uint32_t AltFpm = Layout.alternateFpmBlock(); 201*0b57cec5SDimitry Andric 202*0b57cec5SDimitry Andric assert(isPdbFpmBlock()); 203*0b57cec5SDimitry Andric uint32_t Fpm = isPdbFpm1() ? 1 : 2; 204*0b57cec5SDimitry Andric uint32_t FpmChunk = pdbBlockIndex() / File.pdb().getBlockSize(); 205*0b57cec5SDimitry Andric assert((Fpm == MainFpm) || (Fpm == AltFpm)); 206*0b57cec5SDimitry Andric (void)AltFpm; 207*0b57cec5SDimitry Andric bool IsMain = (Fpm == MainFpm); 208*0b57cec5SDimitry Andric P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt"); 209*0b57cec5SDimitry Andric uint32_t DescribedBlockStart = 210*0b57cec5SDimitry Andric 8 * (FpmChunk * File.pdb().getBlockSize() + pdbBlockOffset()); 211*0b57cec5SDimitry Andric if (DescribedBlockStart > File.pdb().getBlockCount()) { 212*0b57cec5SDimitry Andric P.printLine("Address is in extraneous FPM space."); 213*0b57cec5SDimitry Andric return; 214*0b57cec5SDimitry Andric } 215*0b57cec5SDimitry Andric 216*0b57cec5SDimitry Andric P.formatLine("Address describes the allocation status of blocks [{0},{1})", 217*0b57cec5SDimitry Andric DescribedBlockStart, DescribedBlockStart + 8); 218*0b57cec5SDimitry Andric ArrayRef<uint8_t> Bytes; 219*0b57cec5SDimitry Andric cantFail(File.pdb().getMsfBuffer().readBytes(FileOffset, 1, Bytes)); 220*0b57cec5SDimitry Andric P.formatLine("Status = {0} (Note: 0 = allocated, 1 = free)", 221*0b57cec5SDimitry Andric toBinaryString(Bytes[0])); 222*0b57cec5SDimitry Andric } 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric void ExplainOutputStyle::explainPdbBlockMapOffset() { 225*0b57cec5SDimitry Andric uint64_t BlockMapOffset = File.pdb().getBlockMapOffset(); 226*0b57cec5SDimitry Andric uint32_t OffsetInBlock = FileOffset - BlockMapOffset; 227*0b57cec5SDimitry Andric P.formatLine("Address is at offset {0} of the directory block list", 228*0b57cec5SDimitry Andric OffsetInBlock); 229*0b57cec5SDimitry Andric } 230*0b57cec5SDimitry Andric 231*0b57cec5SDimitry Andric static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks, 232*0b57cec5SDimitry Andric uint64_t FileOffset, uint32_t BlockSize) { 233*0b57cec5SDimitry Andric uint32_t BlockIndex = FileOffset / BlockSize; 234*0b57cec5SDimitry Andric uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize; 235*0b57cec5SDimitry Andric 236*0b57cec5SDimitry Andric auto Iter = llvm::find(StreamBlocks, BlockIndex); 237*0b57cec5SDimitry Andric assert(Iter != StreamBlocks.end()); 238*0b57cec5SDimitry Andric uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter); 239*0b57cec5SDimitry Andric return StreamBlockIndex * BlockSize + OffsetInBlock; 240*0b57cec5SDimitry Andric } 241*0b57cec5SDimitry Andric 242*0b57cec5SDimitry Andric void ExplainOutputStyle::explainPdbStreamOffset(uint32_t Stream) { 243*0b57cec5SDimitry Andric SmallVector<StreamInfo, 12> Streams; 244*0b57cec5SDimitry Andric discoverStreamPurposes(File.pdb(), Streams); 245*0b57cec5SDimitry Andric 246*0b57cec5SDimitry Andric assert(Stream <= Streams.size()); 247*0b57cec5SDimitry Andric const StreamInfo &S = Streams[Stream]; 248*0b57cec5SDimitry Andric const auto &Layout = File.pdb().getStreamLayout(Stream); 249*0b57cec5SDimitry Andric uint32_t StreamOff = 250*0b57cec5SDimitry Andric getOffsetInStream(Layout.Blocks, FileOffset, File.pdb().getBlockSize()); 251*0b57cec5SDimitry Andric P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.", 252*0b57cec5SDimitry Andric StreamOff, Layout.Length, Stream, S.getLongName(), 253*0b57cec5SDimitry Andric (StreamOff > Layout.Length) ? " in unused space" : ""); 254*0b57cec5SDimitry Andric switch (S.getPurpose()) { 255*0b57cec5SDimitry Andric case StreamPurpose::DBI: { 256*0b57cec5SDimitry Andric DbiStream &Dbi = cantFail(File.pdb().getPDBDbiStream()); 257*0b57cec5SDimitry Andric explainStreamOffset(Dbi, StreamOff); 258*0b57cec5SDimitry Andric break; 259*0b57cec5SDimitry Andric } 260*0b57cec5SDimitry Andric case StreamPurpose::PDB: { 261*0b57cec5SDimitry Andric InfoStream &Info = cantFail(File.pdb().getPDBInfoStream()); 262*0b57cec5SDimitry Andric explainStreamOffset(Info, StreamOff); 263*0b57cec5SDimitry Andric break; 264*0b57cec5SDimitry Andric } 265*0b57cec5SDimitry Andric case StreamPurpose::IPI: 266*0b57cec5SDimitry Andric case StreamPurpose::TPI: 267*0b57cec5SDimitry Andric case StreamPurpose::ModuleStream: 268*0b57cec5SDimitry Andric case StreamPurpose::NamedStream: 269*0b57cec5SDimitry Andric default: 270*0b57cec5SDimitry Andric break; 271*0b57cec5SDimitry Andric } 272*0b57cec5SDimitry Andric } 273*0b57cec5SDimitry Andric 274*0b57cec5SDimitry Andric void ExplainOutputStyle::explainPdbStreamDirectoryOffset() { 275*0b57cec5SDimitry Andric auto DirectoryBlocks = File.pdb().getDirectoryBlockArray(); 276*0b57cec5SDimitry Andric const auto &Layout = File.pdb().getMsfLayout(); 277*0b57cec5SDimitry Andric uint32_t StreamOff = 278*0b57cec5SDimitry Andric getOffsetInStream(DirectoryBlocks, FileOffset, File.pdb().getBlockSize()); 279*0b57cec5SDimitry Andric P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.", 280*0b57cec5SDimitry Andric StreamOff, uint32_t(Layout.SB->NumDirectoryBytes), 281*0b57cec5SDimitry Andric uint32_t(StreamOff > Layout.SB->NumDirectoryBytes) 282*0b57cec5SDimitry Andric ? " in unused space" 283*0b57cec5SDimitry Andric : ""); 284*0b57cec5SDimitry Andric } 285*0b57cec5SDimitry Andric 286*0b57cec5SDimitry Andric void ExplainOutputStyle::explainPdbUnknownBlock() { 287*0b57cec5SDimitry Andric P.formatLine("Address has unknown purpose."); 288*0b57cec5SDimitry Andric } 289*0b57cec5SDimitry Andric 290*0b57cec5SDimitry Andric template <typename T> 291*0b57cec5SDimitry Andric static void printStructField(LinePrinter &P, StringRef Label, T Value) { 292*0b57cec5SDimitry Andric P.formatLine("which contains {0}.", Label); 293*0b57cec5SDimitry Andric P.formatLine("The current value is {0}.", Value); 294*0b57cec5SDimitry Andric } 295*0b57cec5SDimitry Andric 296*0b57cec5SDimitry Andric static void explainDbiHeaderOffset(LinePrinter &P, DbiStream &Dbi, 297*0b57cec5SDimitry Andric uint32_t Offset) { 298*0b57cec5SDimitry Andric const DbiStreamHeader *Header = Dbi.getHeader(); 299*0b57cec5SDimitry Andric assert(Header != nullptr); 300*0b57cec5SDimitry Andric 301*0b57cec5SDimitry Andric if (Offset < endof(DbiStreamHeader, VersionSignature)) 302*0b57cec5SDimitry Andric printStructField(P, "the DBI Stream Version Signature", 303*0b57cec5SDimitry Andric int32_t(Header->VersionSignature)); 304*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, VersionHeader)) 305*0b57cec5SDimitry Andric printStructField(P, "the DBI Stream Version Header", 306*0b57cec5SDimitry Andric uint32_t(Header->VersionHeader)); 307*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, Age)) 308*0b57cec5SDimitry Andric printStructField(P, "the age of the DBI Stream", uint32_t(Header->Age)); 309*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, GlobalSymbolStreamIndex)) 310*0b57cec5SDimitry Andric printStructField(P, "the index of the Global Symbol Stream", 311*0b57cec5SDimitry Andric uint16_t(Header->GlobalSymbolStreamIndex)); 312*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, BuildNumber)) 313*0b57cec5SDimitry Andric printStructField(P, "the build number", uint16_t(Header->BuildNumber)); 314*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, PublicSymbolStreamIndex)) 315*0b57cec5SDimitry Andric printStructField(P, "the index of the Public Symbol Stream", 316*0b57cec5SDimitry Andric uint16_t(Header->PublicSymbolStreamIndex)); 317*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, PdbDllVersion)) 318*0b57cec5SDimitry Andric printStructField(P, "the version of mspdb.dll", 319*0b57cec5SDimitry Andric uint16_t(Header->PdbDllVersion)); 320*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, SymRecordStreamIndex)) 321*0b57cec5SDimitry Andric printStructField(P, "the index of the Symbol Record Stream", 322*0b57cec5SDimitry Andric uint16_t(Header->SymRecordStreamIndex)); 323*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, PdbDllRbld)) 324*0b57cec5SDimitry Andric printStructField(P, "the rbld of mspdb.dll", uint16_t(Header->PdbDllRbld)); 325*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, ModiSubstreamSize)) 326*0b57cec5SDimitry Andric printStructField(P, "the size of the Module Info Substream", 327*0b57cec5SDimitry Andric int32_t(Header->ModiSubstreamSize)); 328*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, SecContrSubstreamSize)) 329*0b57cec5SDimitry Andric printStructField(P, "the size of the Section Contribution Substream", 330*0b57cec5SDimitry Andric int32_t(Header->SecContrSubstreamSize)); 331*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, SectionMapSize)) 332*0b57cec5SDimitry Andric printStructField(P, "the size of the Section Map Substream", 333*0b57cec5SDimitry Andric int32_t(Header->SectionMapSize)); 334*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, FileInfoSize)) 335*0b57cec5SDimitry Andric printStructField(P, "the size of the File Info Substream", 336*0b57cec5SDimitry Andric int32_t(Header->FileInfoSize)); 337*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, TypeServerSize)) 338*0b57cec5SDimitry Andric printStructField(P, "the size of the Type Server Map", 339*0b57cec5SDimitry Andric int32_t(Header->TypeServerSize)); 340*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, MFCTypeServerIndex)) 341*0b57cec5SDimitry Andric printStructField(P, "the index of the MFC Type Server stream", 342*0b57cec5SDimitry Andric uint32_t(Header->MFCTypeServerIndex)); 343*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, OptionalDbgHdrSize)) 344*0b57cec5SDimitry Andric printStructField(P, "the size of the Optional Debug Stream array", 345*0b57cec5SDimitry Andric int32_t(Header->OptionalDbgHdrSize)); 346*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, ECSubstreamSize)) 347*0b57cec5SDimitry Andric printStructField(P, "the size of the Edit & Continue Substream", 348*0b57cec5SDimitry Andric int32_t(Header->ECSubstreamSize)); 349*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, Flags)) 350*0b57cec5SDimitry Andric printStructField(P, "the DBI Stream flags", uint16_t(Header->Flags)); 351*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, MachineType)) 352*0b57cec5SDimitry Andric printStructField(P, "the machine type", uint16_t(Header->MachineType)); 353*0b57cec5SDimitry Andric else if (Offset < endof(DbiStreamHeader, Reserved)) 354*0b57cec5SDimitry Andric printStructField(P, "reserved data", uint32_t(Header->Reserved)); 355*0b57cec5SDimitry Andric } 356*0b57cec5SDimitry Andric 357*0b57cec5SDimitry Andric static void explainDbiModiSubstreamOffset(LinePrinter &P, DbiStream &Dbi, 358*0b57cec5SDimitry Andric uint32_t Offset) { 359*0b57cec5SDimitry Andric VarStreamArray<DbiModuleDescriptor> ModuleDescriptors; 360*0b57cec5SDimitry Andric BinaryStreamRef ModiSubstreamData = Dbi.getModiSubstreamData().StreamData; 361*0b57cec5SDimitry Andric BinaryStreamReader Reader(ModiSubstreamData); 362*0b57cec5SDimitry Andric 363*0b57cec5SDimitry Andric cantFail(Reader.readArray(ModuleDescriptors, ModiSubstreamData.getLength())); 364*0b57cec5SDimitry Andric auto Prev = ModuleDescriptors.begin(); 365*0b57cec5SDimitry Andric assert(Prev.offset() == 0); 366*0b57cec5SDimitry Andric auto Current = Prev; 367*0b57cec5SDimitry Andric uint32_t Index = 0; 368*0b57cec5SDimitry Andric while (true) { 369*0b57cec5SDimitry Andric Prev = Current; 370*0b57cec5SDimitry Andric ++Current; 371*0b57cec5SDimitry Andric if (Current == ModuleDescriptors.end() || Offset < Current.offset()) 372*0b57cec5SDimitry Andric break; 373*0b57cec5SDimitry Andric ++Index; 374*0b57cec5SDimitry Andric } 375*0b57cec5SDimitry Andric 376*0b57cec5SDimitry Andric DbiModuleDescriptor &Descriptor = *Prev; 377*0b57cec5SDimitry Andric P.formatLine("which contains the descriptor for module {0} ({1}).", Index, 378*0b57cec5SDimitry Andric Descriptor.getModuleName()); 379*0b57cec5SDimitry Andric } 380*0b57cec5SDimitry Andric 381*0b57cec5SDimitry Andric template <typename T> 382*0b57cec5SDimitry Andric static void dontExplain(LinePrinter &Printer, T &Stream, uint32_t Offset) {} 383*0b57cec5SDimitry Andric 384*0b57cec5SDimitry Andric template <typename T, typename SubstreamRangeT> 385*0b57cec5SDimitry Andric static void explainSubstreamOffset(LinePrinter &P, uint32_t OffsetInStream, 386*0b57cec5SDimitry Andric T &Stream, 387*0b57cec5SDimitry Andric const SubstreamRangeT &Substreams) { 388*0b57cec5SDimitry Andric uint32_t SubOffset = OffsetInStream; 389*0b57cec5SDimitry Andric for (const auto &Entry : Substreams) { 390*0b57cec5SDimitry Andric if (Entry.Size <= 0) 391*0b57cec5SDimitry Andric continue; 392*0b57cec5SDimitry Andric uint32_t S = static_cast<uint32_t>(Entry.Size); 393*0b57cec5SDimitry Andric if (SubOffset < S) { 394*0b57cec5SDimitry Andric P.formatLine("address is at offset {0}/{1} of the {2}.", SubOffset, S, 395*0b57cec5SDimitry Andric Entry.Label); 396*0b57cec5SDimitry Andric Entry.Explain(P, Stream, SubOffset); 397*0b57cec5SDimitry Andric return; 398*0b57cec5SDimitry Andric } 399*0b57cec5SDimitry Andric SubOffset -= S; 400*0b57cec5SDimitry Andric } 401*0b57cec5SDimitry Andric } 402*0b57cec5SDimitry Andric 403*0b57cec5SDimitry Andric void ExplainOutputStyle::explainStreamOffset(DbiStream &Dbi, 404*0b57cec5SDimitry Andric uint32_t OffsetInStream) { 405*0b57cec5SDimitry Andric P.printLine("Within the DBI stream:"); 406*0b57cec5SDimitry Andric AutoIndent Indent(P); 407*0b57cec5SDimitry Andric const DbiStreamHeader *Header = Dbi.getHeader(); 408*0b57cec5SDimitry Andric assert(Header != nullptr); 409*0b57cec5SDimitry Andric 410*0b57cec5SDimitry Andric struct SubstreamInfo { 411*0b57cec5SDimitry Andric int32_t Size; 412*0b57cec5SDimitry Andric StringRef Label; 413*0b57cec5SDimitry Andric void (*Explain)(LinePrinter &, DbiStream &, uint32_t); 414*0b57cec5SDimitry Andric } Substreams[] = { 415*0b57cec5SDimitry Andric {sizeof(DbiStreamHeader), "DBI Stream Header", explainDbiHeaderOffset}, 416*0b57cec5SDimitry Andric {int32_t(Header->ModiSubstreamSize), "Module Info Substream", 417*0b57cec5SDimitry Andric explainDbiModiSubstreamOffset}, 418*0b57cec5SDimitry Andric {int32_t(Header->SecContrSubstreamSize), "Section Contribution Substream", 419*0b57cec5SDimitry Andric dontExplain<DbiStream>}, 420*0b57cec5SDimitry Andric {int32_t(Header->SectionMapSize), "Section Map", dontExplain<DbiStream>}, 421*0b57cec5SDimitry Andric {int32_t(Header->FileInfoSize), "File Info Substream", 422*0b57cec5SDimitry Andric dontExplain<DbiStream>}, 423*0b57cec5SDimitry Andric {int32_t(Header->TypeServerSize), "Type Server Map Substream", 424*0b57cec5SDimitry Andric dontExplain<DbiStream>}, 425*0b57cec5SDimitry Andric {int32_t(Header->ECSubstreamSize), "Edit & Continue Substream", 426*0b57cec5SDimitry Andric dontExplain<DbiStream>}, 427*0b57cec5SDimitry Andric {int32_t(Header->OptionalDbgHdrSize), "Optional Debug Stream Array", 428*0b57cec5SDimitry Andric dontExplain<DbiStream>}, 429*0b57cec5SDimitry Andric }; 430*0b57cec5SDimitry Andric 431*0b57cec5SDimitry Andric explainSubstreamOffset(P, OffsetInStream, Dbi, Substreams); 432*0b57cec5SDimitry Andric } 433*0b57cec5SDimitry Andric 434*0b57cec5SDimitry Andric static void explainPdbStreamHeaderOffset(LinePrinter &P, InfoStream &Info, 435*0b57cec5SDimitry Andric uint32_t Offset) { 436*0b57cec5SDimitry Andric const InfoStreamHeader *Header = Info.getHeader(); 437*0b57cec5SDimitry Andric assert(Header != nullptr); 438*0b57cec5SDimitry Andric 439*0b57cec5SDimitry Andric if (Offset < endof(InfoStreamHeader, Version)) 440*0b57cec5SDimitry Andric printStructField(P, "the PDB Stream Version Signature", 441*0b57cec5SDimitry Andric uint32_t(Header->Version)); 442*0b57cec5SDimitry Andric else if (Offset < endof(InfoStreamHeader, Signature)) 443*0b57cec5SDimitry Andric printStructField(P, "the signature of the PDB Stream", 444*0b57cec5SDimitry Andric uint32_t(Header->Signature)); 445*0b57cec5SDimitry Andric else if (Offset < endof(InfoStreamHeader, Age)) 446*0b57cec5SDimitry Andric printStructField(P, "the age of the PDB", uint32_t(Header->Age)); 447*0b57cec5SDimitry Andric else if (Offset < endof(InfoStreamHeader, Guid)) 448*0b57cec5SDimitry Andric printStructField(P, "the guid of the PDB", fmt_guid(Header->Guid.Guid)); 449*0b57cec5SDimitry Andric } 450*0b57cec5SDimitry Andric 451*0b57cec5SDimitry Andric void ExplainOutputStyle::explainStreamOffset(InfoStream &Info, 452*0b57cec5SDimitry Andric uint32_t OffsetInStream) { 453*0b57cec5SDimitry Andric P.printLine("Within the PDB stream:"); 454*0b57cec5SDimitry Andric AutoIndent Indent(P); 455*0b57cec5SDimitry Andric 456*0b57cec5SDimitry Andric struct SubstreamInfo { 457*0b57cec5SDimitry Andric uint32_t Size; 458*0b57cec5SDimitry Andric StringRef Label; 459*0b57cec5SDimitry Andric void (*Explain)(LinePrinter &, InfoStream &, uint32_t); 460*0b57cec5SDimitry Andric } Substreams[] = {{sizeof(InfoStreamHeader), "PDB Stream Header", 461*0b57cec5SDimitry Andric explainPdbStreamHeaderOffset}, 462*0b57cec5SDimitry Andric {Info.getNamedStreamMapByteSize(), "Named Stream Map", 463*0b57cec5SDimitry Andric dontExplain<InfoStream>}, 464*0b57cec5SDimitry Andric {Info.getStreamSize(), "PDB Feature Signatures", 465*0b57cec5SDimitry Andric dontExplain<InfoStream>}}; 466*0b57cec5SDimitry Andric 467*0b57cec5SDimitry Andric explainSubstreamOffset(P, OffsetInStream, Info, Substreams); 468*0b57cec5SDimitry Andric } 469