1 //===------ dwarf2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/BinaryFormat/Dwarf.h"
10 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
11 #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
12 #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h"
13 #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h"
14 #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
15 #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h"
16 #include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
17 #include "llvm/DebugInfo/DWARF/DWARFSection.h"
18 #include "llvm/ObjectYAML/DWARFYAML.h"
25 Error
dumpDebugAbbrev(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
26 auto AbbrevSetPtr
= DCtx
.getDebugAbbrev();
28 uint64_t AbbrevTableID
= 0;
29 if (Error Err
= AbbrevSetPtr
->parse())
31 for (const auto &AbbrvDeclSet
: *AbbrevSetPtr
) {
32 Y
.DebugAbbrev
.emplace_back();
33 Y
.DebugAbbrev
.back().ID
= AbbrevTableID
++;
34 for (const DWARFAbbreviationDeclaration
&AbbrvDecl
:
35 AbbrvDeclSet
.second
) {
36 DWARFYAML::Abbrev Abbrv
;
37 Abbrv
.Code
= AbbrvDecl
.getCode();
38 Abbrv
.Tag
= AbbrvDecl
.getTag();
39 Abbrv
.Children
= AbbrvDecl
.hasChildren() ? dwarf::DW_CHILDREN_yes
40 : dwarf::DW_CHILDREN_no
;
41 for (auto Attribute
: AbbrvDecl
.attributes()) {
42 DWARFYAML::AttributeAbbrev AttAbrv
;
43 AttAbrv
.Attribute
= Attribute
.Attr
;
44 AttAbrv
.Form
= Attribute
.Form
;
45 if (AttAbrv
.Form
== dwarf::DW_FORM_implicit_const
)
46 AttAbrv
.Value
= Attribute
.getImplicitConstValue();
47 Abbrv
.Attributes
.push_back(AttAbrv
);
49 Y
.DebugAbbrev
.back().Table
.push_back(Abbrv
);
53 return Error::success();
56 Error
dumpDebugAddr(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
57 DWARFDebugAddrTable AddrTable
;
58 DWARFDataExtractor
AddrData(DCtx
.getDWARFObj(),
59 DCtx
.getDWARFObj().getAddrSection(),
60 DCtx
.isLittleEndian(), /*AddressSize=*/0);
61 std::vector
<DWARFYAML::AddrTableEntry
> AddrTables
;
63 while (AddrData
.isValidOffset(Offset
)) {
64 // We ignore any errors that don't prevent parsing the section, since we can
65 // still represent such sections.
66 if (Error Err
= AddrTable
.extractV5(AddrData
, &Offset
, /*CUAddrSize=*/0,
69 AddrTables
.emplace_back();
70 for (uint64_t Addr
: AddrTable
.getAddressEntries()) {
71 // Currently, the parser doesn't support parsing an address table with non
72 // linear addresses (segment_selector_size != 0). The segment selectors
73 // are specified to be zero.
74 AddrTables
.back().SegAddrPairs
.push_back(
75 {/*SegmentSelector=*/0, /*Address=*/Addr
});
78 AddrTables
.back().Format
= AddrTable
.getFormat();
79 AddrTables
.back().Length
= AddrTable
.getLength();
80 AddrTables
.back().Version
= AddrTable
.getVersion();
81 AddrTables
.back().AddrSize
= AddrTable
.getAddressSize();
82 AddrTables
.back().SegSelectorSize
= AddrTable
.getSegmentSelectorSize();
84 Y
.DebugAddr
= std::move(AddrTables
);
85 return Error::success();
88 Error
dumpDebugStrings(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
89 DataExtractor StrData
= DCtx
.getStringExtractor();
91 std::vector
<StringRef
> DebugStr
;
92 Error Err
= Error::success();
93 while (StrData
.isValidOffset(Offset
)) {
94 const char *CStr
= StrData
.getCStr(&Offset
, &Err
);
97 DebugStr
.push_back(CStr
);
100 Y
.DebugStrings
= DebugStr
;
104 Error
dumpDebugARanges(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
105 DWARFDataExtractor
ArangesData(DCtx
.getDWARFObj().getArangesSection(),
106 DCtx
.isLittleEndian(), 0);
108 DWARFDebugArangeSet Set
;
109 std::vector
<DWARFYAML::ARange
> DebugAranges
;
111 // We ignore any errors that don't prevent parsing the section, since we can
112 // still represent such sections. These errors are recorded via the
113 // WarningHandler parameter of Set.extract().
114 auto DiscardError
= [](Error Err
) { consumeError(std::move(Err
)); };
116 while (ArangesData
.isValidOffset(Offset
)) {
117 if (Error E
= Set
.extract(ArangesData
, &Offset
, DiscardError
))
119 DWARFYAML::ARange Range
;
120 Range
.Format
= Set
.getHeader().Format
;
121 Range
.Length
= Set
.getHeader().Length
;
122 Range
.Version
= Set
.getHeader().Version
;
123 Range
.CuOffset
= Set
.getHeader().CuOffset
;
124 Range
.AddrSize
= Set
.getHeader().AddrSize
;
125 Range
.SegSize
= Set
.getHeader().SegSize
;
126 for (auto Descriptor
: Set
.descriptors()) {
127 DWARFYAML::ARangeDescriptor Desc
;
128 Desc
.Address
= Descriptor
.Address
;
129 Desc
.Length
= Descriptor
.Length
;
130 Range
.Descriptors
.push_back(Desc
);
132 DebugAranges
.push_back(Range
);
135 Y
.DebugAranges
= DebugAranges
;
136 return ErrorSuccess();
139 Error
dumpDebugRanges(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
140 // We are assuming all address byte sizes will be consistent across all
142 uint8_t AddrSize
= 0;
143 for (const auto &CU
: DCtx
.compile_units()) {
144 const uint8_t CUAddrSize
= CU
->getAddressByteSize();
146 AddrSize
= CUAddrSize
;
147 else if (CUAddrSize
!= AddrSize
)
148 return createStringError(std::errc::invalid_argument
,
149 "address sizes vary in different compile units");
152 DWARFDataExtractor
Data(DCtx
.getDWARFObj().getRangesSection().Data
,
153 DCtx
.isLittleEndian(), AddrSize
);
155 DWARFDebugRangeList DwarfRanges
;
156 std::vector
<DWARFYAML::Ranges
> DebugRanges
;
158 while (Data
.isValidOffset(Offset
)) {
159 DWARFYAML::Ranges YamlRanges
;
160 YamlRanges
.Offset
= Offset
;
161 YamlRanges
.AddrSize
= AddrSize
;
162 if (Error E
= DwarfRanges
.extract(Data
, &Offset
))
164 for (const auto &RLE
: DwarfRanges
.getEntries())
165 YamlRanges
.Entries
.push_back({RLE
.StartAddress
, RLE
.EndAddress
});
166 DebugRanges
.push_back(std::move(YamlRanges
));
169 Y
.DebugRanges
= DebugRanges
;
170 return ErrorSuccess();
173 static std::optional
<DWARFYAML::PubSection
>
174 dumpPubSection(const DWARFContext
&DCtx
, const DWARFSection
&Section
,
176 DWARFYAML::PubSection Y
;
177 DWARFDataExtractor
PubSectionData(DCtx
.getDWARFObj(), Section
,
178 DCtx
.isLittleEndian(), 0);
179 DWARFDebugPubTable Table
;
180 // We ignore any errors that don't prevent parsing the section, since we can
181 // still represent such sections.
182 Table
.extract(PubSectionData
, IsGNUStyle
,
183 [](Error Err
) { consumeError(std::move(Err
)); });
184 ArrayRef
<DWARFDebugPubTable::Set
> Sets
= Table
.getData();
188 // FIXME: Currently, obj2yaml only supports dumping the first pubtable.
189 Y
.Format
= Sets
[0].Format
;
190 Y
.Length
= Sets
[0].Length
;
191 Y
.Version
= Sets
[0].Version
;
192 Y
.UnitOffset
= Sets
[0].Offset
;
193 Y
.UnitSize
= Sets
[0].Size
;
195 for (const DWARFDebugPubTable::Entry
&E
: Sets
[0].Entries
)
196 Y
.Entries
.push_back(DWARFYAML::PubEntry
{(uint32_t)E
.SecOffset
,
197 E
.Descriptor
.toBits(), E
.Name
});
202 void dumpDebugPubSections(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
203 const DWARFObject
&D
= DCtx
.getDWARFObj();
206 dumpPubSection(DCtx
, D
.getPubnamesSection(), /*IsGNUStyle=*/false);
208 dumpPubSection(DCtx
, D
.getPubtypesSection(), /*IsGNUStyle=*/false);
209 // TODO: Test dumping .debug_gnu_pubnames section.
211 dumpPubSection(DCtx
, D
.getGnuPubnamesSection(), /*IsGNUStyle=*/true);
212 // TODO: Test dumping .debug_gnu_pubtypes section.
214 dumpPubSection(DCtx
, D
.getGnuPubtypesSection(), /*IsGNUStyle=*/true);
217 void dumpDebugInfo(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
218 for (const auto &CU
: DCtx
.compile_units()) {
219 DWARFYAML::Unit NewUnit
;
220 NewUnit
.Format
= CU
->getFormat();
221 NewUnit
.Length
= CU
->getLength();
222 NewUnit
.Version
= CU
->getVersion();
223 if (NewUnit
.Version
>= 5)
224 NewUnit
.Type
= (dwarf::UnitType
)CU
->getUnitType();
225 const DWARFDebugAbbrev
*DebugAbbrev
= DCtx
.getDebugAbbrev();
226 // FIXME: Ideally we would propagate this error upwards, but that would
227 // prevent us from displaying any debug info at all. For now we just consume
228 // the error and display everything that was parsed successfully.
229 if (Error Err
= DebugAbbrev
->parse())
230 llvm::consumeError(std::move(Err
));
232 NewUnit
.AbbrevTableID
= std::distance(
233 DebugAbbrev
->begin(),
236 [&](const std::pair
<uint64_t, DWARFAbbreviationDeclarationSet
> &P
) {
237 return P
.first
== CU
->getAbbreviations()->getOffset();
239 NewUnit
.AbbrOffset
= CU
->getAbbreviations()->getOffset();
240 NewUnit
.AddrSize
= CU
->getAddressByteSize();
241 for (auto DIE
: CU
->dies()) {
242 DWARFYAML::Entry NewEntry
;
243 DataExtractor EntryData
= CU
->getDebugInfoExtractor();
244 uint64_t offset
= DIE
.getOffset();
246 assert(EntryData
.isValidOffset(offset
) && "Invalid DIE Offset");
247 if (!EntryData
.isValidOffset(offset
))
250 NewEntry
.AbbrCode
= EntryData
.getULEB128(&offset
);
252 auto AbbrevDecl
= DIE
.getAbbreviationDeclarationPtr();
254 for (const auto &AttrSpec
: AbbrevDecl
->attributes()) {
255 DWARFYAML::FormValue NewValue
;
256 NewValue
.Value
= 0xDEADBEEFDEADBEEF;
257 DWARFDie
DIEWrapper(CU
.get(), &DIE
);
258 auto FormValue
= DIEWrapper
.find(AttrSpec
.Attr
);
261 auto Form
= FormValue
->getForm();
262 bool indirect
= false;
266 case dwarf::DW_FORM_addr
:
267 case dwarf::DW_FORM_GNU_addr_index
:
268 if (auto Val
= FormValue
->getAsAddress())
269 NewValue
.Value
= *Val
;
271 case dwarf::DW_FORM_ref_addr
:
272 case dwarf::DW_FORM_ref1
:
273 case dwarf::DW_FORM_ref2
:
274 case dwarf::DW_FORM_ref4
:
275 case dwarf::DW_FORM_ref8
:
276 case dwarf::DW_FORM_ref_udata
:
277 case dwarf::DW_FORM_ref_sig8
:
278 if (auto Val
= FormValue
->getAsReferenceUVal())
279 NewValue
.Value
= *Val
;
281 case dwarf::DW_FORM_exprloc
:
282 case dwarf::DW_FORM_block
:
283 case dwarf::DW_FORM_block1
:
284 case dwarf::DW_FORM_block2
:
285 case dwarf::DW_FORM_block4
:
286 if (auto Val
= FormValue
->getAsBlock()) {
287 auto BlockData
= *Val
;
288 std::copy(BlockData
.begin(), BlockData
.end(),
289 std::back_inserter(NewValue
.BlockData
));
291 NewValue
.Value
= NewValue
.BlockData
.size();
293 case dwarf::DW_FORM_data1
:
294 case dwarf::DW_FORM_flag
:
295 case dwarf::DW_FORM_data2
:
296 case dwarf::DW_FORM_data4
:
297 case dwarf::DW_FORM_data8
:
298 case dwarf::DW_FORM_sdata
:
299 case dwarf::DW_FORM_udata
:
300 case dwarf::DW_FORM_ref_sup4
:
301 case dwarf::DW_FORM_ref_sup8
:
302 if (auto Val
= FormValue
->getAsUnsignedConstant())
303 NewValue
.Value
= *Val
;
305 case dwarf::DW_FORM_string
:
306 if (auto Val
= dwarf::toString(FormValue
))
307 NewValue
.CStr
= *Val
;
309 case dwarf::DW_FORM_indirect
:
311 if (auto Val
= FormValue
->getAsUnsignedConstant()) {
312 NewValue
.Value
= *Val
;
313 NewEntry
.Values
.push_back(NewValue
);
314 Form
= static_cast<dwarf::Form
>(*Val
);
317 case dwarf::DW_FORM_strp
:
318 case dwarf::DW_FORM_sec_offset
:
319 case dwarf::DW_FORM_GNU_ref_alt
:
320 case dwarf::DW_FORM_GNU_strp_alt
:
321 case dwarf::DW_FORM_line_strp
:
322 case dwarf::DW_FORM_strp_sup
:
323 case dwarf::DW_FORM_GNU_str_index
:
324 case dwarf::DW_FORM_strx
:
325 if (auto Val
= FormValue
->getAsCStringOffset())
326 NewValue
.Value
= *Val
;
328 case dwarf::DW_FORM_flag_present
:
335 NewEntry
.Values
.push_back(NewValue
);
339 NewUnit
.Entries
.push_back(NewEntry
);
341 Y
.Units
.push_back(NewUnit
);
345 bool dumpFileEntry(DataExtractor
&Data
, uint64_t &Offset
,
346 DWARFYAML::File
&File
) {
347 File
.Name
= Data
.getCStr(&Offset
);
348 if (File
.Name
.empty())
350 File
.DirIdx
= Data
.getULEB128(&Offset
);
351 File
.ModTime
= Data
.getULEB128(&Offset
);
352 File
.Length
= Data
.getULEB128(&Offset
);
356 void dumpDebugLines(DWARFContext
&DCtx
, DWARFYAML::Data
&Y
) {
357 for (const auto &CU
: DCtx
.compile_units()) {
358 auto CUDIE
= CU
->getUnitDIE();
361 if (auto StmtOffset
=
362 dwarf::toSectionOffset(CUDIE
.find(dwarf::DW_AT_stmt_list
))) {
363 DWARFYAML::LineTable DebugLines
;
364 DataExtractor
LineData(DCtx
.getDWARFObj().getLineSection().Data
,
365 DCtx
.isLittleEndian(), CU
->getAddressByteSize());
366 uint64_t Offset
= *StmtOffset
;
367 uint64_t LengthOrDWARF64Prefix
= LineData
.getU32(&Offset
);
368 if (LengthOrDWARF64Prefix
== dwarf::DW_LENGTH_DWARF64
) {
369 DebugLines
.Format
= dwarf::DWARF64
;
370 DebugLines
.Length
= LineData
.getU64(&Offset
);
372 DebugLines
.Format
= dwarf::DWARF32
;
373 DebugLines
.Length
= LengthOrDWARF64Prefix
;
375 assert(DebugLines
.Length
);
376 uint64_t LineTableLength
= *DebugLines
.Length
;
377 uint64_t SizeOfPrologueLength
=
378 DebugLines
.Format
== dwarf::DWARF64
? 8 : 4;
379 DebugLines
.Version
= LineData
.getU16(&Offset
);
380 DebugLines
.PrologueLength
=
381 LineData
.getUnsigned(&Offset
, SizeOfPrologueLength
);
382 assert(DebugLines
.PrologueLength
);
383 const uint64_t EndPrologue
= *DebugLines
.PrologueLength
+ Offset
;
385 DebugLines
.MinInstLength
= LineData
.getU8(&Offset
);
386 if (DebugLines
.Version
>= 4)
387 DebugLines
.MaxOpsPerInst
= LineData
.getU8(&Offset
);
388 DebugLines
.DefaultIsStmt
= LineData
.getU8(&Offset
);
389 DebugLines
.LineBase
= LineData
.getU8(&Offset
);
390 DebugLines
.LineRange
= LineData
.getU8(&Offset
);
391 DebugLines
.OpcodeBase
= LineData
.getU8(&Offset
);
393 DebugLines
.StandardOpcodeLengths
.emplace();
394 for (uint8_t i
= 1; i
< DebugLines
.OpcodeBase
; ++i
)
395 DebugLines
.StandardOpcodeLengths
->push_back(LineData
.getU8(&Offset
));
397 while (Offset
< EndPrologue
) {
398 StringRef Dir
= LineData
.getCStr(&Offset
);
400 DebugLines
.IncludeDirs
.push_back(Dir
);
405 while (Offset
< EndPrologue
) {
406 DWARFYAML::File TmpFile
;
407 if (dumpFileEntry(LineData
, Offset
, TmpFile
))
408 DebugLines
.Files
.push_back(TmpFile
);
413 const uint64_t LineEnd
=
414 LineTableLength
+ *StmtOffset
+ SizeOfPrologueLength
;
415 while (Offset
< LineEnd
) {
416 DWARFYAML::LineTableOpcode NewOp
= {};
417 NewOp
.Opcode
= (dwarf::LineNumberOps
)LineData
.getU8(&Offset
);
418 if (NewOp
.Opcode
== 0) {
419 auto StartExt
= Offset
;
420 NewOp
.ExtLen
= LineData
.getULEB128(&Offset
);
422 (dwarf::LineNumberExtendedOps
)LineData
.getU8(&Offset
);
423 switch (NewOp
.SubOpcode
) {
424 case dwarf::DW_LNE_set_address
:
425 case dwarf::DW_LNE_set_discriminator
:
426 NewOp
.Data
= LineData
.getAddress(&Offset
);
428 case dwarf::DW_LNE_define_file
:
429 dumpFileEntry(LineData
, Offset
, NewOp
.FileEntry
);
431 case dwarf::DW_LNE_end_sequence
:
434 while (Offset
< StartExt
+ *NewOp
.ExtLen
)
435 NewOp
.UnknownOpcodeData
.push_back(LineData
.getU8(&Offset
));
437 } else if (NewOp
.Opcode
< *DebugLines
.OpcodeBase
) {
438 switch (NewOp
.Opcode
) {
439 case dwarf::DW_LNS_copy
:
440 case dwarf::DW_LNS_negate_stmt
:
441 case dwarf::DW_LNS_set_basic_block
:
442 case dwarf::DW_LNS_const_add_pc
:
443 case dwarf::DW_LNS_set_prologue_end
:
444 case dwarf::DW_LNS_set_epilogue_begin
:
447 case dwarf::DW_LNS_advance_pc
:
448 case dwarf::DW_LNS_set_file
:
449 case dwarf::DW_LNS_set_column
:
450 case dwarf::DW_LNS_set_isa
:
451 NewOp
.Data
= LineData
.getULEB128(&Offset
);
454 case dwarf::DW_LNS_advance_line
:
455 NewOp
.SData
= LineData
.getSLEB128(&Offset
);
458 case dwarf::DW_LNS_fixed_advance_pc
:
459 NewOp
.Data
= LineData
.getU16(&Offset
);
464 i
< (*DebugLines
.StandardOpcodeLengths
)[NewOp
.Opcode
- 1]; ++i
)
465 NewOp
.StandardOpcodeData
.push_back(LineData
.getULEB128(&Offset
));
468 DebugLines
.Opcodes
.push_back(NewOp
);
470 Y
.DebugLines
.push_back(DebugLines
);