464 lines
17 KiB
C++
464 lines
17 KiB
C++
//===------ 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/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 <algorithm>
|
|
|
|
using namespace llvm;
|
|
|
|
void dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) {
|
|
auto AbbrevSetPtr = DCtx.getDebugAbbrev();
|
|
if (AbbrevSetPtr) {
|
|
uint64_t AbbrevTableID = 0;
|
|
for (auto AbbrvDeclSet : *AbbrevSetPtr) {
|
|
Y.DebugAbbrev.emplace_back();
|
|
Y.DebugAbbrev.back().ID = AbbrevTableID++;
|
|
for (auto 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Error dumpDebugAddr(DWARFContext &DCtx, DWARFYAML::Data &Y) {
|
|
DWARFDebugAddrTable AddrTable;
|
|
DWARFDataExtractor AddrData(DCtx.getDWARFObj(),
|
|
DCtx.getDWARFObj().getAddrSection(),
|
|
DCtx.isLittleEndian(), /*AddrSize=*/0);
|
|
std::vector<DWARFYAML::AddrTableEntry> 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<StringRef> 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<DWARFYAML::ARange> 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<DWARFYAML::Ranges> 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 Optional<DWARFYAML::PubSection>
|
|
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<DWARFDebugPubTable::Set> Sets = Table.getData();
|
|
if (Sets.empty())
|
|
return None;
|
|
|
|
// 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();
|
|
NewUnit.AbbrevTableID = std::distance(
|
|
DebugAbbrev->begin(),
|
|
llvm::find_if(
|
|
*DebugAbbrev,
|
|
[&](const std::pair<uint64_t, DWARFAbbreviationDeclarationSet> &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.getValue().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.getValue().getAsAddress())
|
|
NewValue.Value = Val.getValue();
|
|
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.getValue().getAsReferenceUVal())
|
|
NewValue.Value = Val.getValue();
|
|
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.getValue().getAsBlock()) {
|
|
auto BlockData = Val.getValue();
|
|
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.getValue().getAsUnsignedConstant())
|
|
NewValue.Value = Val.getValue();
|
|
break;
|
|
case dwarf::DW_FORM_string:
|
|
if (auto Val = FormValue.getValue().getAsCString())
|
|
NewValue.CStr = Val.getValue();
|
|
break;
|
|
case dwarf::DW_FORM_indirect:
|
|
indirect = true;
|
|
if (auto Val = FormValue.getValue().getAsUnsignedConstant()) {
|
|
NewValue.Value = Val.getValue();
|
|
NewEntry.Values.push_back(NewValue);
|
|
Form = static_cast<dwarf::Form>(Val.getValue());
|
|
}
|
|
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.getValue().getAsCStringOffset())
|
|
NewValue.Value = Val.getValue();
|
|
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.getValue()[NewOp.Opcode - 1];
|
|
++i)
|
|
NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset));
|
|
}
|
|
}
|
|
DebugLines.Opcodes.push_back(NewOp);
|
|
}
|
|
Y.DebugLines.push_back(DebugLines);
|
|
}
|
|
}
|
|
}
|