Skip to content

[DWARFYAML] Implement debug_names support #79666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/include/llvm/ObjectYAML/DWARFEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Error emitDebugAddr(raw_ostream &OS, const Data &DI);
Error emitDebugStrOffsets(raw_ostream &OS, const Data &DI);
Error emitDebugRnglists(raw_ostream &OS, const Data &DI);
Error emitDebugLoclists(raw_ostream &OS, const Data &DI);
Error emitDebugNames(raw_ostream &OS, const Data &DI);

std::function<Error(raw_ostream &, const Data &)>
getDWARFEmitterByName(StringRef SecName);
Expand Down
49 changes: 49 additions & 0 deletions llvm/include/llvm/ObjectYAML/DWARFYAML.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,28 @@ struct Unit {
std::vector<Entry> Entries;
};

struct IdxForm {
dwarf::Index Idx;
dwarf::Form Form;
};

struct DebugNameAbbreviation {
yaml::Hex64 Code;
dwarf::Tag Tag;
std::vector<IdxForm> Indices;
};

struct DebugNameEntry {
yaml::Hex32 NameStrp;
yaml::Hex64 Code;
std::vector<yaml::Hex64> Values;
};

struct DebugNamesSection {
std::vector<DebugNameAbbreviation> Abbrevs;
std::vector<DebugNameEntry> Entries;
};

struct File {
StringRef Name;
uint64_t DirIdx;
Expand Down Expand Up @@ -228,6 +250,7 @@ struct Data {
std::vector<LineTable> DebugLines;
std::optional<std::vector<ListTable<RnglistEntry>>> DebugRnglists;
std::optional<std::vector<ListTable<LoclistEntry>>> DebugLoclists;
std::optional<DebugNamesSection> DebugNames;

bool isEmpty() const;

Expand Down Expand Up @@ -276,6 +299,9 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(
llvm::DWARFYAML::ListEntries<DWARFYAML::LoclistEntry>)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::LoclistEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::DWARFOperation)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::DebugNameEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::DebugNameAbbreviation)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::IdxForm)

namespace llvm {
namespace yaml {
Expand Down Expand Up @@ -324,6 +350,19 @@ template <> struct MappingTraits<DWARFYAML::Unit> {
static void mapping(IO &IO, DWARFYAML::Unit &Unit);
};

template <> struct MappingTraits<DWARFYAML::DebugNamesSection> {
static void mapping(IO &IO, DWARFYAML::DebugNamesSection &);
};
template <> struct MappingTraits<DWARFYAML::DebugNameEntry> {
static void mapping(IO &IO, DWARFYAML::DebugNameEntry &);
};
template <> struct MappingTraits<DWARFYAML::DebugNameAbbreviation> {
static void mapping(IO &IO, DWARFYAML::DebugNameAbbreviation &);
};
template <> struct MappingTraits<DWARFYAML::IdxForm> {
static void mapping(IO &IO, DWARFYAML::IdxForm &);
};

template <> struct MappingTraits<DWARFYAML::Entry> {
static void mapping(IO &IO, DWARFYAML::Entry &Entry);
};
Expand Down Expand Up @@ -437,6 +476,16 @@ template <> struct ScalarEnumerationTraits<dwarf::Form> {
}
};

#define HANDLE_DW_IDX(unused, name) \
io.enumCase(value, "DW_IDX_" #name, dwarf::DW_IDX_##name);

template <> struct ScalarEnumerationTraits<dwarf::Index> {
static void enumeration(IO &io, dwarf::Index &value) {
#include "llvm/BinaryFormat/Dwarf.def"
io.enumFallback<Hex16>(value);
}
};

#define HANDLE_DW_UT(unused, name) \
io.enumCase(value, "DW_UT_" #name, dwarf::DW_UT_##name);

Expand Down
189 changes: 189 additions & 0 deletions llvm/lib/ObjectYAML/DWARFEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,194 @@ Error DWARFYAML::emitDebugStrOffsets(raw_ostream &OS, const Data &DI) {
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());
return;
}

/// 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(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to my above comment, could we instead permit this and just assume some arbitrary size (0 seems like a reasonable bet), to allow explicit specification of an invalid Form value, which in turn would allow users to be able to test invalid Form handling in the DWARF reading code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with the idea, but please see my answer in the previous thread.

"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) {
Expand Down Expand Up @@ -1024,6 +1212,7 @@ DWARFYAML::getDWARFEmitterByName(StringRef SecName) {
.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");
Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/ObjectYAML/DWARFYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ SetVector<StringRef> DWARFYAML::Data::getNonEmptySectionNames() const {
SecNames.insert("debug_rnglists");
if (DebugLoclists)
SecNames.insert("debug_loclists");
if (DebugNames)
SecNames.insert("debug_names");
return SecNames;
}

Expand Down Expand Up @@ -105,6 +107,7 @@ void MappingTraits<DWARFYAML::Data>::mapping(IO &IO, DWARFYAML::Data &DWARF) {
IO.mapOptional("debug_str_offsets", DWARF.DebugStrOffsets);
IO.mapOptional("debug_rnglists", DWARF.DebugRnglists);
IO.mapOptional("debug_loclists", DWARF.DebugLoclists);
IO.mapOptional("debug_names", DWARF.DebugNames);
IO.setContext(OldContext);
}

Expand All @@ -122,6 +125,32 @@ void MappingTraits<DWARFYAML::Abbrev>::mapping(IO &IO,
IO.mapOptional("Attributes", Abbrev.Attributes);
}

void MappingTraits<DWARFYAML::IdxForm>::mapping(IO &IO,
DWARFYAML::IdxForm &IdxForm) {
IO.mapRequired("Idx", IdxForm.Idx);
IO.mapRequired("Form", IdxForm.Form);
}

void MappingTraits<DWARFYAML::DebugNameAbbreviation>::mapping(
IO &IO, DWARFYAML::DebugNameAbbreviation &DebugNameAbbreviation) {
IO.mapRequired("Code", DebugNameAbbreviation.Code);
IO.mapRequired("Tag", DebugNameAbbreviation.Tag);
IO.mapRequired("Indices", DebugNameAbbreviation.Indices);
}

void MappingTraits<DWARFYAML::DebugNameEntry>::mapping(
IO &IO, DWARFYAML::DebugNameEntry &DebugNameEntry) {
IO.mapRequired("Name", DebugNameEntry.NameStrp);
IO.mapRequired("Code", DebugNameEntry.Code);
IO.mapOptional("Values", DebugNameEntry.Values);
}

void MappingTraits<DWARFYAML::DebugNamesSection>::mapping(
IO &IO, DWARFYAML::DebugNamesSection &DebugNames) {
IO.mapRequired("Abbreviations", DebugNames.Abbrevs);
IO.mapRequired("Entries", DebugNames.Entries);
}

void MappingTraits<DWARFYAML::AttributeAbbrev>::mapping(
IO &IO, DWARFYAML::AttributeAbbrev &AttAbbrev) {
IO.mapRequired("Attribute", AttAbbrev.Attribute);
Expand Down
Loading