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