This makes a bunch of these call sites independent of a follow-up change I'm making to have getAsCString return Expected<const char*> for more descriptive error messages so that the failures there can be communicated up to DWARFVerifier (or other callers who want to provide more verbose diagnostics) so DWARFVerifier doesn't have to re-implement the string lookup logic and error checking.
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 = dwarf::toString(FormValue))
|
|
NewValue.CStr = *Val;
|
|
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);
|
|
}
|
|
}
|
|
}
|