Files
llvm-project/llvm/lib/ObjectYAML/DWARFEmitter.cpp
Pavel Labath f1fe791ab5 [DWARFYAML] Add support for v5 debug_line file/dir entries (#192226)
This lets us specify all fields in the v5 header. Since v5 entries are
form-based, I've extracted the relevant parts of the debug_info DIE
writing code so it could be reused here as well.

The v5 file and directory entries are more expressive than <=v4 ones, so
one could in theory store everything in the v5 format, while still
reading (YAML) and writing (raw DWARF) in the old format. However, that
would create more corner cases (what if the data cannot be represented
in the older format), and it didn't seem like it was particularly
worthwhile to handle those.
2026-04-20 10:32:13 +02:00

1363 lines
50 KiB
C++

//===- DWARFEmitter - Convert YAML to DWARF binary data -------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// The DWARF component of yaml2obj. Provided as library code for tests.
///
//===----------------------------------------------------------------------===//
#include "llvm/ObjectYAML/DWARFEmitter.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/ObjectYAML/DWARFYAML.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SwapByteOrder.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>
using namespace llvm;
template <typename T>
static void writeInteger(T Integer, raw_ostream &OS, bool IsLittleEndian) {
if (IsLittleEndian != sys::IsLittleEndianHost)
sys::swapByteOrder(Integer);
OS.write(reinterpret_cast<char *>(&Integer), sizeof(T));
}
static Error writeVariableSizedInteger(uint64_t Integer, size_t Size,
raw_ostream &OS, bool IsLittleEndian) {
if (8 == Size)
writeInteger((uint64_t)Integer, OS, IsLittleEndian);
else if (4 == Size)
writeInteger((uint32_t)Integer, OS, IsLittleEndian);
else if (2 == Size)
writeInteger((uint16_t)Integer, OS, IsLittleEndian);
else if (1 == Size)
writeInteger((uint8_t)Integer, OS, IsLittleEndian);
else
return createStringError(errc::not_supported,
"invalid integer write size: %zu", Size);
return Error::success();
}
static void ZeroFillBytes(raw_ostream &OS, size_t Size) {
std::vector<uint8_t> FillData(Size, 0);
OS.write(reinterpret_cast<char *>(FillData.data()), Size);
}
static void writeInitialLength(const dwarf::DwarfFormat Format,
const uint64_t Length, raw_ostream &OS,
bool IsLittleEndian) {
bool IsDWARF64 = Format == dwarf::DWARF64;
if (IsDWARF64)
cantFail(writeVariableSizedInteger(dwarf::DW_LENGTH_DWARF64, 4, OS,
IsLittleEndian));
cantFail(
writeVariableSizedInteger(Length, IsDWARF64 ? 8 : 4, OS, IsLittleEndian));
}
static void writeDWARFOffset(uint64_t Offset, dwarf::DwarfFormat Format,
raw_ostream &OS, bool IsLittleEndian) {
cantFail(writeVariableSizedInteger(Offset, Format == dwarf::DWARF64 ? 8 : 4,
OS, IsLittleEndian));
}
Error DWARFYAML::emitDebugStr(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (StringRef Str : *DI.DebugStrings) {
OS.write(Str.data(), Str.size());
OS.write('\0');
}
return Error::success();
}
StringRef DWARFYAML::Data::getAbbrevTableContentByIndex(uint64_t Index) const {
assert(Index < DebugAbbrev.size() &&
"Index should be less than the size of DebugAbbrev array");
auto [It, Inserted] = AbbrevTableContents.try_emplace(Index);
if (!Inserted)
return It->second;
raw_string_ostream OS(It->second);
uint64_t AbbrevCode = 0;
for (const DWARFYAML::Abbrev &AbbrevDecl : DebugAbbrev[Index].Table) {
AbbrevCode = AbbrevDecl.Code ? (uint64_t)*AbbrevDecl.Code : AbbrevCode + 1;
encodeULEB128(AbbrevCode, OS);
encodeULEB128(AbbrevDecl.Tag, OS);
OS.write(AbbrevDecl.Children);
for (const auto &Attr : AbbrevDecl.Attributes) {
encodeULEB128(Attr.Attribute, OS);
encodeULEB128(Attr.Form, OS);
if (Attr.Form == dwarf::DW_FORM_implicit_const)
encodeSLEB128(Attr.Value, OS);
}
encodeULEB128(0, OS);
encodeULEB128(0, OS);
}
// The abbreviations for a given compilation unit end with an entry
// consisting of a 0 byte for the abbreviation code.
OS.write_zeros(1);
return It->second;
}
Error DWARFYAML::emitDebugAbbrev(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (uint64_t I = 0; I < DI.DebugAbbrev.size(); ++I) {
StringRef AbbrevTableContent = DI.getAbbrevTableContentByIndex(I);
OS.write(AbbrevTableContent.data(), AbbrevTableContent.size());
}
return Error::success();
}
Error DWARFYAML::emitDebugAranges(raw_ostream &OS, const DWARFYAML::Data &DI) {
assert(DI.DebugAranges && "unexpected emitDebugAranges() call");
for (const auto &Range : *DI.DebugAranges) {
uint8_t AddrSize;
if (Range.AddrSize)
AddrSize = *Range.AddrSize;
else
AddrSize = DI.Is64BitAddrSize ? 8 : 4;
uint64_t Length = 4; // sizeof(version) 2 + sizeof(address_size) 1 +
// sizeof(segment_selector_size) 1
Length +=
Range.Format == dwarf::DWARF64 ? 8 : 4; // sizeof(debug_info_offset)
const uint64_t HeaderLength =
Length + (Range.Format == dwarf::DWARF64
? 12
: 4); // sizeof(unit_header) = 12 (DWARF64) or 4 (DWARF32)
const uint64_t PaddedHeaderLength = alignTo(HeaderLength, AddrSize * 2);
if (Range.Length) {
Length = *Range.Length;
} else {
Length += PaddedHeaderLength - HeaderLength;
Length += AddrSize * 2 * (Range.Descriptors.size() + 1);
}
writeInitialLength(Range.Format, Length, OS, DI.IsLittleEndian);
writeInteger((uint16_t)Range.Version, OS, DI.IsLittleEndian);
writeDWARFOffset(Range.CuOffset, Range.Format, OS, DI.IsLittleEndian);
writeInteger((uint8_t)AddrSize, OS, DI.IsLittleEndian);
writeInteger((uint8_t)Range.SegSize, OS, DI.IsLittleEndian);
ZeroFillBytes(OS, PaddedHeaderLength - HeaderLength);
for (const auto &Descriptor : Range.Descriptors) {
if (Error Err = writeVariableSizedInteger(Descriptor.Address, AddrSize,
OS, DI.IsLittleEndian))
return createStringError(errc::not_supported,
"unable to write debug_aranges address: %s",
toString(std::move(Err)).c_str());
cantFail(writeVariableSizedInteger(Descriptor.Length, AddrSize, OS,
DI.IsLittleEndian));
}
ZeroFillBytes(OS, AddrSize * 2);
}
return Error::success();
}
Error DWARFYAML::emitDebugRanges(raw_ostream &OS, const DWARFYAML::Data &DI) {
const size_t RangesOffset = OS.tell();
uint64_t EntryIndex = 0;
for (const auto &DebugRanges : *DI.DebugRanges) {
const size_t CurrOffset = OS.tell() - RangesOffset;
if (DebugRanges.Offset && (uint64_t)*DebugRanges.Offset < CurrOffset)
return createStringError(errc::invalid_argument,
"'Offset' for 'debug_ranges' with index " +
Twine(EntryIndex) +
" must be greater than or equal to the "
"number of bytes written already (0x" +
Twine::utohexstr(CurrOffset) + ")");
if (DebugRanges.Offset)
ZeroFillBytes(OS, *DebugRanges.Offset - CurrOffset);
uint8_t AddrSize;
if (DebugRanges.AddrSize)
AddrSize = *DebugRanges.AddrSize;
else
AddrSize = DI.Is64BitAddrSize ? 8 : 4;
for (const auto &Entry : DebugRanges.Entries) {
if (Error Err = writeVariableSizedInteger(Entry.LowOffset, AddrSize, OS,
DI.IsLittleEndian))
return createStringError(
errc::not_supported,
"unable to write debug_ranges address offset: %s",
toString(std::move(Err)).c_str());
cantFail(writeVariableSizedInteger(Entry.HighOffset, AddrSize, OS,
DI.IsLittleEndian));
}
ZeroFillBytes(OS, AddrSize * 2);
++EntryIndex;
}
return Error::success();
}
static Error emitPubSection(raw_ostream &OS, const DWARFYAML::PubSection &Sect,
bool IsLittleEndian, bool IsGNUPubSec = false) {
writeInitialLength(Sect.Format, Sect.Length, OS, IsLittleEndian);
writeInteger((uint16_t)Sect.Version, OS, IsLittleEndian);
writeInteger((uint32_t)Sect.UnitOffset, OS, IsLittleEndian);
writeInteger((uint32_t)Sect.UnitSize, OS, IsLittleEndian);
for (const auto &Entry : Sect.Entries) {
writeInteger((uint32_t)Entry.DieOffset, OS, IsLittleEndian);
if (IsGNUPubSec)
writeInteger((uint8_t)Entry.Descriptor, OS, IsLittleEndian);
OS.write(Entry.Name.data(), Entry.Name.size());
OS.write('\0');
}
return Error::success();
}
Error DWARFYAML::emitDebugPubnames(raw_ostream &OS, const Data &DI) {
assert(DI.PubNames && "unexpected emitDebugPubnames() call");
return emitPubSection(OS, *DI.PubNames, DI.IsLittleEndian);
}
Error DWARFYAML::emitDebugPubtypes(raw_ostream &OS, const Data &DI) {
assert(DI.PubTypes && "unexpected emitDebugPubtypes() call");
return emitPubSection(OS, *DI.PubTypes, DI.IsLittleEndian);
}
Error DWARFYAML::emitDebugGNUPubnames(raw_ostream &OS, const Data &DI) {
assert(DI.GNUPubNames && "unexpected emitDebugGNUPubnames() call");
return emitPubSection(OS, *DI.GNUPubNames, DI.IsLittleEndian,
/*IsGNUStyle=*/true);
}
Error DWARFYAML::emitDebugGNUPubtypes(raw_ostream &OS, const Data &DI) {
assert(DI.GNUPubTypes && "unexpected emitDebugGNUPubtypes() call");
return emitPubSection(OS, *DI.GNUPubTypes, DI.IsLittleEndian,
/*IsGNUStyle=*/true);
}
template <typename FormTy>
static Error writeFormValues(raw_ostream &OS, const FormTy &Forms,
ArrayRef<DWARFYAML::FormValue> Values,
const dwarf::FormParams &Params,
bool IsLittleEndian) {
auto FormIt = Forms.begin();
const DWARFYAML::FormValue *FormVal = Values.begin();
for (; FormIt != Forms.end() && FormVal != Values.end();
++FormIt, ++FormVal) {
dwarf::Form Form = *FormIt;
bool Indirect;
do {
Indirect = false;
switch (Form) {
case dwarf::DW_FORM_addr:
// TODO: Test this error.
if (Error Err = writeVariableSizedInteger(
FormVal->Value, Params.AddrSize, OS, IsLittleEndian))
return Err;
break;
case dwarf::DW_FORM_ref_addr:
// TODO: Test this error.
if (Error Err = writeVariableSizedInteger(FormVal->Value,
Params.getRefAddrByteSize(),
OS, IsLittleEndian))
return Err;
break;
case dwarf::DW_FORM_exprloc:
case dwarf::DW_FORM_block:
encodeULEB128(FormVal->BlockData.size(), OS);
OS.write((const char *)FormVal->BlockData.data(),
FormVal->BlockData.size());
break;
case dwarf::DW_FORM_block1: {
writeInteger((uint8_t)FormVal->BlockData.size(), OS, IsLittleEndian);
OS.write((const char *)FormVal->BlockData.data(),
FormVal->BlockData.size());
break;
}
case dwarf::DW_FORM_block2: {
writeInteger((uint16_t)FormVal->BlockData.size(), OS, IsLittleEndian);
OS.write((const char *)FormVal->BlockData.data(),
FormVal->BlockData.size());
break;
}
case dwarf::DW_FORM_block4: {
writeInteger((uint32_t)FormVal->BlockData.size(), OS, IsLittleEndian);
OS.write((const char *)FormVal->BlockData.data(),
FormVal->BlockData.size());
break;
}
case dwarf::DW_FORM_strx:
case dwarf::DW_FORM_addrx:
case dwarf::DW_FORM_rnglistx:
case dwarf::DW_FORM_loclistx:
case dwarf::DW_FORM_udata:
case dwarf::DW_FORM_ref_udata:
case dwarf::DW_FORM_GNU_addr_index:
case dwarf::DW_FORM_GNU_str_index:
encodeULEB128(FormVal->Value, OS);
break;
case dwarf::DW_FORM_data1:
case dwarf::DW_FORM_ref1:
case dwarf::DW_FORM_flag:
case dwarf::DW_FORM_strx1:
case dwarf::DW_FORM_addrx1:
writeInteger((uint8_t)FormVal->Value, OS, IsLittleEndian);
break;
case dwarf::DW_FORM_data2:
case dwarf::DW_FORM_ref2:
case dwarf::DW_FORM_strx2:
case dwarf::DW_FORM_addrx2:
writeInteger((uint16_t)FormVal->Value, OS, IsLittleEndian);
break;
case dwarf::DW_FORM_data4:
case dwarf::DW_FORM_ref4:
case dwarf::DW_FORM_ref_sup4:
case dwarf::DW_FORM_strx4:
case dwarf::DW_FORM_addrx4:
writeInteger((uint32_t)FormVal->Value, OS, IsLittleEndian);
break;
case dwarf::DW_FORM_data8:
case dwarf::DW_FORM_ref8:
case dwarf::DW_FORM_ref_sup8:
case dwarf::DW_FORM_ref_sig8:
writeInteger((uint64_t)FormVal->Value, OS, IsLittleEndian);
break;
case dwarf::DW_FORM_sdata:
encodeSLEB128(FormVal->Value, OS);
break;
case dwarf::DW_FORM_string:
OS.write(FormVal->CStr.data(), FormVal->CStr.size());
OS.write('\0');
break;
case dwarf::DW_FORM_indirect:
encodeULEB128(FormVal->Value, OS);
Indirect = true;
Form = static_cast<dwarf::Form>((uint64_t)FormVal->Value);
++FormVal;
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:
cantFail(writeVariableSizedInteger(FormVal->Value,
Params.getDwarfOffsetByteSize(), OS,
IsLittleEndian));
break;
default:
break;
}
} while (Indirect);
}
return Error::success();
}
static Expected<uint64_t> writeDIE(const DWARFYAML::Data &DI, uint64_t CUIndex,
uint64_t AbbrevTableID,
const dwarf::FormParams &Params,
const DWARFYAML::Entry &Entry,
raw_ostream &OS, bool IsLittleEndian) {
uint64_t EntryBegin = OS.tell();
encodeULEB128(Entry.AbbrCode, OS);
uint32_t AbbrCode = Entry.AbbrCode;
if (AbbrCode == 0 || Entry.Values.empty())
return OS.tell() - EntryBegin;
Expected<DWARFYAML::Data::AbbrevTableInfo> AbbrevTableInfoOrErr =
DI.getAbbrevTableInfoByID(AbbrevTableID);
if (!AbbrevTableInfoOrErr)
return createStringError(errc::invalid_argument,
toString(AbbrevTableInfoOrErr.takeError()) +
" for compilation unit with index " +
utostr(CUIndex));
ArrayRef<DWARFYAML::Abbrev> AbbrevDecls(
DI.DebugAbbrev[AbbrevTableInfoOrErr->Index].Table);
if (AbbrCode > AbbrevDecls.size())
return createStringError(
errc::invalid_argument,
"abbrev code must be less than or equal to the number of "
"entries in abbreviation table");
const DWARFYAML::Abbrev &Abbrev = AbbrevDecls[AbbrCode - 1];
if (Error Err =
writeFormValues(OS,
map_range(Abbrev.Attributes,
[](const DWARFYAML::AttributeAbbrev &Abbr) {
return Abbr.Form;
}),
Entry.Values, Params, IsLittleEndian))
return Err;
return OS.tell() - EntryBegin;
}
Error DWARFYAML::emitDebugInfo(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (uint64_t I = 0; I < DI.Units.size(); ++I) {
const DWARFYAML::Unit &Unit = DI.Units[I];
uint8_t AddrSize;
if (Unit.AddrSize)
AddrSize = *Unit.AddrSize;
else
AddrSize = DI.Is64BitAddrSize ? 8 : 4;
dwarf::FormParams Params = {Unit.Version, AddrSize, Unit.Format};
uint64_t Length = 3; // sizeof(version) + sizeof(address_size)
Length += Params.getDwarfOffsetByteSize(); // sizeof(debug_abbrev_offset)
if (Unit.Version >= 5) {
++Length; // sizeof(unit_type)
switch (Unit.Type) {
case dwarf::DW_UT_compile:
case dwarf::DW_UT_partial:
default:
break;
case dwarf::DW_UT_type:
case dwarf::DW_UT_split_type:
// sizeof(type_signature) + sizeof(type_offset)
Length += 8 + Params.getDwarfOffsetByteSize();
break;
case dwarf::DW_UT_skeleton:
case dwarf::DW_UT_split_compile:
Length += 8; // sizeof(dwo_id)
}
}
// Since the length of the current compilation unit is undetermined yet, we
// firstly write the content of the compilation unit to a buffer to
// calculate it and then serialize the buffer content to the actual output
// stream.
std::string EntryBuffer;
raw_string_ostream EntryBufferOS(EntryBuffer);
uint64_t AbbrevTableID = Unit.AbbrevTableID.value_or(I);
for (const DWARFYAML::Entry &Entry : Unit.Entries) {
if (Expected<uint64_t> EntryLength =
writeDIE(DI, I, AbbrevTableID, Params, Entry, EntryBufferOS,
DI.IsLittleEndian))
Length += *EntryLength;
else
return EntryLength.takeError();
}
// If the length is specified in the YAML description, we use it instead of
// the actual length.
if (Unit.Length)
Length = *Unit.Length;
writeInitialLength(Unit.Format, Length, OS, DI.IsLittleEndian);
writeInteger((uint16_t)Unit.Version, OS, DI.IsLittleEndian);
uint64_t AbbrevTableOffset = 0;
if (Unit.AbbrOffset) {
AbbrevTableOffset = *Unit.AbbrOffset;
} else {
if (Expected<DWARFYAML::Data::AbbrevTableInfo> AbbrevTableInfoOrErr =
DI.getAbbrevTableInfoByID(AbbrevTableID)) {
AbbrevTableOffset = AbbrevTableInfoOrErr->Offset;
} else {
// The current compilation unit may not have DIEs and it will not be
// able to find the associated abbrev table. We consume the error and
// assign 0 to the debug_abbrev_offset in such circumstances.
consumeError(AbbrevTableInfoOrErr.takeError());
}
}
if (Unit.Version >= 5) {
writeInteger((uint8_t)Unit.Type, OS, DI.IsLittleEndian);
writeInteger((uint8_t)AddrSize, OS, DI.IsLittleEndian);
writeDWARFOffset(AbbrevTableOffset, Unit.Format, OS, DI.IsLittleEndian);
switch (Unit.Type) {
case dwarf::DW_UT_compile:
case dwarf::DW_UT_partial:
default:
break;
case dwarf::DW_UT_type:
case dwarf::DW_UT_split_type:
writeInteger(Unit.TypeSignatureOrDwoID, OS, DI.IsLittleEndian);
writeDWARFOffset(Unit.TypeOffset, Unit.Format, OS, DI.IsLittleEndian);
break;
case dwarf::DW_UT_skeleton:
case dwarf::DW_UT_split_compile:
writeInteger(Unit.TypeSignatureOrDwoID, OS, DI.IsLittleEndian);
break;
}
} else {
writeDWARFOffset(AbbrevTableOffset, Unit.Format, OS, DI.IsLittleEndian);
writeInteger((uint8_t)AddrSize, OS, DI.IsLittleEndian);
}
OS.write(EntryBuffer.data(), EntryBuffer.size());
}
return Error::success();
}
static void emitFileEntry(raw_ostream &OS, const DWARFYAML::File &File) {
OS.write(File.Name.data(), File.Name.size());
OS.write('\0');
encodeULEB128(File.DirIdx, OS);
encodeULEB128(File.ModTime, OS);
encodeULEB128(File.Length, OS);
}
static void writeExtendedOpcode(const DWARFYAML::LineTableOpcode &Op,
uint8_t AddrSize, bool IsLittleEndian,
raw_ostream &OS) {
// The first byte of extended opcodes is a zero byte. The next bytes are an
// ULEB128 integer giving the number of bytes in the instruction itself (does
// not include the first zero byte or the size). We serialize the instruction
// itself into the OpBuffer and then write the size of the buffer and the
// buffer to the real output stream.
std::string OpBuffer;
raw_string_ostream OpBufferOS(OpBuffer);
writeInteger((uint8_t)Op.SubOpcode, OpBufferOS, IsLittleEndian);
switch (Op.SubOpcode) {
case dwarf::DW_LNE_set_address:
cantFail(writeVariableSizedInteger(Op.Data, AddrSize, OpBufferOS,
IsLittleEndian));
break;
case dwarf::DW_LNE_define_file:
emitFileEntry(OpBufferOS, Op.FileEntry);
break;
case dwarf::DW_LNE_set_discriminator:
encodeULEB128(Op.Data, OpBufferOS);
break;
case dwarf::DW_LNE_end_sequence:
break;
default:
for (auto OpByte : Op.UnknownOpcodeData)
writeInteger((uint8_t)OpByte, OpBufferOS, IsLittleEndian);
}
uint64_t ExtLen = Op.ExtLen.value_or(OpBuffer.size());
encodeULEB128(ExtLen, OS);
OS.write(OpBuffer.data(), OpBuffer.size());
}
static void writeLineTableOpcode(const DWARFYAML::LineTableOpcode &Op,
uint8_t OpcodeBase, uint8_t AddrSize,
raw_ostream &OS, bool IsLittleEndian) {
writeInteger((uint8_t)Op.Opcode, OS, IsLittleEndian);
if (Op.Opcode == 0) {
writeExtendedOpcode(Op, AddrSize, IsLittleEndian, OS);
} else if (Op.Opcode < OpcodeBase) {
switch (Op.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:
encodeULEB128(Op.Data, OS);
break;
case dwarf::DW_LNS_advance_line:
encodeSLEB128(Op.SData, OS);
break;
case dwarf::DW_LNS_fixed_advance_pc:
writeInteger((uint16_t)Op.Data, OS, IsLittleEndian);
break;
default:
for (auto OpData : Op.StandardOpcodeData) {
encodeULEB128(OpData, OS);
}
}
}
}
static std::vector<uint8_t>
getStandardOpcodeLengths(uint16_t Version, std::optional<uint8_t> OpcodeBase) {
// If the opcode_base field isn't specified, we returns the
// standard_opcode_lengths array according to the version by default.
std::vector<uint8_t> StandardOpcodeLengths{0, 1, 1, 1, 1, 0,
0, 0, 1, 0, 0, 1};
if (Version == 2) {
// DWARF v2 uses the same first 9 standard opcodes as v3-5.
StandardOpcodeLengths.resize(9);
} else if (OpcodeBase) {
StandardOpcodeLengths.resize(*OpcodeBase > 0 ? *OpcodeBase - 1 : 0, 0);
}
return StandardOpcodeLengths;
}
static void writeV5EntryFormat(raw_ostream &OS, uint8_t Count,
ArrayRef<DWARFYAML::LnctForm> Format) {
OS << static_cast<char>(Count);
for (const auto [ContentType, Form] : Format) {
encodeULEB128(ContentType, OS);
encodeULEB128(Form, OS);
}
}
static Error writeV5Entry(raw_ostream &OS, uint64_t Count,
ArrayRef<DWARFYAML::LnctForm> Format,
ArrayRef<std::vector<DWARFYAML::FormValue>> EntryList,
const dwarf::FormParams &Params,
bool IsLittleEndian) {
encodeULEB128(Count, OS);
for (ArrayRef<DWARFYAML::FormValue> Entry : EntryList) {
if (Error Err = writeFormValues(
OS,
map_range(Format,
[](const DWARFYAML::LnctForm &F) { return F.Form; }),
Entry, Params, IsLittleEndian))
return Err;
}
return Error::success();
}
Error DWARFYAML::emitDebugLine(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (const DWARFYAML::LineTable &LineTable : DI.DebugLines) {
// Buffer holds the bytes following the header_length (or prologue_length in
// DWARFv2) field to the end of the line number program itself.
std::string Buffer;
raw_string_ostream BufferOS(Buffer);
writeInteger(LineTable.MinInstLength, BufferOS, DI.IsLittleEndian);
if (LineTable.Version >= 4)
writeInteger(LineTable.MaxOpsPerInst, BufferOS, DI.IsLittleEndian);
writeInteger(LineTable.DefaultIsStmt, BufferOS, DI.IsLittleEndian);
writeInteger(LineTable.LineBase, BufferOS, DI.IsLittleEndian);
writeInteger(LineTable.LineRange, BufferOS, DI.IsLittleEndian);
std::vector<uint8_t> StandardOpcodeLengths =
LineTable.StandardOpcodeLengths.value_or(
getStandardOpcodeLengths(LineTable.Version, LineTable.OpcodeBase));
uint8_t OpcodeBase = LineTable.OpcodeBase
? *LineTable.OpcodeBase
: StandardOpcodeLengths.size() + 1;
writeInteger(OpcodeBase, BufferOS, DI.IsLittleEndian);
for (uint8_t OpcodeLength : StandardOpcodeLengths)
writeInteger(OpcodeLength, BufferOS, DI.IsLittleEndian);
if (LineTable.Version >= 5) {
dwarf::FormParams Params{LineTable.Version, LineTable.AddressSize,
LineTable.Format};
writeV5EntryFormat(BufferOS, LineTable.DirectoryEntryFormatCount,
LineTable.DirectoryEntryFormat);
if (Error Err =
writeV5Entry(BufferOS, LineTable.DirectoriesCount,
LineTable.DirectoryEntryFormat,
LineTable.Directories, Params, DI.IsLittleEndian))
return Err;
writeV5EntryFormat(BufferOS, LineTable.FileNameEntryFormatCount,
LineTable.FileNameEntryFormat);
if (Error Err = writeV5Entry(
BufferOS, LineTable.FileNamesCount, LineTable.FileNameEntryFormat,
LineTable.FileNames, Params, DI.IsLittleEndian))
return Err;
} else {
for (StringRef IncludeDir : LineTable.IncludeDirs) {
BufferOS.write(IncludeDir.data(), IncludeDir.size());
BufferOS.write('\0');
}
BufferOS.write('\0');
for (const DWARFYAML::File &File : LineTable.Files)
emitFileEntry(BufferOS, File);
BufferOS.write('\0');
}
uint64_t HeaderLength =
LineTable.PrologueLength ? *LineTable.PrologueLength : Buffer.size();
for (const DWARFYAML::LineTableOpcode &Op : LineTable.Opcodes)
writeLineTableOpcode(Op, OpcodeBase, DI.Is64BitAddrSize ? 8 : 4, BufferOS,
DI.IsLittleEndian);
uint64_t Length;
if (LineTable.Length) {
Length = *LineTable.Length;
} else {
Length =
(LineTable.Format == dwarf::DWARF64 ? 8 : 4); // sizeof(unit_length)
Length += 2; // sizeof(version)
if (LineTable.Version >= 5)
Length += 2; // sizeof(address_size) + sizeof(segment_selector_size)
Length += Buffer.size();
}
writeInitialLength(LineTable.Format, Length, OS, DI.IsLittleEndian);
writeInteger(LineTable.Version, OS, DI.IsLittleEndian);
if (LineTable.Version >= 5) {
writeInteger(LineTable.AddressSize, OS, DI.IsLittleEndian);
writeInteger(LineTable.SegmentSelectorSize, OS, DI.IsLittleEndian);
}
writeDWARFOffset(HeaderLength, LineTable.Format, OS, DI.IsLittleEndian);
OS.write(Buffer.data(), Buffer.size());
}
return Error::success();
}
Error DWARFYAML::emitDebugAddr(raw_ostream &OS, const Data &DI) {
for (const AddrTableEntry &TableEntry : *DI.DebugAddr) {
uint8_t AddrSize;
if (TableEntry.AddrSize)
AddrSize = *TableEntry.AddrSize;
else
AddrSize = DI.Is64BitAddrSize ? 8 : 4;
uint64_t Length;
if (TableEntry.Length)
Length = (uint64_t)*TableEntry.Length;
else
// 2 (version) + 1 (address_size) + 1 (segment_selector_size) = 4
Length = 4 + (AddrSize + TableEntry.SegSelectorSize) *
TableEntry.SegAddrPairs.size();
writeInitialLength(TableEntry.Format, Length, OS, DI.IsLittleEndian);
writeInteger((uint16_t)TableEntry.Version, OS, DI.IsLittleEndian);
writeInteger((uint8_t)AddrSize, OS, DI.IsLittleEndian);
writeInteger((uint8_t)TableEntry.SegSelectorSize, OS, DI.IsLittleEndian);
for (const SegAddrPair &Pair : TableEntry.SegAddrPairs) {
if (TableEntry.SegSelectorSize != yaml::Hex8{0})
if (Error Err = writeVariableSizedInteger(Pair.Segment,
TableEntry.SegSelectorSize,
OS, DI.IsLittleEndian))
return createStringError(errc::not_supported,
"unable to write debug_addr segment: %s",
toString(std::move(Err)).c_str());
if (AddrSize != 0)
if (Error Err = writeVariableSizedInteger(Pair.Address, AddrSize, OS,
DI.IsLittleEndian))
return createStringError(errc::not_supported,
"unable to write debug_addr address: %s",
toString(std::move(Err)).c_str());
}
}
return Error::success();
}
Error DWARFYAML::emitDebugStrOffsets(raw_ostream &OS, const Data &DI) {
assert(DI.DebugStrOffsets && "unexpected emitDebugStrOffsets() call");
for (const DWARFYAML::StringOffsetsTable &Table : *DI.DebugStrOffsets) {
uint64_t Length;
if (Table.Length)
Length = *Table.Length;
else
// sizeof(version) + sizeof(padding) = 4
Length =
4 + Table.Offsets.size() * (Table.Format == dwarf::DWARF64 ? 8 : 4);
writeInitialLength(Table.Format, Length, OS, DI.IsLittleEndian);
writeInteger((uint16_t)Table.Version, OS, DI.IsLittleEndian);
writeInteger((uint16_t)Table.Padding, OS, DI.IsLittleEndian);
for (uint64_t Offset : Table.Offsets)
writeDWARFOffset(Offset, Table.Format, OS, DI.IsLittleEndian);
}
return Error::success();
}
namespace {
/// Emits the header for a DebugNames section.
void emitDebugNamesHeader(raw_ostream &OS, bool IsLittleEndian,
uint32_t NameCount, uint32_t AbbrevSize,
uint32_t CombinedSizeOtherParts) {
// Use the same AugmentationString as AsmPrinter.
StringRef AugmentationString = "LLVM0700";
size_t TotalSize = CombinedSizeOtherParts + 5 * sizeof(uint32_t) +
2 * sizeof(uint16_t) + sizeof(NameCount) +
sizeof(AbbrevSize) + AugmentationString.size();
writeInteger(uint32_t(TotalSize), OS, IsLittleEndian); // Unit length
// Everything below is included in total size.
writeInteger(uint16_t(5), OS, IsLittleEndian); // Version
writeInteger(uint16_t(0), OS, IsLittleEndian); // Padding
writeInteger(uint32_t(1), OS, IsLittleEndian); // Compilation Unit count
writeInteger(uint32_t(0), OS, IsLittleEndian); // Local Type Unit count
writeInteger(uint32_t(0), OS, IsLittleEndian); // Foreign Type Unit count
writeInteger(uint32_t(0), OS, IsLittleEndian); // Bucket count
writeInteger(NameCount, OS, IsLittleEndian);
writeInteger(AbbrevSize, OS, IsLittleEndian);
writeInteger(uint32_t(AugmentationString.size()), OS, IsLittleEndian);
OS.write(AugmentationString.data(), AugmentationString.size());
}
/// Emits the abbreviations for a DebugNames section.
std::string
emitDebugNamesAbbrev(ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
std::string Data;
raw_string_ostream OS(Data);
for (const DWARFYAML::DebugNameAbbreviation &Abbrev : Abbrevs) {
encodeULEB128(Abbrev.Code, OS);
encodeULEB128(Abbrev.Tag, OS);
for (auto [Idx, Form] : Abbrev.Indices) {
encodeULEB128(Idx, OS);
encodeULEB128(Form, OS);
}
encodeULEB128(0, OS);
encodeULEB128(0, OS);
}
encodeULEB128(0, OS);
return Data;
}
/// Emits a simple CU offsets list for a DebugNames section containing a single
/// CU at offset 0.
std::string emitDebugNamesCUOffsets(bool IsLittleEndian) {
std::string Data;
raw_string_ostream OS(Data);
writeInteger(uint32_t(0), OS, IsLittleEndian);
return Data;
}
/// Emits the "NameTable" for a DebugNames section; according to the spec, it
/// consists of two arrays: an array of string offsets, followed immediately by
/// an array of entry offsets. The string offsets are emitted in the order
/// provided in `Entries`.
std::string emitDebugNamesNameTable(
bool IsLittleEndian,
const DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> &Entries,
ArrayRef<uint32_t> EntryPoolOffsets) {
assert(Entries.size() == EntryPoolOffsets.size());
std::string Data;
raw_string_ostream OS(Data);
for (uint32_t Strp : make_first_range(Entries))
writeInteger(Strp, OS, IsLittleEndian);
for (uint32_t PoolOffset : EntryPoolOffsets)
writeInteger(PoolOffset, OS, IsLittleEndian);
return Data;
}
/// Groups entries based on their name (strp) code and returns a map.
DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
groupEntries(ArrayRef<DWARFYAML::DebugNameEntry> Entries) {
DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>> StrpToEntries;
for (const DWARFYAML::DebugNameEntry &Entry : Entries)
StrpToEntries[Entry.NameStrp].push_back(Entry);
return StrpToEntries;
}
/// Finds the abbreviation whose code is AbbrevCode and returns a list
/// containing the expected size of all non-zero-length forms.
Expected<SmallVector<uint8_t>>
getNonZeroDataSizesFor(uint32_t AbbrevCode,
ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
const auto *AbbrevIt = find_if(Abbrevs, [&](const auto &Abbrev) {
return Abbrev.Code.value == AbbrevCode;
});
if (AbbrevIt == Abbrevs.end())
return createStringError(inconvertibleErrorCode(),
"did not find an Abbreviation for this code");
SmallVector<uint8_t> DataSizes;
dwarf::FormParams Params{/*Version=*/5, /*AddrSize=*/4, dwarf::DWARF32};
for (auto [Idx, Form] : AbbrevIt->Indices) {
std::optional<uint8_t> FormSize = dwarf::getFixedFormByteSize(Form, Params);
if (!FormSize)
return createStringError(inconvertibleErrorCode(),
"unsupported Form for YAML debug_names emitter");
if (FormSize == 0)
continue;
DataSizes.push_back(*FormSize);
}
return DataSizes;
}
struct PoolOffsetsAndData {
std::string PoolData;
std::vector<uint32_t> PoolOffsets;
};
/// Emits the entry pool and returns an array of offsets containing the start
/// offset for the entries of each unique name.
/// Verifies that the provided number of data values match those expected by
/// the abbreviation table.
Expected<PoolOffsetsAndData> emitDebugNamesEntryPool(
bool IsLittleEndian,
const DenseMap<uint32_t, std::vector<DWARFYAML::DebugNameEntry>>
&StrpToEntries,
ArrayRef<DWARFYAML::DebugNameAbbreviation> Abbrevs) {
PoolOffsetsAndData Result;
raw_string_ostream OS(Result.PoolData);
for (ArrayRef<DWARFYAML::DebugNameEntry> EntriesWithSameName :
make_second_range(StrpToEntries)) {
Result.PoolOffsets.push_back(Result.PoolData.size());
for (const DWARFYAML::DebugNameEntry &Entry : EntriesWithSameName) {
encodeULEB128(Entry.Code, OS);
Expected<SmallVector<uint8_t>> DataSizes =
getNonZeroDataSizesFor(Entry.Code, Abbrevs);
if (!DataSizes)
return DataSizes.takeError();
if (DataSizes->size() != Entry.Values.size())
return createStringError(
inconvertibleErrorCode(),
"mismatch between provided and required number of values");
for (auto [Value, ValueSize] : zip_equal(Entry.Values, *DataSizes))
if (Error E =
writeVariableSizedInteger(Value, ValueSize, OS, IsLittleEndian))
return std::move(E);
}
encodeULEB128(0, OS);
}
return Result;
}
} // namespace
Error DWARFYAML::emitDebugNames(raw_ostream &OS, const Data &DI) {
assert(DI.DebugNames && "unexpected emitDebugNames() call");
const DebugNamesSection DebugNames = DI.DebugNames.value();
DenseMap<uint32_t, std::vector<DebugNameEntry>> StrpToEntries =
groupEntries(DebugNames.Entries);
// Emit all sub-sections into individual strings so that we may compute
// relative offsets and sizes.
Expected<PoolOffsetsAndData> PoolInfo = emitDebugNamesEntryPool(
DI.IsLittleEndian, StrpToEntries, DebugNames.Abbrevs);
if (!PoolInfo)
return PoolInfo.takeError();
std::string NamesTableData = emitDebugNamesNameTable(
DI.IsLittleEndian, StrpToEntries, PoolInfo->PoolOffsets);
std::string AbbrevData = emitDebugNamesAbbrev(DebugNames.Abbrevs);
std::string CUOffsetsData = emitDebugNamesCUOffsets(DI.IsLittleEndian);
size_t TotalSize = PoolInfo->PoolData.size() + NamesTableData.size() +
AbbrevData.size() + CUOffsetsData.size();
// Start real emission by combining all individual strings.
emitDebugNamesHeader(OS, DI.IsLittleEndian, StrpToEntries.size(),
AbbrevData.size(), TotalSize);
OS.write(CUOffsetsData.data(), CUOffsetsData.size());
// No local TUs, no foreign TUs, no hash lookups table.
OS.write(NamesTableData.data(), NamesTableData.size());
OS.write(AbbrevData.data(), AbbrevData.size());
OS.write(PoolInfo->PoolData.data(), PoolInfo->PoolData.size());
return Error::success();
}
static Error checkOperandCount(StringRef EncodingString,
ArrayRef<yaml::Hex64> Values,
uint64_t ExpectedOperands) {
if (Values.size() != ExpectedOperands)
return createStringError(
errc::invalid_argument,
"invalid number (%zu) of operands for the operator: %s, %" PRIu64
" expected",
Values.size(), EncodingString.str().c_str(), ExpectedOperands);
return Error::success();
}
static Error writeListEntryAddress(StringRef EncodingName, raw_ostream &OS,
uint64_t Addr, uint8_t AddrSize,
bool IsLittleEndian) {
if (Error Err = writeVariableSizedInteger(Addr, AddrSize, OS, IsLittleEndian))
return createStringError(errc::invalid_argument,
"unable to write address for the operator %s: %s",
EncodingName.str().c_str(),
toString(std::move(Err)).c_str());
return Error::success();
}
static Expected<uint64_t>
writeDWARFExpression(raw_ostream &OS,
const DWARFYAML::DWARFOperation &Operation,
uint8_t AddrSize, bool IsLittleEndian) {
auto CheckOperands = [&](uint64_t ExpectedOperands) -> Error {
return checkOperandCount(dwarf::OperationEncodingString(Operation.Operator),
Operation.Values, ExpectedOperands);
};
uint64_t ExpressionBegin = OS.tell();
writeInteger((uint8_t)Operation.Operator, OS, IsLittleEndian);
switch (Operation.Operator) {
case dwarf::DW_OP_consts:
if (Error Err = CheckOperands(1))
return std::move(Err);
encodeSLEB128(Operation.Values[0], OS);
break;
case dwarf::DW_OP_stack_value:
if (Error Err = CheckOperands(0))
return std::move(Err);
break;
default:
StringRef EncodingStr = dwarf::OperationEncodingString(Operation.Operator);
return createStringError(errc::not_supported,
"DWARF expression: " +
(EncodingStr.empty()
? "0x" + utohexstr(Operation.Operator)
: EncodingStr) +
" is not supported");
}
return OS.tell() - ExpressionBegin;
}
static Expected<uint64_t> writeListEntry(raw_ostream &OS,
const DWARFYAML::RnglistEntry &Entry,
uint8_t AddrSize,
bool IsLittleEndian) {
uint64_t BeginOffset = OS.tell();
writeInteger((uint8_t)Entry.Operator, OS, IsLittleEndian);
StringRef EncodingName = dwarf::RangeListEncodingString(Entry.Operator);
auto CheckOperands = [&](uint64_t ExpectedOperands) -> Error {
return checkOperandCount(EncodingName, Entry.Values, ExpectedOperands);
};
auto WriteAddress = [&](uint64_t Addr) -> Error {
return writeListEntryAddress(EncodingName, OS, Addr, AddrSize,
IsLittleEndian);
};
switch (Entry.Operator) {
case dwarf::DW_RLE_end_of_list:
if (Error Err = CheckOperands(0))
return std::move(Err);
break;
case dwarf::DW_RLE_base_addressx:
if (Error Err = CheckOperands(1))
return std::move(Err);
encodeULEB128(Entry.Values[0], OS);
break;
case dwarf::DW_RLE_startx_endx:
case dwarf::DW_RLE_startx_length:
case dwarf::DW_RLE_offset_pair:
if (Error Err = CheckOperands(2))
return std::move(Err);
encodeULEB128(Entry.Values[0], OS);
encodeULEB128(Entry.Values[1], OS);
break;
case dwarf::DW_RLE_base_address:
if (Error Err = CheckOperands(1))
return std::move(Err);
if (Error Err = WriteAddress(Entry.Values[0]))
return std::move(Err);
break;
case dwarf::DW_RLE_start_end:
if (Error Err = CheckOperands(2))
return std::move(Err);
if (Error Err = WriteAddress(Entry.Values[0]))
return std::move(Err);
cantFail(WriteAddress(Entry.Values[1]));
break;
case dwarf::DW_RLE_start_length:
if (Error Err = CheckOperands(2))
return std::move(Err);
if (Error Err = WriteAddress(Entry.Values[0]))
return std::move(Err);
encodeULEB128(Entry.Values[1], OS);
break;
}
return OS.tell() - BeginOffset;
}
static Expected<uint64_t> writeListEntry(raw_ostream &OS,
const DWARFYAML::LoclistEntry &Entry,
uint8_t AddrSize,
bool IsLittleEndian) {
uint64_t BeginOffset = OS.tell();
writeInteger((uint8_t)Entry.Operator, OS, IsLittleEndian);
StringRef EncodingName = dwarf::LocListEncodingString(Entry.Operator);
auto CheckOperands = [&](uint64_t ExpectedOperands) -> Error {
return checkOperandCount(EncodingName, Entry.Values, ExpectedOperands);
};
auto WriteAddress = [&](uint64_t Addr) -> Error {
return writeListEntryAddress(EncodingName, OS, Addr, AddrSize,
IsLittleEndian);
};
auto WriteDWARFOperations = [&]() -> Error {
std::string OpBuffer;
raw_string_ostream OpBufferOS(OpBuffer);
uint64_t DescriptionsLength = 0;
for (const DWARFYAML::DWARFOperation &Op : Entry.Descriptions) {
if (Expected<uint64_t> OpSize =
writeDWARFExpression(OpBufferOS, Op, AddrSize, IsLittleEndian))
DescriptionsLength += *OpSize;
else
return OpSize.takeError();
}
if (Entry.DescriptionsLength)
DescriptionsLength = *Entry.DescriptionsLength;
else
DescriptionsLength = OpBuffer.size();
encodeULEB128(DescriptionsLength, OS);
OS.write(OpBuffer.data(), OpBuffer.size());
return Error::success();
};
switch (Entry.Operator) {
case dwarf::DW_LLE_end_of_list:
if (Error Err = CheckOperands(0))
return std::move(Err);
break;
case dwarf::DW_LLE_base_addressx:
if (Error Err = CheckOperands(1))
return std::move(Err);
encodeULEB128(Entry.Values[0], OS);
break;
case dwarf::DW_LLE_startx_endx:
case dwarf::DW_LLE_startx_length:
case dwarf::DW_LLE_offset_pair:
if (Error Err = CheckOperands(2))
return std::move(Err);
encodeULEB128(Entry.Values[0], OS);
encodeULEB128(Entry.Values[1], OS);
if (Error Err = WriteDWARFOperations())
return std::move(Err);
break;
case dwarf::DW_LLE_default_location:
if (Error Err = CheckOperands(0))
return std::move(Err);
if (Error Err = WriteDWARFOperations())
return std::move(Err);
break;
case dwarf::DW_LLE_base_address:
if (Error Err = CheckOperands(1))
return std::move(Err);
if (Error Err = WriteAddress(Entry.Values[0]))
return std::move(Err);
break;
case dwarf::DW_LLE_start_end:
if (Error Err = CheckOperands(2))
return std::move(Err);
if (Error Err = WriteAddress(Entry.Values[0]))
return std::move(Err);
cantFail(WriteAddress(Entry.Values[1]));
if (Error Err = WriteDWARFOperations())
return std::move(Err);
break;
case dwarf::DW_LLE_start_length:
if (Error Err = CheckOperands(2))
return std::move(Err);
if (Error Err = WriteAddress(Entry.Values[0]))
return std::move(Err);
encodeULEB128(Entry.Values[1], OS);
if (Error Err = WriteDWARFOperations())
return std::move(Err);
break;
}
return OS.tell() - BeginOffset;
}
template <typename EntryType>
static Error writeDWARFLists(raw_ostream &OS,
ArrayRef<DWARFYAML::ListTable<EntryType>> Tables,
bool IsLittleEndian, bool Is64BitAddrSize) {
for (const DWARFYAML::ListTable<EntryType> &Table : Tables) {
// sizeof(version) + sizeof(address_size) + sizeof(segment_selector_size) +
// sizeof(offset_entry_count) = 8
uint64_t Length = 8;
uint8_t AddrSize;
if (Table.AddrSize)
AddrSize = *Table.AddrSize;
else
AddrSize = Is64BitAddrSize ? 8 : 4;
// Since the length of the current range/location lists entry is
// undetermined yet, we firstly write the content of the range/location
// lists to a buffer to calculate the length and then serialize the buffer
// content to the actual output stream.
std::string ListBuffer;
raw_string_ostream ListBufferOS(ListBuffer);
// Offsets holds offsets for each range/location list. The i-th element is
// the offset from the beginning of the first range/location list to the
// location of the i-th range list.
std::vector<uint64_t> Offsets;
for (const DWARFYAML::ListEntries<EntryType> &List : Table.Lists) {
Offsets.push_back(ListBufferOS.tell());
if (List.Content) {
List.Content->writeAsBinary(ListBufferOS, UINT64_MAX);
Length += List.Content->binary_size();
} else if (List.Entries) {
for (const EntryType &Entry : *List.Entries) {
Expected<uint64_t> EntrySize =
writeListEntry(ListBufferOS, Entry, AddrSize, IsLittleEndian);
if (!EntrySize)
return EntrySize.takeError();
Length += *EntrySize;
}
}
}
// If the offset_entry_count field isn't specified, yaml2obj will infer it
// from the 'Offsets' field in the YAML description. If the 'Offsets' field
// isn't specified either, yaml2obj will infer it from the auto-generated
// offsets.
uint32_t OffsetEntryCount;
if (Table.OffsetEntryCount)
OffsetEntryCount = *Table.OffsetEntryCount;
else
OffsetEntryCount = Table.Offsets ? Table.Offsets->size() : Offsets.size();
uint64_t OffsetsSize =
OffsetEntryCount * (Table.Format == dwarf::DWARF64 ? 8 : 4);
Length += OffsetsSize;
// If the length is specified in the YAML description, we use it instead of
// the actual length.
if (Table.Length)
Length = *Table.Length;
writeInitialLength(Table.Format, Length, OS, IsLittleEndian);
writeInteger((uint16_t)Table.Version, OS, IsLittleEndian);
writeInteger((uint8_t)AddrSize, OS, IsLittleEndian);
writeInteger((uint8_t)Table.SegSelectorSize, OS, IsLittleEndian);
writeInteger((uint32_t)OffsetEntryCount, OS, IsLittleEndian);
auto EmitOffsets = [&](ArrayRef<uint64_t> Offsets, uint64_t OffsetsSize) {
for (uint64_t Offset : Offsets)
writeDWARFOffset(OffsetsSize + Offset, Table.Format, OS,
IsLittleEndian);
};
if (Table.Offsets)
EmitOffsets(ArrayRef<uint64_t>((const uint64_t *)Table.Offsets->data(),
Table.Offsets->size()),
0);
else if (OffsetEntryCount != 0)
EmitOffsets(Offsets, OffsetsSize);
OS.write(ListBuffer.data(), ListBuffer.size());
}
return Error::success();
}
Error DWARFYAML::emitDebugRnglists(raw_ostream &OS, const Data &DI) {
assert(DI.DebugRnglists && "unexpected emitDebugRnglists() call");
return writeDWARFLists<DWARFYAML::RnglistEntry>(
OS, *DI.DebugRnglists, DI.IsLittleEndian, DI.Is64BitAddrSize);
}
Error DWARFYAML::emitDebugLoclists(raw_ostream &OS, const Data &DI) {
assert(DI.DebugLoclists && "unexpected emitDebugRnglists() call");
return writeDWARFLists<DWARFYAML::LoclistEntry>(
OS, *DI.DebugLoclists, DI.IsLittleEndian, DI.Is64BitAddrSize);
}
std::function<Error(raw_ostream &, const DWARFYAML::Data &)>
DWARFYAML::getDWARFEmitterByName(StringRef SecName) {
auto EmitFunc =
StringSwitch<
std::function<Error(raw_ostream &, const DWARFYAML::Data &)>>(SecName)
.Case("debug_abbrev", DWARFYAML::emitDebugAbbrev)
.Case("debug_addr", DWARFYAML::emitDebugAddr)
.Case("debug_aranges", DWARFYAML::emitDebugAranges)
.Case("debug_gnu_pubnames", DWARFYAML::emitDebugGNUPubnames)
.Case("debug_gnu_pubtypes", DWARFYAML::emitDebugGNUPubtypes)
.Case("debug_info", DWARFYAML::emitDebugInfo)
.Case("debug_line", DWARFYAML::emitDebugLine)
.Case("debug_loclists", DWARFYAML::emitDebugLoclists)
.Case("debug_pubnames", DWARFYAML::emitDebugPubnames)
.Case("debug_pubtypes", DWARFYAML::emitDebugPubtypes)
.Case("debug_ranges", DWARFYAML::emitDebugRanges)
.Case("debug_rnglists", DWARFYAML::emitDebugRnglists)
.Case("debug_str", DWARFYAML::emitDebugStr)
.Case("debug_str_offsets", DWARFYAML::emitDebugStrOffsets)
.Case("debug_names", DWARFYAML::emitDebugNames)
.Default([&](raw_ostream &, const DWARFYAML::Data &) {
return createStringError(errc::not_supported,
SecName + " is not supported");
});
return EmitFunc;
}
static Error
emitDebugSectionImpl(const DWARFYAML::Data &DI, StringRef Sec,
StringMap<std::unique_ptr<MemoryBuffer>> &OutputBuffers) {
std::string Data;
raw_string_ostream DebugInfoStream(Data);
auto EmitFunc = DWARFYAML::getDWARFEmitterByName(Sec);
if (Error Err = EmitFunc(DebugInfoStream, DI))
return Err;
if (!Data.empty())
OutputBuffers[Sec] = MemoryBuffer::getMemBufferCopy(Data);
return Error::success();
}
Expected<StringMap<std::unique_ptr<MemoryBuffer>>>
DWARFYAML::emitDebugSections(StringRef YAMLString, bool IsLittleEndian,
bool Is64BitAddrSize) {
auto CollectDiagnostic = [](const SMDiagnostic &Diag, void *DiagContext) {
*static_cast<SMDiagnostic *>(DiagContext) = Diag;
};
SMDiagnostic GeneratedDiag;
yaml::Input YIn(YAMLString, /*Ctxt=*/nullptr, CollectDiagnostic,
&GeneratedDiag);
DWARFYAML::Data DI;
DI.IsLittleEndian = IsLittleEndian;
DI.Is64BitAddrSize = Is64BitAddrSize;
YIn >> DI;
if (YIn.error())
return createStringError(YIn.error(), GeneratedDiag.getMessage());
StringMap<std::unique_ptr<MemoryBuffer>> DebugSections;
Error Err = Error::success();
for (StringRef SecName : DI.getNonEmptySectionNames())
Err = joinErrors(std::move(Err),
emitDebugSectionImpl(DI, SecName, DebugSections));
if (Err)
return std::move(Err);
return std::move(DebugSections);
}