1 //===- BytesOutputStyle.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 "BytesOutputStyle.h"
10 
11 #include "FormatUtil.h"
12 #include "StreamUtil.h"
13 #include "llvm-pdbutil.h"
14 
15 #include "llvm/DebugInfo/CodeView/Formatters.h"
16 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
17 #include "llvm/DebugInfo/MSF/MSFCommon.h"
18 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
19 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
20 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
21 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
22 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
23 #include "llvm/DebugInfo/PDB/Native/RawError.h"
24 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
25 #include "llvm/Support/BinaryStreamReader.h"
26 #include "llvm/Support/FormatAdapters.h"
27 #include "llvm/Support/FormatVariadic.h"
28 
29 using namespace llvm;
30 using namespace llvm::codeview;
31 using namespace llvm::msf;
32 using namespace llvm::pdb;
33 
34 namespace {
35 struct StreamSpec {
36   uint32_t SI = 0;
37   uint32_t Begin = 0;
38   uint32_t Size = 0;
39 };
40 } // namespace
41 
42 static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
43   StreamSpec Result;
44   if (Str.consumeInteger(0, Result.SI))
45     return make_error<RawError>(raw_error_code::invalid_format,
46                                 "Invalid Stream Specification");
47   if (Str.consume_front(":")) {
48     if (Str.consumeInteger(0, Result.Begin))
49       return make_error<RawError>(raw_error_code::invalid_format,
50                                   "Invalid Stream Specification");
51   }
52   if (Str.consume_front("@")) {
53     if (Str.consumeInteger(0, Result.Size))
54       return make_error<RawError>(raw_error_code::invalid_format,
55                                   "Invalid Stream Specification");
56   }
57 
58   if (!Str.empty())
59     return make_error<RawError>(raw_error_code::invalid_format,
60                                 "Invalid Stream Specification");
61   return Result;
62 }
63 
64 static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
65   SmallVector<StreamSpec, 2> Result;
66 
67   for (auto &Str : opts::bytes::DumpStreamData) {
68     auto ESS = parseStreamSpec(Str);
69     if (!ESS) {
70       P.formatLine("Error parsing stream spec {0}: {1}", Str,
71                    toString(ESS.takeError()));
72       continue;
73     }
74     Result.push_back(*ESS);
75   }
76   return Result;
77 }
78 
79 static void printHeader(LinePrinter &P, const Twine &S) {
80   P.NewLine();
81   P.formatLine("{0,=60}", S);
82   P.formatLine("{0}", fmt_repeat('=', 60));
83 }
84 
85 BytesOutputStyle::BytesOutputStyle(PDBFile &File)
86     : File(File), P(2, false, outs()) {}
87 
88 Error BytesOutputStyle::dump() {
89 
90   if (opts::bytes::DumpBlockRange.hasValue()) {
91     auto &R = *opts::bytes::DumpBlockRange;
92     uint32_t Max = R.Max.getValueOr(R.Min);
93 
94     if (Max < R.Min)
95       return make_error<StringError>(
96           "Invalid block range specified.  Max < Min",
97           inconvertibleErrorCode());
98     if (Max >= File.getBlockCount())
99       return make_error<StringError>(
100           "Invalid block range specified.  Requested block out of bounds",
101           inconvertibleErrorCode());
102 
103     dumpBlockRanges(R.Min, Max);
104     P.NewLine();
105   }
106 
107   if (opts::bytes::DumpByteRange.hasValue()) {
108     auto &R = *opts::bytes::DumpByteRange;
109     uint32_t Max = R.Max.getValueOr(File.getFileSize());
110 
111     if (Max < R.Min)
112       return make_error<StringError>("Invalid byte range specified.  Max < Min",
113                                      inconvertibleErrorCode());
114     if (Max >= File.getFileSize())
115       return make_error<StringError>(
116           "Invalid byte range specified.  Requested byte larger than file size",
117           inconvertibleErrorCode());
118 
119     dumpByteRanges(R.Min, Max);
120     P.NewLine();
121   }
122 
123   if (opts::bytes::Fpm) {
124     dumpFpm();
125     P.NewLine();
126   }
127 
128   if (!opts::bytes::DumpStreamData.empty()) {
129     dumpStreamBytes();
130     P.NewLine();
131   }
132 
133   if (opts::bytes::NameMap) {
134     dumpNameMap();
135     P.NewLine();
136   }
137 
138   if (opts::bytes::SectionContributions) {
139     dumpSectionContributions();
140     P.NewLine();
141   }
142 
143   if (opts::bytes::SectionMap) {
144     dumpSectionMap();
145     P.NewLine();
146   }
147 
148   if (opts::bytes::ModuleInfos) {
149     dumpModuleInfos();
150     P.NewLine();
151   }
152 
153   if (opts::bytes::FileInfo) {
154     dumpFileInfo();
155     P.NewLine();
156   }
157 
158   if (opts::bytes::TypeServerMap) {
159     dumpTypeServerMap();
160     P.NewLine();
161   }
162 
163   if (opts::bytes::ECData) {
164     dumpECData();
165     P.NewLine();
166   }
167 
168   if (!opts::bytes::TypeIndex.empty()) {
169     dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex);
170     P.NewLine();
171   }
172 
173   if (!opts::bytes::IdIndex.empty()) {
174     dumpTypeIndex(StreamIPI, opts::bytes::IdIndex);
175     P.NewLine();
176   }
177 
178   if (opts::bytes::ModuleSyms) {
179     dumpModuleSyms();
180     P.NewLine();
181   }
182 
183   if (opts::bytes::ModuleC11) {
184     dumpModuleC11();
185     P.NewLine();
186   }
187 
188   if (opts::bytes::ModuleC13) {
189     dumpModuleC13();
190     P.NewLine();
191   }
192 
193   return Error::success();
194 }
195 
196 void BytesOutputStyle::dumpNameMap() {
197   printHeader(P, "Named Stream Map");
198 
199   AutoIndent Indent(P);
200 
201   auto &InfoS = Err(File.getPDBInfoStream());
202   BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer();
203   auto Layout = File.getStreamLayout(StreamPDB);
204   P.formatMsfStreamData("Named Stream Map", File, Layout, NS);
205 }
206 
207 void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
208   printHeader(P, "MSF Blocks");
209 
210   AutoIndent Indent(P);
211   for (uint32_t I = Min; I <= Max; ++I) {
212     uint64_t Base = I;
213     Base *= File.getBlockSize();
214 
215     auto ExpectedData = File.getBlockData(I, File.getBlockSize());
216     if (!ExpectedData) {
217       P.formatLine("Could not get block {0}.  Reason = {1}", I,
218                    toString(ExpectedData.takeError()));
219       continue;
220     }
221     std::string Label = formatv("Block {0}", I).str();
222     P.formatBinary(Label, *ExpectedData, Base, 0);
223   }
224 }
225 
226 void BytesOutputStyle::dumpSectionContributions() {
227   printHeader(P, "Section Contributions");
228 
229   AutoIndent Indent(P);
230 
231   auto &DbiS = Err(File.getPDBDbiStream());
232   BinarySubstreamRef NS = DbiS.getSectionContributionData();
233   auto Layout = File.getStreamLayout(StreamDBI);
234   P.formatMsfStreamData("Section Contributions", File, Layout, NS);
235 }
236 
237 void BytesOutputStyle::dumpSectionMap() {
238   printHeader(P, "Section Map");
239 
240   AutoIndent Indent(P);
241 
242   auto &DbiS = Err(File.getPDBDbiStream());
243   BinarySubstreamRef NS = DbiS.getSecMapSubstreamData();
244   auto Layout = File.getStreamLayout(StreamDBI);
245   P.formatMsfStreamData("Section Map", File, Layout, NS);
246 }
247 
248 void BytesOutputStyle::dumpModuleInfos() {
249   printHeader(P, "Module Infos");
250 
251   AutoIndent Indent(P);
252 
253   auto &DbiS = Err(File.getPDBDbiStream());
254   BinarySubstreamRef NS = DbiS.getModiSubstreamData();
255   auto Layout = File.getStreamLayout(StreamDBI);
256   P.formatMsfStreamData("Module Infos", File, Layout, NS);
257 }
258 
259 void BytesOutputStyle::dumpFileInfo() {
260   printHeader(P, "File Info");
261 
262   AutoIndent Indent(P);
263 
264   auto &DbiS = Err(File.getPDBDbiStream());
265   BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData();
266   auto Layout = File.getStreamLayout(StreamDBI);
267   P.formatMsfStreamData("File Info", File, Layout, NS);
268 }
269 
270 void BytesOutputStyle::dumpTypeServerMap() {
271   printHeader(P, "Type Server Map");
272 
273   AutoIndent Indent(P);
274 
275   auto &DbiS = Err(File.getPDBDbiStream());
276   BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData();
277   auto Layout = File.getStreamLayout(StreamDBI);
278   P.formatMsfStreamData("Type Server Map", File, Layout, NS);
279 }
280 
281 void BytesOutputStyle::dumpECData() {
282   printHeader(P, "Edit and Continue Data");
283 
284   AutoIndent Indent(P);
285 
286   auto &DbiS = Err(File.getPDBDbiStream());
287   BinarySubstreamRef NS = DbiS.getECSubstreamData();
288   auto Layout = File.getStreamLayout(StreamDBI);
289   P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS);
290 }
291 
292 void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx,
293                                      ArrayRef<uint32_t> Indices) {
294   assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
295   assert(!Indices.empty());
296 
297   bool IsTpi = (StreamIdx == StreamTPI);
298 
299   StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records";
300   printHeader(P, Label);
301   auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream());
302 
303   AutoIndent Indent(P);
304 
305   auto Substream = Stream.getTypeRecordsSubstream();
306   auto &Types = Err(initializeTypes(StreamIdx));
307   auto Layout = File.getStreamLayout(StreamIdx);
308   for (const auto &Id : Indices) {
309     TypeIndex TI(Id);
310     if (TI.toArrayIndex() >= Types.capacity()) {
311       P.formatLine("Error: TypeIndex {0} does not exist", TI);
312       continue;
313     }
314 
315     auto Type = Types.getType(TI);
316     uint32_t Offset = Types.getOffsetOfType(TI);
317     auto OneType = Substream.slice(Offset, Type.length());
318     P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType);
319   }
320 }
321 
322 template <typename CallbackT>
323 static void iterateOneModule(PDBFile &File, LinePrinter &P,
324                              const DbiModuleList &Modules, uint32_t I,
325                              uint32_t Digits, uint32_t IndentLevel,
326                              CallbackT Callback) {
327   if (I >= Modules.getModuleCount()) {
328     P.formatLine("Mod {0:4} | Invalid module index ",
329                  fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)));
330     return;
331   }
332 
333   auto Modi = Modules.getModuleDescriptor(I);
334   P.formatLine("Mod {0:4} | `{1}`: ",
335                fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)),
336                Modi.getModuleName());
337 
338   uint16_t ModiStream = Modi.getModuleStreamIndex();
339   AutoIndent Indent2(P, IndentLevel);
340   if (ModiStream == kInvalidStreamIndex)
341     return;
342 
343   auto ModStreamData = File.createIndexedStream(ModiStream);
344   ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData));
345   if (auto EC = ModStream.reload()) {
346     P.formatLine("Could not parse debug information.");
347     return;
348   }
349   auto Layout = File.getStreamLayout(ModiStream);
350   Callback(I, ModStream, Layout);
351 }
352 
353 template <typename CallbackT>
354 static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
355                            CallbackT Callback) {
356   AutoIndent Indent(P);
357   if (!File.hasPDBDbiStream()) {
358     P.formatLine("DBI Stream not present");
359     return;
360   }
361 
362   ExitOnError Err("Unexpected error processing modules");
363 
364   auto &Stream = Err(File.getPDBDbiStream());
365 
366   const DbiModuleList &Modules = Stream.modules();
367 
368   if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) {
369     iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel,
370                      Callback);
371   } else {
372     uint32_t Count = Modules.getModuleCount();
373     uint32_t Digits = NumDigits(Count);
374     for (uint32_t I = 0; I < Count; ++I) {
375       iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback);
376     }
377   }
378 }
379 
380 void BytesOutputStyle::dumpModuleSyms() {
381   printHeader(P, "Module Symbols");
382 
383   AutoIndent Indent(P);
384 
385   iterateModules(File, P, 2,
386                  [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
387                         const MSFStreamLayout &Layout) {
388                    auto Symbols = Stream.getSymbolsSubstream();
389                    P.formatMsfStreamData("Symbols", File, Layout, Symbols);
390                  });
391 }
392 
393 void BytesOutputStyle::dumpModuleC11() {
394   printHeader(P, "C11 Debug Chunks");
395 
396   AutoIndent Indent(P);
397 
398   iterateModules(File, P, 2,
399                  [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
400                         const MSFStreamLayout &Layout) {
401                    auto Chunks = Stream.getC11LinesSubstream();
402                    P.formatMsfStreamData("C11 Debug Chunks", File, Layout,
403                                          Chunks);
404                  });
405 }
406 
407 void BytesOutputStyle::dumpModuleC13() {
408   printHeader(P, "Debug Chunks");
409 
410   AutoIndent Indent(P);
411 
412   iterateModules(
413       File, P, 2,
414       [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
415              const MSFStreamLayout &Layout) {
416         auto Chunks = Stream.getC13LinesSubstream();
417         if (opts::bytes::SplitChunks) {
418           for (const auto &SS : Stream.subsections()) {
419             BinarySubstreamRef ThisChunk;
420             std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength());
421             P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout,
422                                   ThisChunk);
423           }
424         } else {
425           P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks);
426         }
427       });
428 }
429 
430 void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
431   printHeader(P, "MSF Bytes");
432 
433   AutoIndent Indent(P);
434 
435   BinaryStreamReader Reader(File.getMsfBuffer());
436   ArrayRef<uint8_t> Data;
437   consumeError(Reader.skip(Min));
438   uint32_t Size = Max - Min + 1;
439   auto EC = Reader.readBytes(Data, Size);
440   assert(!EC);
441   consumeError(std::move(EC));
442   P.formatBinary("Bytes", Data, Min);
443 }
444 
445 Expected<codeview::LazyRandomTypeCollection &>
446 BytesOutputStyle::initializeTypes(uint32_t StreamIdx) {
447   auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes;
448   if (TypeCollection)
449     return *TypeCollection;
450 
451   auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
452                                       : File.getPDBIpiStream();
453   if (!Tpi)
454     return Tpi.takeError();
455 
456   auto &Types = Tpi->typeArray();
457   uint32_t Count = Tpi->getNumTypeRecords();
458   auto Offsets = Tpi->getTypeIndexOffsets();
459   TypeCollection =
460       std::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
461 
462   return *TypeCollection;
463 }
464 
465 void BytesOutputStyle::dumpFpm() {
466   printHeader(P, "Free Page Map");
467 
468   msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout();
469   P.formatMsfStreamBlocks(File, FpmLayout);
470 }
471 
472 void BytesOutputStyle::dumpStreamBytes() {
473   if (StreamPurposes.empty())
474     discoverStreamPurposes(File, StreamPurposes);
475 
476   printHeader(P, "Stream Data");
477   ExitOnError Err("Unexpected error reading stream data");
478 
479   auto Specs = parseStreamSpecs(P);
480 
481   for (const auto &Spec : Specs) {
482     AutoIndent Indent(P);
483     if (Spec.SI >= StreamPurposes.size()) {
484       P.formatLine("Stream {0}: Not present", Spec.SI);
485       continue;
486     }
487     P.formatMsfStreamData("Data", File, Spec.SI,
488                           StreamPurposes[Spec.SI].getShortName(), Spec.Begin,
489                           Spec.Size);
490   }
491 }
492