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