xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- YAMLOutputStyle.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 "YAMLOutputStyle.h"
10 
11 #include "PdbYaml.h"
12 #include "llvm-pdbutil.h"
13 
14 #include "llvm/BinaryFormat/COFF.h"
15 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
16 #include "llvm/DebugInfo/CodeView/DebugSubsection.h"
17 #include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h"
18 #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
19 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
20 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
21 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
22 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
23 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
24 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
25 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
26 #include "llvm/DebugInfo/PDB/Native/RawConstants.h"
27 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
28 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
29 
30 using namespace llvm;
31 using namespace llvm::codeview;
32 using namespace llvm::pdb;
33 
checkModuleSubsection(opts::ModuleSubsection MS)34 static bool checkModuleSubsection(opts::ModuleSubsection MS) {
35   return any_of(opts::pdb2yaml::DumpModuleSubsections,
36                 [=](opts::ModuleSubsection M) {
37                   return M == MS || M == opts::ModuleSubsection::All;
38                 });
39 }
40 
YAMLOutputStyle(PDBFile & File)41 YAMLOutputStyle::YAMLOutputStyle(PDBFile &File)
42     : File(File), Out(outs()), Obj(File.getAllocator()) {
43   Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal);
44 }
45 
dump()46 Error YAMLOutputStyle::dump() {
47   if (opts::pdb2yaml::StreamDirectory)
48     opts::pdb2yaml::StreamMetadata = true;
49 
50   if (auto EC = dumpFileHeaders())
51     return EC;
52 
53   if (auto EC = dumpStreamMetadata())
54     return EC;
55 
56   if (auto EC = dumpStreamDirectory())
57     return EC;
58 
59   if (auto EC = dumpStringTable())
60     return EC;
61 
62   if (auto EC = dumpPDBStream())
63     return EC;
64 
65   if (auto EC = dumpDbiStream())
66     return EC;
67 
68   if (auto EC = dumpTpiStream())
69     return EC;
70 
71   if (auto EC = dumpIpiStream())
72     return EC;
73 
74   if (auto EC = dumpPublics())
75     return EC;
76 
77   // Fake Coff header for dumping register enumerations.
78   COFF::header Header;
79   auto MachineType =
80       Obj.DbiStream ? Obj.DbiStream->MachineType : PDB_Machine::Unknown;
81   Header.Machine = static_cast<uint16_t>(MachineType);
82   Out.setContext(&Header);
83   flush();
84   Out.setContext(nullptr);
85 
86   return Error::success();
87 }
88 
89 
dumpFileHeaders()90 Error YAMLOutputStyle::dumpFileHeaders() {
91   if (opts::pdb2yaml::NoFileHeaders)
92     return Error::success();
93 
94   yaml::MSFHeaders Headers;
95   Obj.Headers.emplace();
96   Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount();
97   Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex();
98   Obj.Headers->SuperBlock.BlockSize = File.getBlockSize();
99   auto Blocks = File.getDirectoryBlockArray();
100   Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end());
101   Obj.Headers->NumDirectoryBlocks = File.getNumDirectoryBlocks();
102   Obj.Headers->SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes();
103   Obj.Headers->NumStreams =
104       opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0;
105   Obj.Headers->SuperBlock.FreeBlockMapBlock = File.getFreeBlockMapBlock();
106   Obj.Headers->SuperBlock.Unknown1 = File.getUnknown1();
107   Obj.Headers->FileSize = File.getFileSize();
108 
109   return Error::success();
110 }
111 
dumpStringTable()112 Error YAMLOutputStyle::dumpStringTable() {
113   bool RequiresStringTable = opts::pdb2yaml::DumpModuleFiles ||
114                              !opts::pdb2yaml::DumpModuleSubsections.empty();
115   bool RequestedStringTable = opts::pdb2yaml::StringTable;
116   if (!RequiresStringTable && !RequestedStringTable)
117     return Error::success();
118 
119   auto ExpectedST = File.getStringTable();
120   if (!ExpectedST)
121     return ExpectedST.takeError();
122 
123   Obj.StringTable.emplace();
124   const auto &ST = ExpectedST.get();
125   for (auto ID : ST.name_ids()) {
126     auto S = ST.getStringForID(ID);
127     if (!S)
128       return S.takeError();
129     if (S->empty())
130       continue;
131     Obj.StringTable->push_back(*S);
132   }
133   return Error::success();
134 }
135 
dumpStreamMetadata()136 Error YAMLOutputStyle::dumpStreamMetadata() {
137   if (!opts::pdb2yaml::StreamMetadata)
138     return Error::success();
139 
140   Obj.StreamSizes.emplace();
141   Obj.StreamSizes->assign(File.getStreamSizes().begin(),
142                           File.getStreamSizes().end());
143   return Error::success();
144 }
145 
dumpStreamDirectory()146 Error YAMLOutputStyle::dumpStreamDirectory() {
147   if (!opts::pdb2yaml::StreamDirectory)
148     return Error::success();
149 
150   auto StreamMap = File.getStreamMap();
151   Obj.StreamMap.emplace();
152   for (auto &Stream : StreamMap) {
153     pdb::yaml::StreamBlockList BlockList;
154     BlockList.Blocks.assign(Stream.begin(), Stream.end());
155     Obj.StreamMap->push_back(BlockList);
156   }
157 
158   return Error::success();
159 }
160 
dumpPDBStream()161 Error YAMLOutputStyle::dumpPDBStream() {
162   if (!opts::pdb2yaml::PdbStream)
163     return Error::success();
164 
165   auto IS = File.getPDBInfoStream();
166   if (!IS)
167     return IS.takeError();
168 
169   auto &InfoS = IS.get();
170   Obj.PdbStream.emplace();
171   Obj.PdbStream->Age = InfoS.getAge();
172   Obj.PdbStream->Guid = InfoS.getGuid();
173   Obj.PdbStream->Signature = InfoS.getSignature();
174   Obj.PdbStream->Version = InfoS.getVersion();
175   Obj.PdbStream->Features = InfoS.getFeatureSignatures();
176 
177   return Error::success();
178 }
179 
convertSubsectionKind(DebugSubsectionKind K)180 static opts::ModuleSubsection convertSubsectionKind(DebugSubsectionKind K) {
181   switch (K) {
182   case DebugSubsectionKind::CrossScopeExports:
183     return opts::ModuleSubsection::CrossScopeExports;
184   case DebugSubsectionKind::CrossScopeImports:
185     return opts::ModuleSubsection::CrossScopeImports;
186   case DebugSubsectionKind::FileChecksums:
187     return opts::ModuleSubsection::FileChecksums;
188   case DebugSubsectionKind::InlineeLines:
189     return opts::ModuleSubsection::InlineeLines;
190   case DebugSubsectionKind::Lines:
191     return opts::ModuleSubsection::Lines;
192   case DebugSubsectionKind::Symbols:
193     return opts::ModuleSubsection::Symbols;
194   case DebugSubsectionKind::StringTable:
195     return opts::ModuleSubsection::StringTable;
196   case DebugSubsectionKind::FrameData:
197     return opts::ModuleSubsection::FrameData;
198   default:
199     return opts::ModuleSubsection::Unknown;
200   }
201   llvm_unreachable("Unreachable!");
202 }
203 
dumpDbiStream()204 Error YAMLOutputStyle::dumpDbiStream() {
205   if (!opts::pdb2yaml::DbiStream)
206     return Error::success();
207 
208   if (!File.hasPDBDbiStream())
209     return Error::success();
210 
211   auto DbiS = File.getPDBDbiStream();
212   if (!DbiS)
213     return DbiS.takeError();
214 
215   auto &DS = DbiS.get();
216   Obj.DbiStream.emplace();
217   Obj.DbiStream->Age = DS.getAge();
218   Obj.DbiStream->BuildNumber = DS.getBuildNumber();
219   Obj.DbiStream->Flags = DS.getFlags();
220   Obj.DbiStream->MachineType = DS.getMachineType();
221   Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld();
222   Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion();
223   Obj.DbiStream->VerHeader = DS.getDbiVersion();
224   if (opts::pdb2yaml::DumpModules) {
225     const auto &Modules = DS.modules();
226     for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) {
227       DbiModuleDescriptor MI = Modules.getModuleDescriptor(I);
228 
229       Obj.DbiStream->ModInfos.emplace_back();
230       yaml::PdbDbiModuleInfo &DMI = Obj.DbiStream->ModInfos.back();
231 
232       DMI.Mod = MI.getModuleName();
233       DMI.Obj = MI.getObjFileName();
234       if (opts::pdb2yaml::DumpModuleFiles) {
235         auto Files = Modules.source_files(I);
236         DMI.SourceFiles.assign(Files.begin(), Files.end());
237       }
238 
239       uint16_t ModiStream = MI.getModuleStreamIndex();
240       if (ModiStream == kInvalidStreamIndex)
241         continue;
242 
243       auto ModStreamData = File.createIndexedStream(ModiStream);
244       pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData));
245       if (auto EC = ModS.reload())
246         return EC;
247 
248       auto ExpectedST = File.getStringTable();
249       if (!ExpectedST)
250         return ExpectedST.takeError();
251       if (!opts::pdb2yaml::DumpModuleSubsections.empty() &&
252           ModS.hasDebugSubsections()) {
253         auto ExpectedChecksums = ModS.findChecksumsSubsection();
254         if (!ExpectedChecksums)
255           return ExpectedChecksums.takeError();
256 
257         StringsAndChecksumsRef SC(ExpectedST->getStringTable(),
258                                   *ExpectedChecksums);
259 
260         for (const auto &SS : ModS.subsections()) {
261           opts::ModuleSubsection OptionKind = convertSubsectionKind(SS.kind());
262           if (!checkModuleSubsection(OptionKind))
263             continue;
264 
265           auto Converted =
266               CodeViewYAML::YAMLDebugSubsection::fromCodeViewSubection(SC, SS);
267           if (!Converted)
268             return Converted.takeError();
269           DMI.Subsections.push_back(*Converted);
270         }
271       }
272 
273       if (opts::pdb2yaml::DumpModuleSyms) {
274         DMI.Modi.emplace();
275 
276         DMI.Modi->Signature = ModS.signature();
277         bool HadError = false;
278         for (auto &Sym : ModS.symbols(&HadError)) {
279           auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Sym);
280           if (!ES)
281             return ES.takeError();
282 
283           DMI.Modi->Symbols.push_back(*ES);
284         }
285       }
286     }
287   }
288   return Error::success();
289 }
290 
dumpTpiStream()291 Error YAMLOutputStyle::dumpTpiStream() {
292   if (!opts::pdb2yaml::TpiStream)
293     return Error::success();
294 
295   auto TpiS = File.getPDBTpiStream();
296   if (!TpiS)
297     return TpiS.takeError();
298 
299   auto &TS = TpiS.get();
300   Obj.TpiStream.emplace();
301   Obj.TpiStream->Version = TS.getTpiVersion();
302   for (auto &Record : TS.types(nullptr)) {
303     auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record);
304     if (!ExpectedRecord)
305       return ExpectedRecord.takeError();
306     Obj.TpiStream->Records.push_back(*ExpectedRecord);
307   }
308 
309   return Error::success();
310 }
311 
dumpIpiStream()312 Error YAMLOutputStyle::dumpIpiStream() {
313   if (!opts::pdb2yaml::IpiStream)
314     return Error::success();
315 
316   auto InfoS = File.getPDBInfoStream();
317   if (!InfoS)
318     return InfoS.takeError();
319   if (!InfoS->containsIdStream())
320     return Error::success();
321 
322   auto IpiS = File.getPDBIpiStream();
323   if (!IpiS)
324     return IpiS.takeError();
325 
326   auto &IS = IpiS.get();
327   Obj.IpiStream.emplace();
328   Obj.IpiStream->Version = IS.getTpiVersion();
329   for (auto &Record : IS.types(nullptr)) {
330     auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record);
331     if (!ExpectedRecord)
332       return ExpectedRecord.takeError();
333 
334     Obj.IpiStream->Records.push_back(*ExpectedRecord);
335   }
336 
337   return Error::success();
338 }
339 
dumpPublics()340 Error YAMLOutputStyle::dumpPublics() {
341   if (!opts::pdb2yaml::PublicsStream)
342     return Error::success();
343 
344   Obj.PublicsStream.emplace();
345   auto ExpectedPublics = File.getPDBPublicsStream();
346   if (!ExpectedPublics) {
347     llvm::consumeError(ExpectedPublics.takeError());
348     return Error::success();
349   }
350 
351   PublicsStream &Publics = *ExpectedPublics;
352   const GSIHashTable &PublicsTable = Publics.getPublicsTable();
353 
354   auto ExpectedSyms = File.getPDBSymbolStream();
355   if (!ExpectedSyms) {
356     llvm::consumeError(ExpectedSyms.takeError());
357     return Error::success();
358   }
359 
360   BinaryStreamRef SymStream =
361       ExpectedSyms->getSymbolArray().getUnderlyingStream();
362   for (uint32_t PubSymOff : PublicsTable) {
363     Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
364     if (!Sym)
365       return Sym.takeError();
366     auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(*Sym);
367     if (!ES)
368       return ES.takeError();
369 
370     Obj.PublicsStream->PubSyms.push_back(*ES);
371   }
372 
373   return Error::success();
374 }
375 
flush()376 void YAMLOutputStyle::flush() {
377   Out << Obj;
378   outs().flush();
379 }
380