1 //===------ dwarf2yaml.cpp - obj2yaml conversion tool -----------*- 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 "Error.h"
10 #include "llvm/BinaryFormat/Dwarf.h"
11 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
12 #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h"
13 #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h"
14 #include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
15 #include "llvm/DebugInfo/DWARF/DWARFSection.h"
16 #include "llvm/ObjectYAML/DWARFYAML.h"
17 
18 #include <algorithm>
19 
20 using namespace llvm;
21 
22 void dumpInitialLength(DataExtractor &Data, uint64_t &Offset,
23                        DWARFYAML::InitialLength &InitialLength) {
24   InitialLength.TotalLength = Data.getU32(&Offset);
25   if (InitialLength.isDWARF64())
26     InitialLength.TotalLength64 = Data.getU64(&Offset);
27 }
28 
29 void dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) {
30   auto AbbrevSetPtr = DCtx.getDebugAbbrev();
31   if (AbbrevSetPtr) {
32     for (auto AbbrvDeclSet : *AbbrevSetPtr) {
33       for (auto AbbrvDecl : AbbrvDeclSet.second) {
34         DWARFYAML::Abbrev Abbrv;
35         Abbrv.Code = AbbrvDecl.getCode();
36         Abbrv.Tag = AbbrvDecl.getTag();
37         Abbrv.Children = AbbrvDecl.hasChildren() ? dwarf::DW_CHILDREN_yes
38                                                  : dwarf::DW_CHILDREN_no;
39         for (auto Attribute : AbbrvDecl.attributes()) {
40           DWARFYAML::AttributeAbbrev AttAbrv;
41           AttAbrv.Attribute = Attribute.Attr;
42           AttAbrv.Form = Attribute.Form;
43           if (AttAbrv.Form == dwarf::DW_FORM_implicit_const)
44             AttAbrv.Value = Attribute.getImplicitConstValue();
45           Abbrv.Attributes.push_back(AttAbrv);
46         }
47         Y.AbbrevDecls.push_back(Abbrv);
48       }
49     }
50   }
51 }
52 
53 void dumpDebugStrings(DWARFContext &DCtx, DWARFYAML::Data &Y) {
54   StringRef RemainingTable = DCtx.getDWARFObj().getStrSection();
55   while (RemainingTable.size() > 0) {
56     auto SymbolPair = RemainingTable.split('\0');
57     RemainingTable = SymbolPair.second;
58     Y.DebugStrings.push_back(SymbolPair.first);
59   }
60 }
61 
62 Error dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {
63   DWARFDataExtractor ArangesData(DCtx.getDWARFObj().getArangesSection(),
64                                  DCtx.isLittleEndian(), 0);
65   uint64_t Offset = 0;
66   DWARFDebugArangeSet Set;
67   std::vector<DWARFYAML::ARange> DebugAranges;
68   while (ArangesData.isValidOffset(Offset)) {
69     if (Error E = Set.extract(ArangesData, &Offset))
70       return E;
71     DWARFYAML::ARange Range;
72     Range.Format = Set.getHeader().Format;
73     Range.Length = Set.getHeader().Length;
74     Range.Version = Set.getHeader().Version;
75     Range.CuOffset = Set.getHeader().CuOffset;
76     Range.AddrSize = Set.getHeader().AddrSize;
77     Range.SegSize = Set.getHeader().SegSize;
78     for (auto Descriptor : Set.descriptors()) {
79       DWARFYAML::ARangeDescriptor Desc;
80       Desc.Address = Descriptor.Address;
81       Desc.Length = Descriptor.Length;
82       Range.Descriptors.push_back(Desc);
83     }
84     DebugAranges.push_back(Range);
85   }
86 
87   Y.DebugAranges = DebugAranges;
88   return ErrorSuccess();
89 }
90 
91 Error dumpDebugRanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {
92   // We are assuming all address byte sizes will be consistent across all
93   // compile units.
94   uint8_t AddrSize = 0;
95   for (const auto &CU : DCtx.compile_units()) {
96     const uint8_t CUAddrSize = CU->getAddressByteSize();
97     if (AddrSize == 0)
98       AddrSize = CUAddrSize;
99     else if (CUAddrSize != AddrSize)
100       return createStringError(std::errc::invalid_argument,
101                                "address sizes vary in different compile units");
102   }
103 
104   DWARFDataExtractor Data(DCtx.getDWARFObj().getRangesSection().Data,
105                           DCtx.isLittleEndian(), AddrSize);
106   uint64_t Offset = 0;
107   DWARFDebugRangeList DwarfRanges;
108 
109   while (Data.isValidOffset(Offset)) {
110     DWARFYAML::Ranges YamlRanges;
111     YamlRanges.Offset = Offset;
112     YamlRanges.AddrSize = AddrSize;
113     if (Error E = DwarfRanges.extract(Data, &Offset))
114       return E;
115     for (const auto &RLE : DwarfRanges.getEntries())
116       YamlRanges.Entries.push_back({RLE.StartAddress, RLE.EndAddress});
117     Y.DebugRanges.push_back(std::move(YamlRanges));
118   }
119   return ErrorSuccess();
120 }
121 
122 static DWARFYAML::PubSection dumpPubSection(const DWARFContext &DCtx,
123                                             const DWARFSection &Section,
124                                             bool IsGNUStyle) {
125   DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section,
126                                     DCtx.isLittleEndian(), 0);
127   DWARFYAML::PubSection Y;
128   uint64_t Offset = 0;
129   dumpInitialLength(PubSectionData, Offset, Y.Length);
130   Y.Version = PubSectionData.getU16(&Offset);
131   Y.UnitOffset = PubSectionData.getU32(&Offset);
132   Y.UnitSize = PubSectionData.getU32(&Offset);
133   while (Offset < Y.Length.getLength()) {
134     DWARFYAML::PubEntry NewEntry;
135     NewEntry.DieOffset = PubSectionData.getU32(&Offset);
136     if (IsGNUStyle)
137       NewEntry.Descriptor = PubSectionData.getU8(&Offset);
138     NewEntry.Name = PubSectionData.getCStr(&Offset);
139     Y.Entries.push_back(NewEntry);
140   }
141 
142   return Y;
143 }
144 
145 void dumpDebugPubSections(DWARFContext &DCtx, DWARFYAML::Data &Y) {
146   const DWARFObject &D = DCtx.getDWARFObj();
147 
148   const DWARFSection PubNames = D.getPubnamesSection();
149   if (!PubNames.Data.empty())
150     Y.PubNames = dumpPubSection(DCtx, PubNames, /*IsGNUStyle=*/false);
151 
152   const DWARFSection PubTypes = D.getPubtypesSection();
153   if (!PubTypes.Data.empty())
154     Y.PubTypes = dumpPubSection(DCtx, PubTypes, /*IsGNUStyle=*/false);
155 
156   const DWARFSection GNUPubNames = D.getGnuPubnamesSection();
157   if (!GNUPubNames.Data.empty())
158     // TODO: Test dumping .debug_gnu_pubnames section.
159     Y.GNUPubNames = dumpPubSection(DCtx, GNUPubNames, /*IsGNUStyle=*/true);
160 
161   const DWARFSection GNUPubTypes = D.getGnuPubtypesSection();
162   if (!GNUPubTypes.Data.empty())
163     // TODO: Test dumping .debug_gnu_pubtypes section.
164     Y.GNUPubTypes = dumpPubSection(DCtx, GNUPubTypes, /*IsGNUStyle=*/true);
165 }
166 
167 void dumpDebugInfo(DWARFContext &DCtx, DWARFYAML::Data &Y) {
168   for (const auto &CU : DCtx.compile_units()) {
169     DWARFYAML::Unit NewUnit;
170     NewUnit.FormParams.Format = CU->getFormat();
171     NewUnit.Length = CU->getLength();
172     NewUnit.FormParams.Version = CU->getVersion();
173     if (NewUnit.FormParams.Version >= 5)
174       NewUnit.Type = (dwarf::UnitType)CU->getUnitType();
175     NewUnit.AbbrOffset = CU->getAbbreviations()->getOffset();
176     NewUnit.FormParams.AddrSize = CU->getAddressByteSize();
177     for (auto DIE : CU->dies()) {
178       DWARFYAML::Entry NewEntry;
179       DataExtractor EntryData = CU->getDebugInfoExtractor();
180       uint64_t offset = DIE.getOffset();
181 
182       assert(EntryData.isValidOffset(offset) && "Invalid DIE Offset");
183       if (!EntryData.isValidOffset(offset))
184         continue;
185 
186       NewEntry.AbbrCode = EntryData.getULEB128(&offset);
187 
188       auto AbbrevDecl = DIE.getAbbreviationDeclarationPtr();
189       if (AbbrevDecl) {
190         for (const auto &AttrSpec : AbbrevDecl->attributes()) {
191           DWARFYAML::FormValue NewValue;
192           NewValue.Value = 0xDEADBEEFDEADBEEF;
193           DWARFDie DIEWrapper(CU.get(), &DIE);
194           auto FormValue = DIEWrapper.find(AttrSpec.Attr);
195           if (!FormValue)
196             return;
197           auto Form = FormValue.getValue().getForm();
198           bool indirect = false;
199           do {
200             indirect = false;
201             switch (Form) {
202             case dwarf::DW_FORM_addr:
203             case dwarf::DW_FORM_GNU_addr_index:
204               if (auto Val = FormValue.getValue().getAsAddress())
205                 NewValue.Value = Val.getValue();
206               break;
207             case dwarf::DW_FORM_ref_addr:
208             case dwarf::DW_FORM_ref1:
209             case dwarf::DW_FORM_ref2:
210             case dwarf::DW_FORM_ref4:
211             case dwarf::DW_FORM_ref8:
212             case dwarf::DW_FORM_ref_udata:
213             case dwarf::DW_FORM_ref_sig8:
214               if (auto Val = FormValue.getValue().getAsReferenceUVal())
215                 NewValue.Value = Val.getValue();
216               break;
217             case dwarf::DW_FORM_exprloc:
218             case dwarf::DW_FORM_block:
219             case dwarf::DW_FORM_block1:
220             case dwarf::DW_FORM_block2:
221             case dwarf::DW_FORM_block4:
222               if (auto Val = FormValue.getValue().getAsBlock()) {
223                 auto BlockData = Val.getValue();
224                 std::copy(BlockData.begin(), BlockData.end(),
225                           std::back_inserter(NewValue.BlockData));
226               }
227               NewValue.Value = NewValue.BlockData.size();
228               break;
229             case dwarf::DW_FORM_data1:
230             case dwarf::DW_FORM_flag:
231             case dwarf::DW_FORM_data2:
232             case dwarf::DW_FORM_data4:
233             case dwarf::DW_FORM_data8:
234             case dwarf::DW_FORM_sdata:
235             case dwarf::DW_FORM_udata:
236             case dwarf::DW_FORM_ref_sup4:
237             case dwarf::DW_FORM_ref_sup8:
238               if (auto Val = FormValue.getValue().getAsUnsignedConstant())
239                 NewValue.Value = Val.getValue();
240               break;
241             case dwarf::DW_FORM_string:
242               if (auto Val = FormValue.getValue().getAsCString())
243                 NewValue.CStr = Val.getValue();
244               break;
245             case dwarf::DW_FORM_indirect:
246               indirect = true;
247               if (auto Val = FormValue.getValue().getAsUnsignedConstant()) {
248                 NewValue.Value = Val.getValue();
249                 NewEntry.Values.push_back(NewValue);
250                 Form = static_cast<dwarf::Form>(Val.getValue());
251               }
252               break;
253             case dwarf::DW_FORM_strp:
254             case dwarf::DW_FORM_sec_offset:
255             case dwarf::DW_FORM_GNU_ref_alt:
256             case dwarf::DW_FORM_GNU_strp_alt:
257             case dwarf::DW_FORM_line_strp:
258             case dwarf::DW_FORM_strp_sup:
259             case dwarf::DW_FORM_GNU_str_index:
260             case dwarf::DW_FORM_strx:
261               if (auto Val = FormValue.getValue().getAsCStringOffset())
262                 NewValue.Value = Val.getValue();
263               break;
264             case dwarf::DW_FORM_flag_present:
265               NewValue.Value = 1;
266               break;
267             default:
268               break;
269             }
270           } while (indirect);
271           NewEntry.Values.push_back(NewValue);
272         }
273       }
274 
275       NewUnit.Entries.push_back(NewEntry);
276     }
277     Y.CompileUnits.push_back(NewUnit);
278   }
279 }
280 
281 bool dumpFileEntry(DataExtractor &Data, uint64_t &Offset,
282                    DWARFYAML::File &File) {
283   File.Name = Data.getCStr(&Offset);
284   if (File.Name.empty())
285     return false;
286   File.DirIdx = Data.getULEB128(&Offset);
287   File.ModTime = Data.getULEB128(&Offset);
288   File.Length = Data.getULEB128(&Offset);
289   return true;
290 }
291 
292 void dumpDebugLines(DWARFContext &DCtx, DWARFYAML::Data &Y) {
293   for (const auto &CU : DCtx.compile_units()) {
294     auto CUDIE = CU->getUnitDIE();
295     if (!CUDIE)
296       continue;
297     if (auto StmtOffset =
298             dwarf::toSectionOffset(CUDIE.find(dwarf::DW_AT_stmt_list))) {
299       DWARFYAML::LineTable DebugLines;
300       DataExtractor LineData(DCtx.getDWARFObj().getLineSection().Data,
301                              DCtx.isLittleEndian(), CU->getAddressByteSize());
302       uint64_t Offset = *StmtOffset;
303       uint64_t LengthOrDWARF64Prefix = LineData.getU32(&Offset);
304       if (LengthOrDWARF64Prefix == dwarf::DW_LENGTH_DWARF64) {
305         DebugLines.Format = dwarf::DWARF64;
306         DebugLines.Length = LineData.getU64(&Offset);
307       } else {
308         DebugLines.Format = dwarf::DWARF32;
309         DebugLines.Length = LengthOrDWARF64Prefix;
310       }
311       uint64_t LineTableLength = DebugLines.Length;
312       uint64_t SizeOfPrologueLength =
313           DebugLines.Format == dwarf::DWARF64 ? 8 : 4;
314       DebugLines.Version = LineData.getU16(&Offset);
315       DebugLines.PrologueLength =
316           LineData.getUnsigned(&Offset, SizeOfPrologueLength);
317       const uint64_t EndPrologue = DebugLines.PrologueLength + Offset;
318 
319       DebugLines.MinInstLength = LineData.getU8(&Offset);
320       if (DebugLines.Version >= 4)
321         DebugLines.MaxOpsPerInst = LineData.getU8(&Offset);
322       DebugLines.DefaultIsStmt = LineData.getU8(&Offset);
323       DebugLines.LineBase = LineData.getU8(&Offset);
324       DebugLines.LineRange = LineData.getU8(&Offset);
325       DebugLines.OpcodeBase = LineData.getU8(&Offset);
326 
327       DebugLines.StandardOpcodeLengths.reserve(DebugLines.OpcodeBase - 1);
328       for (uint8_t i = 1; i < DebugLines.OpcodeBase; ++i)
329         DebugLines.StandardOpcodeLengths.push_back(LineData.getU8(&Offset));
330 
331       while (Offset < EndPrologue) {
332         StringRef Dir = LineData.getCStr(&Offset);
333         if (!Dir.empty())
334           DebugLines.IncludeDirs.push_back(Dir);
335         else
336           break;
337       }
338 
339       while (Offset < EndPrologue) {
340         DWARFYAML::File TmpFile;
341         if (dumpFileEntry(LineData, Offset, TmpFile))
342           DebugLines.Files.push_back(TmpFile);
343         else
344           break;
345       }
346 
347       const uint64_t LineEnd =
348           LineTableLength + *StmtOffset + SizeOfPrologueLength;
349       while (Offset < LineEnd) {
350         DWARFYAML::LineTableOpcode NewOp = {};
351         NewOp.Opcode = (dwarf::LineNumberOps)LineData.getU8(&Offset);
352         if (NewOp.Opcode == 0) {
353           auto StartExt = Offset;
354           NewOp.ExtLen = LineData.getULEB128(&Offset);
355           NewOp.SubOpcode =
356               (dwarf::LineNumberExtendedOps)LineData.getU8(&Offset);
357           switch (NewOp.SubOpcode) {
358           case dwarf::DW_LNE_set_address:
359           case dwarf::DW_LNE_set_discriminator:
360             NewOp.Data = LineData.getAddress(&Offset);
361             break;
362           case dwarf::DW_LNE_define_file:
363             dumpFileEntry(LineData, Offset, NewOp.FileEntry);
364             break;
365           case dwarf::DW_LNE_end_sequence:
366             break;
367           default:
368             while (Offset < StartExt + NewOp.ExtLen)
369               NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset));
370           }
371         } else if (NewOp.Opcode < DebugLines.OpcodeBase) {
372           switch (NewOp.Opcode) {
373           case dwarf::DW_LNS_copy:
374           case dwarf::DW_LNS_negate_stmt:
375           case dwarf::DW_LNS_set_basic_block:
376           case dwarf::DW_LNS_const_add_pc:
377           case dwarf::DW_LNS_set_prologue_end:
378           case dwarf::DW_LNS_set_epilogue_begin:
379             break;
380 
381           case dwarf::DW_LNS_advance_pc:
382           case dwarf::DW_LNS_set_file:
383           case dwarf::DW_LNS_set_column:
384           case dwarf::DW_LNS_set_isa:
385             NewOp.Data = LineData.getULEB128(&Offset);
386             break;
387 
388           case dwarf::DW_LNS_advance_line:
389             NewOp.SData = LineData.getSLEB128(&Offset);
390             break;
391 
392           case dwarf::DW_LNS_fixed_advance_pc:
393             NewOp.Data = LineData.getU16(&Offset);
394             break;
395 
396           default:
397             for (uint8_t i = 0;
398                  i < DebugLines.StandardOpcodeLengths[NewOp.Opcode - 1]; ++i)
399               NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset));
400           }
401         }
402         DebugLines.Opcodes.push_back(NewOp);
403       }
404       Y.DebugLines.push_back(DebugLines);
405     }
406   }
407 }
408 
409 llvm::Error dwarf2yaml(DWARFContext &DCtx, DWARFYAML::Data &Y) {
410   dumpDebugAbbrev(DCtx, Y);
411   dumpDebugStrings(DCtx, Y);
412   if (Error E = dumpDebugARanges(DCtx, Y))
413     return E;
414   if (Error E = dumpDebugRanges(DCtx, Y))
415     return E;
416   dumpDebugPubSections(DCtx, Y);
417   dumpDebugInfo(DCtx, Y);
418   dumpDebugLines(DCtx, Y);
419   return ErrorSuccess();
420 }
421