1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00
llvm-mirror/tools/obj2yaml/dwarf2yaml.cpp

464 lines
17 KiB
C++
Raw Normal View History

//===------ 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();
Fix debug_abbrev emitter to only assign table id once While generating yamls for my tests I noticed that the new debug_abbrev format (with multiple table support) was incorrectly assigning id's to the table because it was generating one per abbrev entry in the table. For instance, the first table would get id 4 when 5 abbrev entries existed in the table. By itself this is not a problem but the corresponding debug_info sections were still referencing id 0. This was introduced here: https://reviews.llvm.org/D83116. Maybe a better fix is to actually correctly calculate the table id when emitting debug info? From a quick glance it seems to me the ID is just being calculated as the distance between the first DWARFAbbreviationDeclarationSet and the one the debug info entry points to, which means it's just its index and not the actual table id that was generated when emitting the debug_abbrev tables. With my fix I guess this is fine but on the diff that introduced this Pavel mentioned that he would like to have some sort of unique id between them but not necessarily +1 increasing, but for that to work we need to actually find the table ID, I guess by going directly to Y.DebugAbbrev but to honest I have no idea how to link the DWARFAbbreviationDeclarationSet and the Y.DebugAbbrev, so I just did this simple fix. I also realized there's barely any tests for MachO so it might useful to invest on that if the tool is being reworked on. Reviewed By: Higuoxing, jhenderson Differential Revision: https://reviews.llvm.org/D87179
2020-11-09 03:09:32 +01:00
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(),
2021-01-12 03:48:05 +01:00
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);
}
}
}