//===------ dwarf2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/ObjectYAML/DWARFYAML.h" #include #include using namespace llvm; Error dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) { auto AbbrevSetPtr = DCtx.getDebugAbbrev(); if (AbbrevSetPtr) { uint64_t AbbrevTableID = 0; if (Error Err = AbbrevSetPtr->parse()) return Err; for (const auto &AbbrvDeclSet : *AbbrevSetPtr) { Y.DebugAbbrev.emplace_back(); Y.DebugAbbrev.back().ID = AbbrevTableID++; for (const DWARFAbbreviationDeclaration &AbbrvDecl : AbbrvDeclSet.second) { DWARFYAML::Abbrev Abbrv; Abbrv.Code = AbbrvDecl.getCode(); Abbrv.Tag = AbbrvDecl.getTag(); Abbrv.Children = AbbrvDecl.hasChildren() ? dwarf::DW_CHILDREN_yes : dwarf::DW_CHILDREN_no; for (auto Attribute : AbbrvDecl.attributes()) { DWARFYAML::AttributeAbbrev AttAbrv; AttAbrv.Attribute = Attribute.Attr; AttAbrv.Form = Attribute.Form; if (AttAbrv.Form == dwarf::DW_FORM_implicit_const) AttAbrv.Value = Attribute.getImplicitConstValue(); Abbrv.Attributes.push_back(AttAbrv); } Y.DebugAbbrev.back().Table.push_back(Abbrv); } } } return Error::success(); } Error dumpDebugAddr(DWARFContext &DCtx, DWARFYAML::Data &Y) { DWARFDebugAddrTable AddrTable; DWARFDataExtractor AddrData(DCtx.getDWARFObj(), DCtx.getDWARFObj().getAddrSection(), DCtx.isLittleEndian(), /*AddressSize=*/0); std::vector AddrTables; uint64_t Offset = 0; while (AddrData.isValidOffset(Offset)) { // We ignore any errors that don't prevent parsing the section, since we can // still represent such sections. if (Error Err = AddrTable.extractV5(AddrData, &Offset, /*CUAddrSize=*/0, consumeError)) return Err; AddrTables.emplace_back(); for (uint64_t Addr : AddrTable.getAddressEntries()) { // Currently, the parser doesn't support parsing an address table with non // linear addresses (segment_selector_size != 0). The segment selectors // are specified to be zero. AddrTables.back().SegAddrPairs.push_back( {/*SegmentSelector=*/0, /*Address=*/Addr}); } AddrTables.back().Format = AddrTable.getFormat(); AddrTables.back().Length = AddrTable.getLength(); AddrTables.back().Version = AddrTable.getVersion(); AddrTables.back().AddrSize = AddrTable.getAddressSize(); AddrTables.back().SegSelectorSize = AddrTable.getSegmentSelectorSize(); } Y.DebugAddr = std::move(AddrTables); return Error::success(); } Error dumpDebugStrings(DWARFContext &DCtx, DWARFYAML::Data &Y) { DataExtractor StrData = DCtx.getStringExtractor(); uint64_t Offset = 0; std::vector DebugStr; Error Err = Error::success(); while (StrData.isValidOffset(Offset)) { const char *CStr = StrData.getCStr(&Offset, &Err); if (Err) return Err; DebugStr.push_back(CStr); } Y.DebugStrings = DebugStr; return Err; } Error dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { DWARFDataExtractor ArangesData(DCtx.getDWARFObj().getArangesSection(), DCtx.isLittleEndian(), 0); uint64_t Offset = 0; DWARFDebugArangeSet Set; std::vector DebugAranges; // We ignore any errors that don't prevent parsing the section, since we can // still represent such sections. These errors are recorded via the // WarningHandler parameter of Set.extract(). auto DiscardError = [](Error Err) { consumeError(std::move(Err)); }; while (ArangesData.isValidOffset(Offset)) { if (Error E = Set.extract(ArangesData, &Offset, DiscardError)) return E; DWARFYAML::ARange Range; Range.Format = Set.getHeader().Format; Range.Length = Set.getHeader().Length; Range.Version = Set.getHeader().Version; Range.CuOffset = Set.getHeader().CuOffset; Range.AddrSize = Set.getHeader().AddrSize; Range.SegSize = Set.getHeader().SegSize; for (auto Descriptor : Set.descriptors()) { DWARFYAML::ARangeDescriptor Desc; Desc.Address = Descriptor.Address; Desc.Length = Descriptor.Length; Range.Descriptors.push_back(Desc); } DebugAranges.push_back(Range); } Y.DebugAranges = DebugAranges; return ErrorSuccess(); } Error dumpDebugRanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { // We are assuming all address byte sizes will be consistent across all // compile units. uint8_t AddrSize = 0; for (const auto &CU : DCtx.compile_units()) { const uint8_t CUAddrSize = CU->getAddressByteSize(); if (AddrSize == 0) AddrSize = CUAddrSize; else if (CUAddrSize != AddrSize) return createStringError(std::errc::invalid_argument, "address sizes vary in different compile units"); } DWARFDataExtractor Data(DCtx.getDWARFObj().getRangesSection().Data, DCtx.isLittleEndian(), AddrSize); uint64_t Offset = 0; DWARFDebugRangeList DwarfRanges; std::vector DebugRanges; while (Data.isValidOffset(Offset)) { DWARFYAML::Ranges YamlRanges; YamlRanges.Offset = Offset; YamlRanges.AddrSize = AddrSize; if (Error E = DwarfRanges.extract(Data, &Offset)) return E; for (const auto &RLE : DwarfRanges.getEntries()) YamlRanges.Entries.push_back({RLE.StartAddress, RLE.EndAddress}); DebugRanges.push_back(std::move(YamlRanges)); } Y.DebugRanges = DebugRanges; return ErrorSuccess(); } static std::optional dumpPubSection(const DWARFContext &DCtx, const DWARFSection &Section, bool IsGNUStyle) { DWARFYAML::PubSection Y; DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section, DCtx.isLittleEndian(), 0); DWARFDebugPubTable Table; // We ignore any errors that don't prevent parsing the section, since we can // still represent such sections. Table.extract(PubSectionData, IsGNUStyle, [](Error Err) { consumeError(std::move(Err)); }); ArrayRef Sets = Table.getData(); if (Sets.empty()) return std::nullopt; // FIXME: Currently, obj2yaml only supports dumping the first pubtable. Y.Format = Sets[0].Format; Y.Length = Sets[0].Length; Y.Version = Sets[0].Version; Y.UnitOffset = Sets[0].Offset; Y.UnitSize = Sets[0].Size; for (const DWARFDebugPubTable::Entry &E : Sets[0].Entries) Y.Entries.push_back(DWARFYAML::PubEntry{(uint32_t)E.SecOffset, E.Descriptor.toBits(), E.Name}); return Y; } void dumpDebugPubSections(DWARFContext &DCtx, DWARFYAML::Data &Y) { const DWARFObject &D = DCtx.getDWARFObj(); Y.PubNames = dumpPubSection(DCtx, D.getPubnamesSection(), /*IsGNUStyle=*/false); Y.PubTypes = dumpPubSection(DCtx, D.getPubtypesSection(), /*IsGNUStyle=*/false); // TODO: Test dumping .debug_gnu_pubnames section. Y.GNUPubNames = dumpPubSection(DCtx, D.getGnuPubnamesSection(), /*IsGNUStyle=*/true); // TODO: Test dumping .debug_gnu_pubtypes section. Y.GNUPubTypes = dumpPubSection(DCtx, D.getGnuPubtypesSection(), /*IsGNUStyle=*/true); } void dumpDebugInfo(DWARFContext &DCtx, DWARFYAML::Data &Y) { for (const auto &CU : DCtx.compile_units()) { DWARFYAML::Unit NewUnit; NewUnit.Format = CU->getFormat(); NewUnit.Length = CU->getLength(); NewUnit.Version = CU->getVersion(); if (NewUnit.Version >= 5) NewUnit.Type = (dwarf::UnitType)CU->getUnitType(); const DWARFDebugAbbrev *DebugAbbrev = DCtx.getDebugAbbrev(); // FIXME: Ideally we would propagate this error upwards, but that would // prevent us from displaying any debug info at all. For now we just consume // the error and display everything that was parsed successfully. if (Error Err = DebugAbbrev->parse()) llvm::consumeError(std::move(Err)); NewUnit.AbbrevTableID = std::distance( DebugAbbrev->begin(), llvm::find_if( *DebugAbbrev, [&](const std::pair &P) { return P.first == CU->getAbbreviations()->getOffset(); })); NewUnit.AbbrOffset = CU->getAbbreviations()->getOffset(); NewUnit.AddrSize = CU->getAddressByteSize(); for (auto DIE : CU->dies()) { DWARFYAML::Entry NewEntry; DataExtractor EntryData = CU->getDebugInfoExtractor(); uint64_t offset = DIE.getOffset(); assert(EntryData.isValidOffset(offset) && "Invalid DIE Offset"); if (!EntryData.isValidOffset(offset)) continue; NewEntry.AbbrCode = EntryData.getULEB128(&offset); auto AbbrevDecl = DIE.getAbbreviationDeclarationPtr(); if (AbbrevDecl) { for (const auto &AttrSpec : AbbrevDecl->attributes()) { DWARFYAML::FormValue NewValue; NewValue.Value = 0xDEADBEEFDEADBEEF; DWARFDie DIEWrapper(CU.get(), &DIE); auto FormValue = DIEWrapper.find(AttrSpec.Attr); if (!FormValue) return; auto Form = FormValue->getForm(); bool indirect = false; do { indirect = false; switch (Form) { case dwarf::DW_FORM_addr: case dwarf::DW_FORM_GNU_addr_index: if (auto Val = FormValue->getAsAddress()) NewValue.Value = *Val; break; case dwarf::DW_FORM_ref_addr: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: case dwarf::DW_FORM_ref4: case dwarf::DW_FORM_ref8: case dwarf::DW_FORM_ref_udata: case dwarf::DW_FORM_ref_sig8: if (auto Val = FormValue->getAsReferenceUVal()) NewValue.Value = *Val; break; case dwarf::DW_FORM_exprloc: case dwarf::DW_FORM_block: case dwarf::DW_FORM_block1: case dwarf::DW_FORM_block2: case dwarf::DW_FORM_block4: if (auto Val = FormValue->getAsBlock()) { auto BlockData = *Val; std::copy(BlockData.begin(), BlockData.end(), std::back_inserter(NewValue.BlockData)); } NewValue.Value = NewValue.BlockData.size(); break; case dwarf::DW_FORM_data1: case dwarf::DW_FORM_flag: case dwarf::DW_FORM_data2: case dwarf::DW_FORM_data4: case dwarf::DW_FORM_data8: case dwarf::DW_FORM_sdata: case dwarf::DW_FORM_udata: case dwarf::DW_FORM_ref_sup4: case dwarf::DW_FORM_ref_sup8: if (auto Val = FormValue->getAsUnsignedConstant()) NewValue.Value = *Val; break; case dwarf::DW_FORM_string: if (auto Val = dwarf::toString(FormValue)) NewValue.CStr = *Val; break; case dwarf::DW_FORM_indirect: indirect = true; if (auto Val = FormValue->getAsUnsignedConstant()) { NewValue.Value = *Val; NewEntry.Values.push_back(NewValue); Form = static_cast(*Val); } break; case dwarf::DW_FORM_strp: case dwarf::DW_FORM_sec_offset: case dwarf::DW_FORM_GNU_ref_alt: case dwarf::DW_FORM_GNU_strp_alt: case dwarf::DW_FORM_line_strp: case dwarf::DW_FORM_strp_sup: case dwarf::DW_FORM_GNU_str_index: case dwarf::DW_FORM_strx: if (auto Val = FormValue->getAsCStringOffset()) NewValue.Value = *Val; break; case dwarf::DW_FORM_flag_present: NewValue.Value = 1; break; default: break; } } while (indirect); NewEntry.Values.push_back(NewValue); } } NewUnit.Entries.push_back(NewEntry); } Y.CompileUnits.push_back(NewUnit); } } bool dumpFileEntry(DataExtractor &Data, uint64_t &Offset, DWARFYAML::File &File) { File.Name = Data.getCStr(&Offset); if (File.Name.empty()) return false; File.DirIdx = Data.getULEB128(&Offset); File.ModTime = Data.getULEB128(&Offset); File.Length = Data.getULEB128(&Offset); return true; } void dumpDebugLines(DWARFContext &DCtx, DWARFYAML::Data &Y) { for (const auto &CU : DCtx.compile_units()) { auto CUDIE = CU->getUnitDIE(); if (!CUDIE) continue; if (auto StmtOffset = dwarf::toSectionOffset(CUDIE.find(dwarf::DW_AT_stmt_list))) { DWARFYAML::LineTable DebugLines; DataExtractor LineData(DCtx.getDWARFObj().getLineSection().Data, DCtx.isLittleEndian(), CU->getAddressByteSize()); uint64_t Offset = *StmtOffset; uint64_t LengthOrDWARF64Prefix = LineData.getU32(&Offset); if (LengthOrDWARF64Prefix == dwarf::DW_LENGTH_DWARF64) { DebugLines.Format = dwarf::DWARF64; DebugLines.Length = LineData.getU64(&Offset); } else { DebugLines.Format = dwarf::DWARF32; DebugLines.Length = LengthOrDWARF64Prefix; } assert(DebugLines.Length); uint64_t LineTableLength = *DebugLines.Length; uint64_t SizeOfPrologueLength = DebugLines.Format == dwarf::DWARF64 ? 8 : 4; DebugLines.Version = LineData.getU16(&Offset); DebugLines.PrologueLength = LineData.getUnsigned(&Offset, SizeOfPrologueLength); assert(DebugLines.PrologueLength); const uint64_t EndPrologue = *DebugLines.PrologueLength + Offset; DebugLines.MinInstLength = LineData.getU8(&Offset); if (DebugLines.Version >= 4) DebugLines.MaxOpsPerInst = LineData.getU8(&Offset); DebugLines.DefaultIsStmt = LineData.getU8(&Offset); DebugLines.LineBase = LineData.getU8(&Offset); DebugLines.LineRange = LineData.getU8(&Offset); DebugLines.OpcodeBase = LineData.getU8(&Offset); DebugLines.StandardOpcodeLengths.emplace(); for (uint8_t i = 1; i < DebugLines.OpcodeBase; ++i) DebugLines.StandardOpcodeLengths->push_back(LineData.getU8(&Offset)); while (Offset < EndPrologue) { StringRef Dir = LineData.getCStr(&Offset); if (!Dir.empty()) DebugLines.IncludeDirs.push_back(Dir); else break; } while (Offset < EndPrologue) { DWARFYAML::File TmpFile; if (dumpFileEntry(LineData, Offset, TmpFile)) DebugLines.Files.push_back(TmpFile); else break; } const uint64_t LineEnd = LineTableLength + *StmtOffset + SizeOfPrologueLength; while (Offset < LineEnd) { DWARFYAML::LineTableOpcode NewOp = {}; NewOp.Opcode = (dwarf::LineNumberOps)LineData.getU8(&Offset); if (NewOp.Opcode == 0) { auto StartExt = Offset; NewOp.ExtLen = LineData.getULEB128(&Offset); NewOp.SubOpcode = (dwarf::LineNumberExtendedOps)LineData.getU8(&Offset); switch (NewOp.SubOpcode) { case dwarf::DW_LNE_set_address: case dwarf::DW_LNE_set_discriminator: NewOp.Data = LineData.getAddress(&Offset); break; case dwarf::DW_LNE_define_file: dumpFileEntry(LineData, Offset, NewOp.FileEntry); break; case dwarf::DW_LNE_end_sequence: break; default: while (Offset < StartExt + *NewOp.ExtLen) NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset)); } } else if (NewOp.Opcode < *DebugLines.OpcodeBase) { switch (NewOp.Opcode) { case dwarf::DW_LNS_copy: case dwarf::DW_LNS_negate_stmt: case dwarf::DW_LNS_set_basic_block: case dwarf::DW_LNS_const_add_pc: case dwarf::DW_LNS_set_prologue_end: case dwarf::DW_LNS_set_epilogue_begin: break; case dwarf::DW_LNS_advance_pc: case dwarf::DW_LNS_set_file: case dwarf::DW_LNS_set_column: case dwarf::DW_LNS_set_isa: NewOp.Data = LineData.getULEB128(&Offset); break; case dwarf::DW_LNS_advance_line: NewOp.SData = LineData.getSLEB128(&Offset); break; case dwarf::DW_LNS_fixed_advance_pc: NewOp.Data = LineData.getU16(&Offset); break; default: for (uint8_t i = 0; i < (*DebugLines.StandardOpcodeLengths)[NewOp.Opcode - 1]; ++i) NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset)); } } DebugLines.Opcodes.push_back(NewOp); } Y.DebugLines.push_back(DebugLines); } } }