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