ExplainOutputStyle.cpp 18 KB

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