1 //===- CodeViewYAMLDebugSections.cpp - CodeView YAMLIO debug sections -----===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines classes for handling the YAML representation of CodeView
13 //===----------------------------------------------------------------------===//
15 #include "llvm/ObjectYAML/CodeViewYAMLDebugSections.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/StringSwitch.h"
19 #include "llvm/DebugInfo/CodeView/CodeViewError.h"
20 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
21 #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
22 #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
23 #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"
24 #include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h"
25 #include "llvm/DebugInfo/CodeView/EnumTables.h"
26 #include "llvm/DebugInfo/CodeView/SymbolRecord.h"
29 using namespace llvm::codeview;
30 using namespace llvm::CodeViewYAML;
31 using namespace llvm::CodeViewYAML::detail;
32 using namespace llvm::yaml;
34 LLVM_YAML_IS_SEQUENCE_VECTOR(SourceFileChecksumEntry)
35 LLVM_YAML_IS_SEQUENCE_VECTOR(SourceLineEntry)
36 LLVM_YAML_IS_SEQUENCE_VECTOR(SourceColumnEntry)
37 LLVM_YAML_IS_SEQUENCE_VECTOR(SourceLineBlock)
38 LLVM_YAML_IS_SEQUENCE_VECTOR(SourceLineInfo)
39 LLVM_YAML_IS_SEQUENCE_VECTOR(InlineeSite)
40 LLVM_YAML_IS_SEQUENCE_VECTOR(InlineeInfo)
41 LLVM_YAML_IS_SEQUENCE_VECTOR(StringRef)
43 LLVM_YAML_DECLARE_SCALAR_TRAITS(HexFormattedString, false)
44 LLVM_YAML_DECLARE_ENUM_TRAITS(DebugSubsectionKind)
45 LLVM_YAML_DECLARE_ENUM_TRAITS(FileChecksumKind)
46 LLVM_YAML_DECLARE_BITSET_TRAITS(LineFlags)
48 LLVM_YAML_DECLARE_MAPPING_TRAITS(SourceLineEntry)
49 LLVM_YAML_DECLARE_MAPPING_TRAITS(SourceColumnEntry)
50 LLVM_YAML_DECLARE_MAPPING_TRAITS(SourceFileChecksumEntry)
51 LLVM_YAML_DECLARE_MAPPING_TRAITS(SourceLineBlock)
52 LLVM_YAML_DECLARE_MAPPING_TRAITS(InlineeSite)
55 namespace CodeViewYAML {
57 struct YAMLSubsectionBase {
58 explicit YAMLSubsectionBase(DebugSubsectionKind Kind) : Kind(Kind) {}
59 DebugSubsectionKind Kind;
60 virtual ~YAMLSubsectionBase() {}
62 virtual void map(IO &IO) = 0;
63 virtual std::unique_ptr<DebugSubsection>
64 toCodeViewSubsection(DebugStringTableSubsection *UseStrings,
65 DebugChecksumsSubsection *UseChecksums) const = 0;
72 struct YAMLChecksumsSubsection : public YAMLSubsectionBase {
73 YAMLChecksumsSubsection()
74 : YAMLSubsectionBase(DebugSubsectionKind::FileChecksums) {}
76 void map(IO &IO) override;
77 std::unique_ptr<DebugSubsection>
78 toCodeViewSubsection(DebugStringTableSubsection *Strings,
79 DebugChecksumsSubsection *Checksums) const override;
80 static Expected<std::shared_ptr<YAMLChecksumsSubsection>>
81 fromCodeViewSubsection(const DebugStringTableSubsectionRef &Strings,
82 const DebugChecksumsSubsectionRef &FC);
84 std::vector<SourceFileChecksumEntry> Checksums;
87 struct YAMLLinesSubsection : public YAMLSubsectionBase {
88 YAMLLinesSubsection() : YAMLSubsectionBase(DebugSubsectionKind::Lines) {}
90 void map(IO &IO) override;
91 std::unique_ptr<DebugSubsection>
92 toCodeViewSubsection(DebugStringTableSubsection *Strings,
93 DebugChecksumsSubsection *Checksums) const override;
94 static Expected<std::shared_ptr<YAMLLinesSubsection>>
95 fromCodeViewSubsection(const DebugStringTableSubsectionRef &Strings,
96 const DebugChecksumsSubsectionRef &Checksums,
97 const DebugLinesSubsectionRef &Lines);
102 struct YAMLInlineeLinesSubsection : public YAMLSubsectionBase {
103 YAMLInlineeLinesSubsection()
104 : YAMLSubsectionBase(DebugSubsectionKind::InlineeLines) {}
106 void map(IO &IO) override;
107 std::unique_ptr<DebugSubsection>
108 toCodeViewSubsection(DebugStringTableSubsection *Strings,
109 DebugChecksumsSubsection *Checksums) const override;
110 static Expected<std::shared_ptr<YAMLInlineeLinesSubsection>>
111 fromCodeViewSubsection(const DebugStringTableSubsectionRef &Strings,
112 const DebugChecksumsSubsectionRef &Checksums,
113 const DebugInlineeLinesSubsectionRef &Lines);
115 InlineeInfo InlineeLines;
119 void ScalarBitSetTraits<LineFlags>::bitset(IO &io, LineFlags &Flags) {
120 io.bitSetCase(Flags, "HasColumnInfo", LF_HaveColumns);
121 io.enumFallback<Hex16>(Flags);
124 void ScalarEnumerationTraits<FileChecksumKind>::enumeration(
125 IO &io, FileChecksumKind &Kind) {
126 io.enumCase(Kind, "None", FileChecksumKind::None);
127 io.enumCase(Kind, "MD5", FileChecksumKind::MD5);
128 io.enumCase(Kind, "SHA1", FileChecksumKind::SHA1);
129 io.enumCase(Kind, "SHA256", FileChecksumKind::SHA256);
132 void ScalarTraits<HexFormattedString>::output(const HexFormattedString &Value,
133 void *ctx, raw_ostream &Out) {
134 StringRef Bytes(reinterpret_cast<const char *>(Value.Bytes.data()),
139 StringRef ScalarTraits<HexFormattedString>::input(StringRef Scalar, void *ctxt,
140 HexFormattedString &Value) {
141 std::string H = fromHex(Scalar);
142 Value.Bytes.assign(H.begin(), H.end());
146 void MappingTraits<SourceLineEntry>::mapping(IO &IO, SourceLineEntry &Obj) {
147 IO.mapRequired("Offset", Obj.Offset);
148 IO.mapRequired("LineStart", Obj.LineStart);
149 IO.mapRequired("IsStatement", Obj.IsStatement);
150 IO.mapRequired("EndDelta", Obj.EndDelta);
153 void MappingTraits<SourceColumnEntry>::mapping(IO &IO, SourceColumnEntry &Obj) {
154 IO.mapRequired("StartColumn", Obj.StartColumn);
155 IO.mapRequired("EndColumn", Obj.EndColumn);
158 void MappingTraits<SourceLineBlock>::mapping(IO &IO, SourceLineBlock &Obj) {
159 IO.mapRequired("FileName", Obj.FileName);
160 IO.mapRequired("Lines", Obj.Lines);
161 IO.mapRequired("Columns", Obj.Columns);
164 void MappingTraits<SourceFileChecksumEntry>::mapping(
165 IO &IO, SourceFileChecksumEntry &Obj) {
166 IO.mapRequired("FileName", Obj.FileName);
167 IO.mapRequired("Kind", Obj.Kind);
168 IO.mapRequired("Checksum", Obj.ChecksumBytes);
171 void MappingTraits<InlineeSite>::mapping(IO &IO, InlineeSite &Obj) {
172 IO.mapRequired("FileName", Obj.FileName);
173 IO.mapRequired("LineNum", Obj.SourceLineNum);
174 IO.mapRequired("Inlinee", Obj.Inlinee);
175 IO.mapOptional("ExtraFiles", Obj.ExtraFiles);
178 void YAMLChecksumsSubsection::map(IO &IO) {
179 IO.mapTag("!FileChecksums", true);
180 IO.mapRequired("Checksums", Checksums);
183 void YAMLLinesSubsection::map(IO &IO) {
184 IO.mapTag("!Lines", true);
185 IO.mapRequired("CodeSize", Lines.CodeSize);
187 IO.mapRequired("Flags", Lines.Flags);
188 IO.mapRequired("RelocOffset", Lines.RelocOffset);
189 IO.mapRequired("RelocSegment", Lines.RelocSegment);
190 IO.mapRequired("Blocks", Lines.Blocks);
193 void YAMLInlineeLinesSubsection::map(IO &IO) {
194 IO.mapTag("!InlineeLines", true);
195 IO.mapRequired("HasExtraFiles", InlineeLines.HasExtraFiles);
196 IO.mapRequired("Sites", InlineeLines.Sites);
199 void MappingTraits<YAMLDebugSubsection>::mapping(
200 IO &IO, YAMLDebugSubsection &Subsection) {
201 if (!IO.outputting()) {
202 if (IO.mapTag("!FileChecksums")) {
203 auto SS = std::make_shared<YAMLChecksumsSubsection>();
204 Subsection.Subsection = SS;
205 } else if (IO.mapTag("!Lines")) {
206 Subsection.Subsection = std::make_shared<YAMLLinesSubsection>();
207 } else if (IO.mapTag("!InlineeLines")) {
208 Subsection.Subsection = std::make_shared<YAMLInlineeLinesSubsection>();
210 llvm_unreachable("Unexpected subsection tag!");
213 Subsection.Subsection->map(IO);
216 static Expected<const YAMLChecksumsSubsection &>
217 findChecksums(ArrayRef<YAMLDebugSubsection> Subsections) {
218 for (const auto &SS : Subsections) {
219 if (SS.Subsection->Kind == DebugSubsectionKind::FileChecksums) {
220 return static_cast<const YAMLChecksumsSubsection &>(*SS.Subsection);
223 return make_error<CodeViewError>(cv_error_code::no_records);
226 std::unique_ptr<DebugSubsection> YAMLChecksumsSubsection::toCodeViewSubsection(
227 DebugStringTableSubsection *UseStrings,
228 DebugChecksumsSubsection *UseChecksums) const {
229 assert(UseStrings && !UseChecksums);
230 auto Result = llvm::make_unique<DebugChecksumsSubsection>(*UseStrings);
231 for (const auto &CS : Checksums) {
232 Result->addChecksum(CS.FileName, CS.Kind, CS.ChecksumBytes.Bytes);
234 return std::move(Result);
237 std::unique_ptr<DebugSubsection> YAMLLinesSubsection::toCodeViewSubsection(
238 DebugStringTableSubsection *UseStrings,
239 DebugChecksumsSubsection *UseChecksums) const {
240 assert(UseStrings && UseChecksums);
242 llvm::make_unique<DebugLinesSubsection>(*UseChecksums, *UseStrings);
243 Result->setCodeSize(Lines.CodeSize);
244 Result->setRelocationAddress(Lines.RelocSegment, Lines.RelocOffset);
245 Result->setFlags(Lines.Flags);
246 for (const auto &LC : Lines.Blocks) {
247 Result->createBlock(LC.FileName);
248 if (Result->hasColumnInfo()) {
249 for (const auto &Item : zip(LC.Lines, LC.Columns)) {
250 auto &L = std::get<0>(Item);
251 auto &C = std::get<1>(Item);
252 uint32_t LE = L.LineStart + L.EndDelta;
253 Result->addLineAndColumnInfo(L.Offset,
254 LineInfo(L.LineStart, LE, L.IsStatement),
255 C.StartColumn, C.EndColumn);
258 for (const auto &L : LC.Lines) {
259 uint32_t LE = L.LineStart + L.EndDelta;
260 Result->addLineInfo(L.Offset, LineInfo(L.LineStart, LE, L.IsStatement));
264 return llvm::cast<DebugSubsection>(std::move(Result));
267 std::unique_ptr<DebugSubsection>
268 YAMLInlineeLinesSubsection::toCodeViewSubsection(
269 DebugStringTableSubsection *UseStrings,
270 DebugChecksumsSubsection *UseChecksums) const {
271 assert(UseChecksums);
272 auto Result = llvm::make_unique<DebugInlineeLinesSubsection>(
273 *UseChecksums, InlineeLines.HasExtraFiles);
275 for (const auto &Site : InlineeLines.Sites) {
276 Result->addInlineSite(TypeIndex(Site.Inlinee), Site.FileName,
278 if (!InlineeLines.HasExtraFiles)
281 for (auto EF : Site.ExtraFiles) {
282 Result->addExtraFile(EF);
285 return llvm::cast<DebugSubsection>(std::move(Result));
288 static Expected<SourceFileChecksumEntry>
289 convertOneChecksum(const DebugStringTableSubsectionRef &Strings,
290 const FileChecksumEntry &CS) {
291 auto ExpectedString = Strings.getString(CS.FileNameOffset);
293 return ExpectedString.takeError();
295 SourceFileChecksumEntry Result;
296 Result.ChecksumBytes.Bytes = CS.Checksum;
297 Result.Kind = CS.Kind;
298 Result.FileName = *ExpectedString;
302 static Expected<StringRef>
303 getFileName(const DebugStringTableSubsectionRef &Strings,
304 const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) {
305 auto Iter = Checksums.getArray().at(FileID);
306 if (Iter == Checksums.getArray().end())
307 return make_error<CodeViewError>(cv_error_code::no_records);
308 uint32_t Offset = Iter->FileNameOffset;
309 return Strings.getString(Offset);
312 Expected<std::shared_ptr<YAMLChecksumsSubsection>>
313 YAMLChecksumsSubsection::fromCodeViewSubsection(
314 const DebugStringTableSubsectionRef &Strings,
315 const DebugChecksumsSubsectionRef &FC) {
316 auto Result = std::make_shared<YAMLChecksumsSubsection>();
318 for (const auto &CS : FC) {
319 auto ConvertedCS = convertOneChecksum(Strings, CS);
321 return ConvertedCS.takeError();
322 Result->Checksums.push_back(*ConvertedCS);
327 Expected<std::shared_ptr<YAMLLinesSubsection>>
328 YAMLLinesSubsection::fromCodeViewSubsection(
329 const DebugStringTableSubsectionRef &Strings,
330 const DebugChecksumsSubsectionRef &Checksums,
331 const DebugLinesSubsectionRef &Lines) {
332 auto Result = std::make_shared<YAMLLinesSubsection>();
333 Result->Lines.CodeSize = Lines.header()->CodeSize;
334 Result->Lines.RelocOffset = Lines.header()->RelocOffset;
335 Result->Lines.RelocSegment = Lines.header()->RelocSegment;
336 Result->Lines.Flags = static_cast<LineFlags>(uint16_t(Lines.header()->Flags));
337 for (const auto &L : Lines) {
338 SourceLineBlock Block;
339 auto EF = getFileName(Strings, Checksums, L.NameIndex);
341 return EF.takeError();
342 Block.FileName = *EF;
343 if (Lines.hasColumnInfo()) {
344 for (const auto &C : L.Columns) {
345 SourceColumnEntry SCE;
346 SCE.EndColumn = C.EndColumn;
347 SCE.StartColumn = C.StartColumn;
348 Block.Columns.push_back(SCE);
351 for (const auto &LN : L.LineNumbers) {
353 LineInfo LI(LN.Flags);
354 SLE.Offset = LN.Offset;
355 SLE.LineStart = LI.getStartLine();
356 SLE.EndDelta = LI.getLineDelta();
357 SLE.IsStatement = LI.isStatement();
358 Block.Lines.push_back(SLE);
360 Result->Lines.Blocks.push_back(Block);
365 Expected<std::shared_ptr<YAMLInlineeLinesSubsection>>
366 YAMLInlineeLinesSubsection::fromCodeViewSubsection(
367 const DebugStringTableSubsectionRef &Strings,
368 const DebugChecksumsSubsectionRef &Checksums,
369 const DebugInlineeLinesSubsectionRef &Lines) {
370 auto Result = std::make_shared<YAMLInlineeLinesSubsection>();
372 Result->InlineeLines.HasExtraFiles = Lines.hasExtraFiles();
373 for (const auto &IL : Lines) {
375 auto ExpF = getFileName(Strings, Checksums, IL.Header->FileID);
377 return ExpF.takeError();
378 Site.FileName = *ExpF;
379 Site.Inlinee = IL.Header->Inlinee.getIndex();
380 Site.SourceLineNum = IL.Header->SourceLineNum;
381 if (Lines.hasExtraFiles()) {
382 for (const auto EF : IL.ExtraFiles) {
383 auto ExpF2 = getFileName(Strings, Checksums, EF);
385 return ExpF2.takeError();
386 Site.ExtraFiles.push_back(*ExpF2);
389 Result->InlineeLines.Sites.push_back(Site);
394 Expected<std::vector<std::unique_ptr<DebugSubsection>>>
395 llvm::CodeViewYAML::convertSubsectionList(
396 ArrayRef<YAMLDebugSubsection> Subsections,
397 DebugStringTableSubsection &Strings) {
398 std::vector<std::unique_ptr<DebugSubsection>> Result;
399 if (Subsections.empty())
400 return std::move(Result);
402 auto Checksums = findChecksums(Subsections);
404 return Checksums.takeError();
405 auto ChecksumsBase = Checksums->toCodeViewSubsection(&Strings, nullptr);
406 DebugChecksumsSubsection &CS =
407 llvm::cast<DebugChecksumsSubsection>(*ChecksumsBase);
408 for (const auto &SS : Subsections) {
409 // We've already converted the checksums subsection, don't do it
411 std::unique_ptr<DebugSubsection> CVS;
412 if (SS.Subsection->Kind == DebugSubsectionKind::FileChecksums)
413 CVS = std::move(ChecksumsBase);
415 CVS = SS.Subsection->toCodeViewSubsection(&Strings, &CS);
416 Result.push_back(std::move(CVS));
418 return std::move(Result);
422 struct SubsectionConversionVisitor : public DebugSubsectionVisitor {
423 explicit SubsectionConversionVisitor(
424 const DebugStringTableSubsectionRef &Strings,
425 const DebugChecksumsSubsectionRef &Checksums)
426 : Strings(Strings), Checksums(Checksums) {}
428 Error visitUnknown(DebugUnknownSubsectionRef &Unknown) override;
429 Error visitLines(DebugLinesSubsectionRef &Lines) override;
430 Error visitFileChecksums(DebugChecksumsSubsectionRef &Checksums) override;
431 Error visitInlineeLines(DebugInlineeLinesSubsectionRef &Inlinees) override;
433 YAMLDebugSubsection Subsection;
436 const DebugStringTableSubsectionRef &Strings;
437 const DebugChecksumsSubsectionRef &Checksums;
440 Error SubsectionConversionVisitor::visitUnknown(
441 DebugUnknownSubsectionRef &Unknown) {
442 return make_error<CodeViewError>(cv_error_code::operation_unsupported);
445 Error SubsectionConversionVisitor::visitLines(DebugLinesSubsectionRef &Lines) {
447 YAMLLinesSubsection::fromCodeViewSubsection(Strings, Checksums, Lines);
449 return Result.takeError();
450 Subsection.Subsection = *Result;
451 return Error::success();
454 Error SubsectionConversionVisitor::visitFileChecksums(
455 DebugChecksumsSubsectionRef &Checksums) {
457 YAMLChecksumsSubsection::fromCodeViewSubsection(Strings, Checksums);
459 return Result.takeError();
460 Subsection.Subsection = *Result;
461 return Error::success();
464 Error SubsectionConversionVisitor::visitInlineeLines(
465 DebugInlineeLinesSubsectionRef &Inlinees) {
466 auto Result = YAMLInlineeLinesSubsection::fromCodeViewSubsection(
467 Strings, Checksums, Inlinees);
469 return Result.takeError();
470 Subsection.Subsection = *Result;
471 return Error::success();
475 Expected<YAMLDebugSubsection> YAMLDebugSubsection::fromCodeViewSubection(
476 const DebugStringTableSubsectionRef &Strings,
477 const DebugChecksumsSubsectionRef &Checksums,
478 const DebugSubsectionRecord &SS) {
479 SubsectionConversionVisitor V(Strings, Checksums);
480 if (auto EC = visitDebugSubsection(SS, V))
481 return std::move(EC);