494 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			494 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "BytesOutputStyle.h"
 | |
| 
 | |
| #include "FormatUtil.h"
 | |
| #include "StreamUtil.h"
 | |
| #include "llvm-pdbutil.h"
 | |
| 
 | |
| #include "llvm/DebugInfo/CodeView/Formatters.h"
 | |
| #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
 | |
| #include "llvm/DebugInfo/MSF/MSFCommon.h"
 | |
| #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/RawError.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
 | |
| #include "llvm/Support/BinaryStreamReader.h"
 | |
| #include "llvm/Support/FormatAdapters.h"
 | |
| #include "llvm/Support/FormatVariadic.h"
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace llvm::codeview;
 | |
| using namespace llvm::msf;
 | |
| using namespace llvm::pdb;
 | |
| 
 | |
| namespace {
 | |
| struct StreamSpec {
 | |
|   uint32_t SI = 0;
 | |
|   uint32_t Begin = 0;
 | |
|   uint32_t Size = 0;
 | |
| };
 | |
| } // namespace
 | |
| 
 | |
| static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
 | |
|   StreamSpec Result;
 | |
|   if (Str.consumeInteger(0, Result.SI))
 | |
|     return make_error<RawError>(raw_error_code::invalid_format,
 | |
|                                 "Invalid Stream Specification");
 | |
|   if (Str.consume_front(":")) {
 | |
|     if (Str.consumeInteger(0, Result.Begin))
 | |
|       return make_error<RawError>(raw_error_code::invalid_format,
 | |
|                                   "Invalid Stream Specification");
 | |
|   }
 | |
|   if (Str.consume_front("@")) {
 | |
|     if (Str.consumeInteger(0, Result.Size))
 | |
|       return make_error<RawError>(raw_error_code::invalid_format,
 | |
|                                   "Invalid Stream Specification");
 | |
|   }
 | |
| 
 | |
|   if (!Str.empty())
 | |
|     return make_error<RawError>(raw_error_code::invalid_format,
 | |
|                                 "Invalid Stream Specification");
 | |
|   return Result;
 | |
| }
 | |
| 
 | |
| static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
 | |
|   SmallVector<StreamSpec, 2> Result;
 | |
| 
 | |
|   for (auto &Str : opts::bytes::DumpStreamData) {
 | |
|     auto ESS = parseStreamSpec(Str);
 | |
|     if (!ESS) {
 | |
|       P.formatLine("Error parsing stream spec {0}: {1}", Str,
 | |
|                    toString(ESS.takeError()));
 | |
|       continue;
 | |
|     }
 | |
|     Result.push_back(*ESS);
 | |
|   }
 | |
|   return Result;
 | |
| }
 | |
| 
 | |
| static void printHeader(LinePrinter &P, const Twine &S) {
 | |
|   P.NewLine();
 | |
|   P.formatLine("{0,=60}", S);
 | |
|   P.formatLine("{0}", fmt_repeat('=', 60));
 | |
| }
 | |
| 
 | |
| BytesOutputStyle::BytesOutputStyle(PDBFile &File)
 | |
|     : File(File), P(2, false, outs()) {}
 | |
| 
 | |
| Error BytesOutputStyle::dump() {
 | |
| 
 | |
|   if (opts::bytes::DumpBlockRange.hasValue()) {
 | |
|     auto &R = *opts::bytes::DumpBlockRange;
 | |
|     uint32_t Max = R.Max.getValueOr(R.Min);
 | |
| 
 | |
|     if (Max < R.Min)
 | |
|       return make_error<StringError>(
 | |
|           "Invalid block range specified.  Max < Min",
 | |
|           inconvertibleErrorCode());
 | |
|     if (Max >= File.getBlockCount())
 | |
|       return make_error<StringError>(
 | |
|           "Invalid block range specified.  Requested block out of bounds",
 | |
|           inconvertibleErrorCode());
 | |
| 
 | |
|     dumpBlockRanges(R.Min, Max);
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::DumpByteRange.hasValue()) {
 | |
|     auto &R = *opts::bytes::DumpByteRange;
 | |
|     uint32_t Max = R.Max.getValueOr(File.getFileSize());
 | |
| 
 | |
|     if (Max < R.Min)
 | |
|       return make_error<StringError>("Invalid byte range specified.  Max < Min",
 | |
|                                      inconvertibleErrorCode());
 | |
|     if (Max >= File.getFileSize())
 | |
|       return make_error<StringError>(
 | |
|           "Invalid byte range specified.  Requested byte larger than file size",
 | |
|           inconvertibleErrorCode());
 | |
| 
 | |
|     dumpByteRanges(R.Min, Max);
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::Fpm) {
 | |
|     dumpFpm();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (!opts::bytes::DumpStreamData.empty()) {
 | |
|     dumpStreamBytes();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::NameMap) {
 | |
|     dumpNameMap();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::SectionContributions) {
 | |
|     dumpSectionContributions();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::SectionMap) {
 | |
|     dumpSectionMap();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::ModuleInfos) {
 | |
|     dumpModuleInfos();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::FileInfo) {
 | |
|     dumpFileInfo();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::TypeServerMap) {
 | |
|     dumpTypeServerMap();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::ECData) {
 | |
|     dumpECData();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (!opts::bytes::TypeIndex.empty()) {
 | |
|     dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex);
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (!opts::bytes::IdIndex.empty()) {
 | |
|     dumpTypeIndex(StreamIPI, opts::bytes::IdIndex);
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::ModuleSyms) {
 | |
|     dumpModuleSyms();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::ModuleC11) {
 | |
|     dumpModuleC11();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   if (opts::bytes::ModuleC13) {
 | |
|     dumpModuleC13();
 | |
|     P.NewLine();
 | |
|   }
 | |
| 
 | |
|   return Error::success();
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpNameMap() {
 | |
|   printHeader(P, "Named Stream Map");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &InfoS = Err(File.getPDBInfoStream());
 | |
|   BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer();
 | |
|   auto Layout = File.getStreamLayout(StreamPDB);
 | |
|   P.formatMsfStreamData("Named Stream Map", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
 | |
|   printHeader(P, "MSF Blocks");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
|   for (uint32_t I = Min; I <= Max; ++I) {
 | |
|     uint64_t Base = I;
 | |
|     Base *= File.getBlockSize();
 | |
| 
 | |
|     auto ExpectedData = File.getBlockData(I, File.getBlockSize());
 | |
|     if (!ExpectedData) {
 | |
|       P.formatLine("Could not get block {0}.  Reason = {1}", I,
 | |
|                    toString(ExpectedData.takeError()));
 | |
|       continue;
 | |
|     }
 | |
|     std::string Label = formatv("Block {0}", I).str();
 | |
|     P.formatBinary(Label, *ExpectedData, Base, 0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpSectionContributions() {
 | |
|   printHeader(P, "Section Contributions");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &DbiS = Err(File.getPDBDbiStream());
 | |
|   BinarySubstreamRef NS = DbiS.getSectionContributionData();
 | |
|   auto Layout = File.getStreamLayout(StreamDBI);
 | |
|   P.formatMsfStreamData("Section Contributions", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpSectionMap() {
 | |
|   printHeader(P, "Section Map");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &DbiS = Err(File.getPDBDbiStream());
 | |
|   BinarySubstreamRef NS = DbiS.getSecMapSubstreamData();
 | |
|   auto Layout = File.getStreamLayout(StreamDBI);
 | |
|   P.formatMsfStreamData("Section Map", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpModuleInfos() {
 | |
|   printHeader(P, "Module Infos");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &DbiS = Err(File.getPDBDbiStream());
 | |
|   BinarySubstreamRef NS = DbiS.getModiSubstreamData();
 | |
|   auto Layout = File.getStreamLayout(StreamDBI);
 | |
|   P.formatMsfStreamData("Module Infos", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpFileInfo() {
 | |
|   printHeader(P, "File Info");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &DbiS = Err(File.getPDBDbiStream());
 | |
|   BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData();
 | |
|   auto Layout = File.getStreamLayout(StreamDBI);
 | |
|   P.formatMsfStreamData("File Info", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpTypeServerMap() {
 | |
|   printHeader(P, "Type Server Map");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &DbiS = Err(File.getPDBDbiStream());
 | |
|   BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData();
 | |
|   auto Layout = File.getStreamLayout(StreamDBI);
 | |
|   P.formatMsfStreamData("Type Server Map", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpECData() {
 | |
|   printHeader(P, "Edit and Continue Data");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto &DbiS = Err(File.getPDBDbiStream());
 | |
|   BinarySubstreamRef NS = DbiS.getECSubstreamData();
 | |
|   auto Layout = File.getStreamLayout(StreamDBI);
 | |
|   P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx,
 | |
|                                      ArrayRef<uint32_t> Indices) {
 | |
|   assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
 | |
|   assert(!Indices.empty());
 | |
| 
 | |
|   bool IsTpi = (StreamIdx == StreamTPI);
 | |
| 
 | |
|   StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records";
 | |
|   printHeader(P, Label);
 | |
|   auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream());
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   auto Substream = Stream.getTypeRecordsSubstream();
 | |
|   auto &Types = Err(initializeTypes(StreamIdx));
 | |
|   auto Layout = File.getStreamLayout(StreamIdx);
 | |
|   for (const auto &Id : Indices) {
 | |
|     TypeIndex TI(Id);
 | |
|     if (TI.toArrayIndex() >= Types.capacity()) {
 | |
|       P.formatLine("Error: TypeIndex {0} does not exist", TI);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     auto Type = Types.getType(TI);
 | |
|     uint32_t Offset = Types.getOffsetOfType(TI);
 | |
|     auto OneType = Substream.slice(Offset, Type.length());
 | |
|     P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType);
 | |
|   }
 | |
| }
 | |
| 
 | |
| template <typename CallbackT>
 | |
| static void iterateOneModule(PDBFile &File, LinePrinter &P,
 | |
|                              const DbiModuleList &Modules, uint32_t I,
 | |
|                              uint32_t Digits, uint32_t IndentLevel,
 | |
|                              CallbackT Callback) {
 | |
|   if (I >= Modules.getModuleCount()) {
 | |
|     P.formatLine("Mod {0:4} | Invalid module index ",
 | |
|                  fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto Modi = Modules.getModuleDescriptor(I);
 | |
|   P.formatLine("Mod {0:4} | `{1}`: ",
 | |
|                fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)),
 | |
|                Modi.getModuleName());
 | |
| 
 | |
|   uint16_t ModiStream = Modi.getModuleStreamIndex();
 | |
|   AutoIndent Indent2(P, IndentLevel);
 | |
|   if (ModiStream == kInvalidStreamIndex)
 | |
|     return;
 | |
| 
 | |
|   auto ModStreamData = MappedBlockStream::createIndexedStream(
 | |
|       File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
 | |
|       File.getAllocator());
 | |
|   ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData));
 | |
|   if (auto EC = ModStream.reload()) {
 | |
|     P.formatLine("Could not parse debug information.");
 | |
|     return;
 | |
|   }
 | |
|   auto Layout = File.getStreamLayout(ModiStream);
 | |
|   Callback(I, ModStream, Layout);
 | |
| }
 | |
| 
 | |
| template <typename CallbackT>
 | |
| static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
 | |
|                            CallbackT Callback) {
 | |
|   AutoIndent Indent(P);
 | |
|   if (!File.hasPDBDbiStream()) {
 | |
|     P.formatLine("DBI Stream not present");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   ExitOnError Err("Unexpected error processing modules");
 | |
| 
 | |
|   auto &Stream = Err(File.getPDBDbiStream());
 | |
| 
 | |
|   const DbiModuleList &Modules = Stream.modules();
 | |
| 
 | |
|   if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) {
 | |
|     iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel,
 | |
|                      Callback);
 | |
|   } else {
 | |
|     uint32_t Count = Modules.getModuleCount();
 | |
|     uint32_t Digits = NumDigits(Count);
 | |
|     for (uint32_t I = 0; I < Count; ++I) {
 | |
|       iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpModuleSyms() {
 | |
|   printHeader(P, "Module Symbols");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   iterateModules(File, P, 2,
 | |
|                  [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
 | |
|                         const MSFStreamLayout &Layout) {
 | |
|                    auto Symbols = Stream.getSymbolsSubstream();
 | |
|                    P.formatMsfStreamData("Symbols", File, Layout, Symbols);
 | |
|                  });
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpModuleC11() {
 | |
|   printHeader(P, "C11 Debug Chunks");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   iterateModules(File, P, 2,
 | |
|                  [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
 | |
|                         const MSFStreamLayout &Layout) {
 | |
|                    auto Chunks = Stream.getC11LinesSubstream();
 | |
|                    P.formatMsfStreamData("C11 Debug Chunks", File, Layout,
 | |
|                                          Chunks);
 | |
|                  });
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpModuleC13() {
 | |
|   printHeader(P, "Debug Chunks");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   iterateModules(
 | |
|       File, P, 2,
 | |
|       [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
 | |
|              const MSFStreamLayout &Layout) {
 | |
|         auto Chunks = Stream.getC13LinesSubstream();
 | |
|         if (opts::bytes::SplitChunks) {
 | |
|           for (const auto &SS : Stream.subsections()) {
 | |
|             BinarySubstreamRef ThisChunk;
 | |
|             std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength());
 | |
|             P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout,
 | |
|                                   ThisChunk);
 | |
|           }
 | |
|         } else {
 | |
|           P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks);
 | |
|         }
 | |
|       });
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
 | |
|   printHeader(P, "MSF Bytes");
 | |
| 
 | |
|   AutoIndent Indent(P);
 | |
| 
 | |
|   BinaryStreamReader Reader(File.getMsfBuffer());
 | |
|   ArrayRef<uint8_t> Data;
 | |
|   consumeError(Reader.skip(Min));
 | |
|   uint32_t Size = Max - Min + 1;
 | |
|   auto EC = Reader.readBytes(Data, Size);
 | |
|   assert(!EC);
 | |
|   consumeError(std::move(EC));
 | |
|   P.formatBinary("Bytes", Data, Min);
 | |
| }
 | |
| 
 | |
| Expected<codeview::LazyRandomTypeCollection &>
 | |
| BytesOutputStyle::initializeTypes(uint32_t StreamIdx) {
 | |
|   auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes;
 | |
|   if (TypeCollection)
 | |
|     return *TypeCollection;
 | |
| 
 | |
|   auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
 | |
|                                       : File.getPDBIpiStream();
 | |
|   if (!Tpi)
 | |
|     return Tpi.takeError();
 | |
| 
 | |
|   auto &Types = Tpi->typeArray();
 | |
|   uint32_t Count = Tpi->getNumTypeRecords();
 | |
|   auto Offsets = Tpi->getTypeIndexOffsets();
 | |
|   TypeCollection =
 | |
|       llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
 | |
| 
 | |
|   return *TypeCollection;
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpFpm() {
 | |
|   printHeader(P, "Free Page Map");
 | |
| 
 | |
|   msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout();
 | |
|   P.formatMsfStreamBlocks(File, FpmLayout);
 | |
| }
 | |
| 
 | |
| void BytesOutputStyle::dumpStreamBytes() {
 | |
|   if (StreamPurposes.empty())
 | |
|     discoverStreamPurposes(File, StreamPurposes);
 | |
| 
 | |
|   printHeader(P, "Stream Data");
 | |
|   ExitOnError Err("Unexpected error reading stream data");
 | |
| 
 | |
|   auto Specs = parseStreamSpecs(P);
 | |
| 
 | |
|   for (const auto &Spec : Specs) {
 | |
|     AutoIndent Indent(P);
 | |
|     if (Spec.SI >= StreamPurposes.size()) {
 | |
|       P.formatLine("Stream {0}: Not present", Spec.SI);
 | |
|       continue;
 | |
|     }
 | |
|     P.formatMsfStreamData("Data", File, Spec.SI,
 | |
|                           StreamPurposes[Spec.SI].getShortName(), Spec.Begin,
 | |
|                           Spec.Size);
 | |
|   }
 | |
| }
 |