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
30using namespace llvm;
31using namespace llvm::codeview;
32using namespace llvm::pdb;
33
34static bool checkModuleSubsection(opts::ModuleSubsection MS) {
35 return any_of(Range&: opts::pdb2yaml::DumpModuleSubsections,
36 P: [=](opts::ModuleSubsection M) {
37 return M == MS || M == opts::ModuleSubsection::All;
38 });
39}
40
41YAMLOutputStyle::YAMLOutputStyle(PDBFile &File)
42 : File(File), Out(outs()), Obj(File.getAllocator()) {
43 Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal);
44}
45
46Error 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
90Error 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(first: Blocks.begin(), last: 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
112Error 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(x: *S);
132 }
133 return Error::success();
134}
135
136Error YAMLOutputStyle::dumpStreamMetadata() {
137 if (!opts::pdb2yaml::StreamMetadata)
138 return Error::success();
139
140 Obj.StreamSizes.emplace();
141 Obj.StreamSizes->assign(first: File.getStreamSizes().begin(),
142 last: File.getStreamSizes().end());
143 return Error::success();
144}
145
146Error 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(first: Stream.begin(), last: Stream.end());
155 Obj.StreamMap->push_back(x: BlockList);
156 }
157
158 return Error::success();
159}
160
161Error 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
180static 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
204Error 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(Modi: 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(Modi: I);
236 DMI.SourceFiles.assign(first: Files.begin(), last: Files.end());
237 }
238
239 uint16_t ModiStream = MI.getModuleStreamIndex();
240 if (ModiStream == kInvalidStreamIndex)
241 continue;
242
243 auto ModStreamData = File.createIndexedStream(SN: 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(K: SS.kind());
262 if (!checkModuleSubsection(MS: 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(x: *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: &HadError)) {
279 auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Symbol: Sym);
280 if (!ES)
281 return ES.takeError();
282
283 DMI.Modi->Symbols.push_back(x: *ES);
284 }
285 }
286 }
287 }
288 return Error::success();
289}
290
291Error 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(HadError: nullptr)) {
303 auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Type: Record);
304 if (!ExpectedRecord)
305 return ExpectedRecord.takeError();
306 Obj.TpiStream->Records.push_back(x: *ExpectedRecord);
307 }
308
309 return Error::success();
310}
311
312Error 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(HadError: nullptr)) {
330 auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Type: Record);
331 if (!ExpectedRecord)
332 return ExpectedRecord.takeError();
333
334 Obj.IpiStream->Records.push_back(x: *ExpectedRecord);
335 }
336
337 return Error::success();
338}
339
340Error 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(Err: 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(Err: 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(Stream: SymStream, Offset: PubSymOff);
364 if (!Sym)
365 return Sym.takeError();
366 auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Symbol: *Sym);
367 if (!ES)
368 return ES.takeError();
369
370 Obj.PublicsStream->PubSyms.push_back(x: *ES);
371 }
372
373 return Error::success();
374}
375
376void YAMLOutputStyle::flush() {
377 Out << Obj;
378 outs().flush();
379}
380