474 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 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"19 20#include <algorithm>21#include <optional>22 23using namespace llvm;24 25Error dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) {26 auto AbbrevSetPtr = DCtx.getDebugAbbrev();27 if (AbbrevSetPtr) {28 uint64_t AbbrevTableID = 0;29 if (Error Err = AbbrevSetPtr->parse())30 return Err;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_yes40 : 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);48 }49 Y.DebugAbbrev.back().Table.push_back(Abbrv);50 }51 }52 }53 return Error::success();54}55 56Error 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;62 uint64_t Offset = 0;63 while (AddrData.isValidOffset(Offset)) {64 // We ignore any errors that don't prevent parsing the section, since we can65 // still represent such sections.66 if (Error Err = AddrTable.extractV5(AddrData, &Offset, /*CUAddrSize=*/0,67 consumeError))68 return Err;69 AddrTables.emplace_back();70 for (uint64_t Addr : AddrTable.getAddressEntries()) {71 // Currently, the parser doesn't support parsing an address table with non72 // linear addresses (segment_selector_size != 0). The segment selectors73 // are specified to be zero.74 AddrTables.back().SegAddrPairs.push_back(75 {/*SegmentSelector=*/0, /*Address=*/Addr});76 }77 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();83 }84 Y.DebugAddr = std::move(AddrTables);85 return Error::success();86}87 88Error dumpDebugStrings(DWARFContext &DCtx, DWARFYAML::Data &Y) {89 DataExtractor StrData = DCtx.getStringExtractor();90 uint64_t Offset = 0;91 std::vector<StringRef> DebugStr;92 Error Err = Error::success();93 while (StrData.isValidOffset(Offset)) {94 const char *CStr = StrData.getCStr(&Offset, &Err);95 if (Err)96 return Err;97 DebugStr.push_back(CStr);98 }99 100 Y.DebugStrings = DebugStr;101 return Err;102}103 104Error dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {105 DWARFDataExtractor ArangesData(DCtx.getDWARFObj().getArangesSection(),106 DCtx.isLittleEndian(), 0);107 uint64_t Offset = 0;108 DWARFDebugArangeSet Set;109 std::vector<DWARFYAML::ARange> DebugAranges;110 111 // We ignore any errors that don't prevent parsing the section, since we can112 // still represent such sections. These errors are recorded via the113 // WarningHandler parameter of Set.extract().114 auto DiscardError = [](Error Err) { consumeError(std::move(Err)); };115 116 while (ArangesData.isValidOffset(Offset)) {117 if (Error E = Set.extract(ArangesData, &Offset, DiscardError))118 return E;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);131 }132 DebugAranges.push_back(Range);133 }134 135 Y.DebugAranges = DebugAranges;136 return ErrorSuccess();137}138 139Error dumpDebugRanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {140 // We are assuming all address byte sizes will be consistent across all141 // compile units.142 uint8_t AddrSize = 0;143 for (const auto &CU : DCtx.compile_units()) {144 const uint8_t CUAddrSize = CU->getAddressByteSize();145 if (AddrSize == 0)146 AddrSize = CUAddrSize;147 else if (CUAddrSize != AddrSize)148 return createStringError(std::errc::invalid_argument,149 "address sizes vary in different compile units");150 }151 152 DWARFDataExtractor Data(DCtx.getDWARFObj().getRangesSection().Data,153 DCtx.isLittleEndian(), AddrSize);154 uint64_t Offset = 0;155 DWARFDebugRangeList DwarfRanges;156 std::vector<DWARFYAML::Ranges> DebugRanges;157 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))163 return E;164 for (const auto &RLE : DwarfRanges.getEntries())165 YamlRanges.Entries.push_back({RLE.StartAddress, RLE.EndAddress});166 DebugRanges.push_back(std::move(YamlRanges));167 }168 169 Y.DebugRanges = DebugRanges;170 return ErrorSuccess();171}172 173static std::optional<DWARFYAML::PubSection>174dumpPubSection(const DWARFContext &DCtx, const DWARFSection &Section,175 bool IsGNUStyle) {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 can181 // still represent such sections.182 Table.extract(PubSectionData, IsGNUStyle,183 [](Error Err) { consumeError(std::move(Err)); });184 ArrayRef<DWARFDebugPubTable::Set> Sets = Table.getData();185 if (Sets.empty())186 return std::nullopt;187 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;194 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});198 199 return Y;200}201 202void dumpDebugPubSections(DWARFContext &DCtx, DWARFYAML::Data &Y) {203 const DWARFObject &D = DCtx.getDWARFObj();204 205 Y.PubNames =206 dumpPubSection(DCtx, D.getPubnamesSection(), /*IsGNUStyle=*/false);207 Y.PubTypes =208 dumpPubSection(DCtx, D.getPubtypesSection(), /*IsGNUStyle=*/false);209 // TODO: Test dumping .debug_gnu_pubnames section.210 Y.GNUPubNames =211 dumpPubSection(DCtx, D.getGnuPubnamesSection(), /*IsGNUStyle=*/true);212 // TODO: Test dumping .debug_gnu_pubtypes section.213 Y.GNUPubTypes =214 dumpPubSection(DCtx, D.getGnuPubtypesSection(), /*IsGNUStyle=*/true);215}216 217void 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 would227 // prevent us from displaying any debug info at all. For now we just consume228 // the error and display everything that was parsed successfully.229 if (Error Err = DebugAbbrev->parse())230 llvm::consumeError(std::move(Err));231 232 NewUnit.AbbrevTableID = std::distance(233 DebugAbbrev->begin(),234 llvm::find_if(235 *DebugAbbrev,236 [&](const std::pair<uint64_t, DWARFAbbreviationDeclarationSet> &P) {237 return P.first == CU->getAbbreviations()->getOffset();238 }));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();245 246 assert(EntryData.isValidOffset(offset) && "Invalid DIE Offset");247 if (!EntryData.isValidOffset(offset))248 continue;249 250 NewEntry.AbbrCode = EntryData.getULEB128(&offset);251 252 auto AbbrevDecl = DIE.getAbbreviationDeclarationPtr();253 if (AbbrevDecl) {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);259 if (!FormValue)260 return;261 auto Form = FormValue->getForm();262 bool indirect = false;263 do {264 indirect = false;265 switch (Form) {266 case dwarf::DW_FORM_addr:267 case dwarf::DW_FORM_GNU_addr_index:268 if (auto Val = FormValue->getAsAddress())269 NewValue.Value = *Val;270 break;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;280 break;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));290 }291 NewValue.Value = NewValue.BlockData.size();292 break;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;304 break;305 case dwarf::DW_FORM_string:306 if (auto Val = dwarf::toString(FormValue))307 NewValue.CStr = *Val;308 break;309 case dwarf::DW_FORM_indirect:310 indirect = true;311 if (auto Val = FormValue->getAsUnsignedConstant()) {312 NewValue.Value = *Val;313 NewEntry.Values.push_back(NewValue);314 Form = static_cast<dwarf::Form>(*Val);315 }316 break;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;327 break;328 case dwarf::DW_FORM_flag_present:329 NewValue.Value = 1;330 break;331 default:332 break;333 }334 } while (indirect);335 NewEntry.Values.push_back(NewValue);336 }337 }338 339 NewUnit.Entries.push_back(NewEntry);340 }341 Y.Units.push_back(NewUnit);342 }343}344 345bool dumpFileEntry(DataExtractor &Data, uint64_t &Offset,346 DWARFYAML::File &File) {347 File.Name = Data.getCStr(&Offset);348 if (File.Name.empty())349 return false;350 File.DirIdx = Data.getULEB128(&Offset);351 File.ModTime = Data.getULEB128(&Offset);352 File.Length = Data.getULEB128(&Offset);353 return true;354}355 356void dumpDebugLines(DWARFContext &DCtx, DWARFYAML::Data &Y) {357 for (const auto &CU : DCtx.compile_units()) {358 auto CUDIE = CU->getUnitDIE();359 if (!CUDIE)360 continue;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);371 } else {372 DebugLines.Format = dwarf::DWARF32;373 DebugLines.Length = LengthOrDWARF64Prefix;374 }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;384 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);392 393 DebugLines.StandardOpcodeLengths.emplace();394 for (uint8_t i = 1; i < DebugLines.OpcodeBase; ++i)395 DebugLines.StandardOpcodeLengths->push_back(LineData.getU8(&Offset));396 397 while (Offset < EndPrologue) {398 StringRef Dir = LineData.getCStr(&Offset);399 if (!Dir.empty())400 DebugLines.IncludeDirs.push_back(Dir);401 else402 break;403 }404 405 while (Offset < EndPrologue) {406 DWARFYAML::File TmpFile;407 if (dumpFileEntry(LineData, Offset, TmpFile))408 DebugLines.Files.push_back(TmpFile);409 else410 break;411 }412 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);421 NewOp.SubOpcode =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);427 break;428 case dwarf::DW_LNE_define_file:429 dumpFileEntry(LineData, Offset, NewOp.FileEntry);430 break;431 case dwarf::DW_LNE_end_sequence:432 break;433 default:434 while (Offset < StartExt + *NewOp.ExtLen)435 NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset));436 }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:445 break;446 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);452 break;453 454 case dwarf::DW_LNS_advance_line:455 NewOp.SData = LineData.getSLEB128(&Offset);456 break;457 458 case dwarf::DW_LNS_fixed_advance_pc:459 NewOp.Data = LineData.getU16(&Offset);460 break;461 462 default:463 for (uint8_t i = 0;464 i < (*DebugLines.StandardOpcodeLengths)[NewOp.Opcode - 1]; ++i)465 NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset));466 }467 }468 DebugLines.Opcodes.push_back(NewOp);469 }470 Y.DebugLines.push_back(DebugLines);471 }472 }473}474