blob: 83f229c24a269c19c23394f5e39ecdc51b4081f1 [file]
//===- bolt/Rewrite/DebugNames.cpp -------------------------------------===//
//
// 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 "bolt/Core/DebugNames.h"
#include "bolt/Core/BinaryContext.h"
#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/LEB128.h"
#include <cstdint>
#include <limits>
#include <optional>
#include <tuple>
namespace llvm {
namespace bolt {
DWARF5AcceleratorTable::DWARF5AcceleratorTable(
const bool CreateDebugNames, BinaryContext &BC,
DebugStrWriter &MainBinaryStrWriter)
: BC(BC), MainBinaryStrWriter(MainBinaryStrWriter) {
NeedToCreate = CreateDebugNames || BC.getDebugNamesSection();
if (!NeedToCreate)
return;
FullTableBuffer = std::make_unique<DebugStrBufferVector>();
FullTableStream = std::make_unique<raw_svector_ostream>(*FullTableBuffer);
StrBuffer = std::make_unique<DebugStrBufferVector>();
StrStream = std::make_unique<raw_svector_ostream>(*StrBuffer);
EntriesBuffer = std::make_unique<DebugStrBufferVector>();
Entriestream = std::make_unique<raw_svector_ostream>(*EntriesBuffer);
AugStringBuffer = std::make_unique<DebugStrBufferVector>();
AugStringtream = std::make_unique<raw_svector_ostream>(*AugStringBuffer);
// Binary has split-dwarf CUs.
// Even thought for non-skeleton-cu all names are in .debug_str.dwo section,
// for the .debug_names contributions they are in .debug_str section.
if (BC.getNumDWOCUs()) {
DataExtractor StrData(BC.DwCtx->getDWARFObj().getStrSection(),
BC.DwCtx->isLittleEndian());
uint64_t Offset = 0;
uint64_t StrOffset = 0;
while (StrData.isValidOffset(Offset)) {
Error Err = Error::success();
const char *CStr = StrData.getCStr(&Offset, &Err);
if (Err) {
NeedToCreate = false;
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: Could not extract "
"string from .debug_str section at offset: "
<< Twine::utohexstr(StrOffset) << ".\n";
return;
}
auto R = StrCacheToOffsetMap.try_emplace(
llvm::hash_value(llvm::StringRef(CStr)), StrOffset);
if (!R.second)
BC.errs()
<< "BOLT-WARNING: [internal-dwarf-error]: collision occurred on "
<< CStr << " at offset : 0x" << Twine::utohexstr(StrOffset)
<< ". Previous string offset is: 0x"
<< Twine::utohexstr(R.first->second) << ".\n";
StrOffset = Offset;
}
}
}
void DWARF5AcceleratorTable::preAllocateUnits(DWARFContext &DwCtx) {
// Single pass: allocate CU slots and collect Foreign TU hashes
// in deterministic order (by CU offset in .debug_info).
for (const auto &CU : DwCtx.compile_units()) {
std::optional<uint64_t> DWOId = CU->getDWOId();
if (!DWOId)
continue;
uint32_t Idx = CUList.size();
CUList.push_back(BADCUOFFSET);
CUOffsetsToPatch[*DWOId] = Idx;
const DWARFDie UnitDIE = CU->getUnitDIE();
if (!UnitDIE.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}))
continue;
std::optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId);
if (!DWOCU || !*DWOCU)
continue;
// Iterate DWO info section units for type units (Foreign TU list).
DWARFContext &DWOCtx = (*DWOCU)->getContext();
for (const auto &TU : DWOCtx.dwo_info_section_units()) {
if (!TU->isTypeUnit())
continue;
const uint64_t TUHash = cast<DWARFTypeUnit>(TU.get())->getTypeHash();
if (!TUHashToIndexMap.count(TUHash)) {
TUHashToIndexMap.insert({TUHash, ForeignTUList.size()});
ForeignTUList.push_back(TUHash);
}
}
}
Preallocated = true;
}
void DWARF5AcceleratorTable::setCurrentUnit(DWARFUnit &Unit,
const uint64_t UnitStartOffset) {
CurrentUnit = nullptr;
CurrentUnitOffset = UnitStartOffset;
std::optional<uint64_t> DWOID = Unit.getDWOId();
// We process skeleton CUs after DWO Units for it.
// Patching offset in CU list to correct one.
if (!Unit.isDWOUnit() && DWOID) {
auto Iter = CUOffsetsToPatch.find(*DWOID);
// Check in case no entries were added from non skeleton DWO section.
if (Iter != CUOffsetsToPatch.end())
CUList[Iter->second] = UnitStartOffset;
}
}
void DWARF5AcceleratorTable::addCrossCUDie(DWARFUnit *Unit, const DIE *Die) {
std::lock_guard<std::mutex> Lock(CrossCUDiesMutex);
CrossCUDies.insert({Die->getOffset(), {Unit, Die}});
}
std::optional<std::pair<DWARFUnit *, const DIE *>>
DWARF5AcceleratorTable::getCrossCUDie(uint64_t Offset) {
std::lock_guard<std::mutex> Lock(CrossCUDiesMutex);
auto Iter = CrossCUDies.find(Offset);
if (Iter == CrossCUDies.end())
return std::nullopt;
return Iter->second;
}
void DWARF5AcceleratorTable::addUnit(DWARFUnit &Unit,
const std::optional<uint64_t> &DWOID) {
StrSection = Unit.getStringSection();
if (Preallocated) {
if (!Unit.isTypeUnit() && !DWOID) {
CUList.push_back(CurrentUnitOffset);
}
if (Unit.isTypeUnit() && !DWOID) {
LocalTUList.push_back(CurrentUnitOffset);
}
// For DWO CUs and TUs, slots are already preallocated.
return;
}
if (Unit.isTypeUnit()) {
if (DWOID) {
// We adding an entry for a DWO TU. The DWO CU might not have any entries,
// so need to add it to the list preemptively.
auto Iter = CUOffsetsToPatch.insert({*DWOID, CUList.size()});
if (Iter.second)
CUList.push_back(BADCUOFFSET);
const uint64_t TUHash = cast<DWARFTypeUnit>(&Unit)->getTypeHash();
if (!TUHashToIndexMap.count(TUHash)) {
TUHashToIndexMap.insert({TUHash, ForeignTUList.size()});
ForeignTUList.push_back(TUHash);
}
} else {
LocalTUList.push_back(CurrentUnitOffset);
}
} else {
if (DWOID) {
// This is a path for split dwarf without type units.
// We process DWO Units before Skeleton CU. So at this point we don't know
// the offset of Skeleton CU. Adding CULit index to a map to patch later
// with the correct offset.
auto Iter = CUOffsetsToPatch.insert({*DWOID, CUList.size()});
if (Iter.second)
CUList.push_back(BADCUOFFSET);
} else {
CUList.push_back(CurrentUnitOffset);
}
}
}
// Returns true if DW_TAG_variable should be included in .debug-names based on
// section 6.1.1.1 for DWARF5 spec.
static bool shouldIncludeVariable(const DWARFUnit &Unit, const DIE &Die) {
const DIEValue LocAttrInfo =
Die.findAttribute(dwarf::Attribute::DW_AT_location);
if (!LocAttrInfo)
return false;
if (!(doesFormBelongToClass(LocAttrInfo.getForm(), DWARFFormValue::FC_Exprloc,
Unit.getVersion()) ||
doesFormBelongToClass(LocAttrInfo.getForm(), DWARFFormValue::FC_Block,
Unit.getVersion())))
return false;
std::vector<uint8_t> Sblock;
auto constructVect =
[&](const DIEValueList::const_value_range &Iter) -> void {
for (const DIEValue &Val : Iter)
Sblock.push_back(Val.getDIEInteger().getValue());
};
if (doesFormBelongToClass(LocAttrInfo.getForm(), DWARFFormValue::FC_Exprloc,
Unit.getVersion()))
constructVect(LocAttrInfo.getDIELoc().values());
else
constructVect(LocAttrInfo.getDIEBlock().values());
DataExtractor Data(Sblock, Unit.getContext().isLittleEndian());
DWARFExpression LocExpr(Data, Unit.getAddressByteSize(),
Unit.getFormParams().Format);
for (const DWARFExpression::Operation &Expr : LocExpr)
if (Expr.getCode() == dwarf::DW_OP_addrx ||
Expr.getCode() == dwarf::DW_OP_form_tls_address ||
Expr.getCode() == dwarf::DW_OP_GNU_push_tls_address)
return true;
return false;
}
bool static canProcess(const DWARFUnit &Unit, const DIE &Die,
std::string &NameToUse, const bool TagsOnly) {
if (Die.findAttribute(dwarf::Attribute::DW_AT_declaration))
return false;
switch (Die.getTag()) {
case dwarf::DW_TAG_base_type:
case dwarf::DW_TAG_class_type:
case dwarf::DW_TAG_enumeration_type:
case dwarf::DW_TAG_imported_declaration:
case dwarf::DW_TAG_pointer_type:
case dwarf::DW_TAG_structure_type:
case dwarf::DW_TAG_typedef:
case dwarf::DW_TAG_unspecified_type:
case dwarf::DW_TAG_union_type:
if (TagsOnly || Die.findAttribute(dwarf::Attribute::DW_AT_name))
return true;
return false;
case dwarf::DW_TAG_namespace:
// According to DWARF5 spec namespaces without DW_AT_name needs to have
// "(anonymous namespace)"
if (!Die.findAttribute(dwarf::Attribute::DW_AT_name))
NameToUse = "(anonymous namespace)";
return true;
case dwarf::DW_TAG_inlined_subroutine:
case dwarf::DW_TAG_label:
case dwarf::DW_TAG_subprogram:
if (TagsOnly || Die.findAttribute(dwarf::Attribute::DW_AT_low_pc) ||
Die.findAttribute(dwarf::Attribute::DW_AT_high_pc) ||
Die.findAttribute(dwarf::Attribute::DW_AT_ranges) ||
Die.findAttribute(dwarf::Attribute::DW_AT_entry_pc))
return true;
return false;
case dwarf::DW_TAG_variable:
return TagsOnly || shouldIncludeVariable(Unit, Die);
default:
break;
}
return false;
}
bool DWARF5AcceleratorTable::canGenerateEntryWithCrossCUReference(
const DWARFUnit &Unit, const DIE &Die,
const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
if (!isCreated())
return false;
std::string NameToUse = "";
if (!canProcess(Unit, Die, NameToUse, true))
return false;
return (AttrSpec.Attr == dwarf::Attribute::DW_AT_abstract_origin ||
AttrSpec.Attr == dwarf::Attribute::DW_AT_specification) &&
AttrSpec.Form == dwarf::DW_FORM_ref_addr;
}
/// Returns name offset in String Offset section.
static uint64_t getNameOffset(BinaryContext &BC, DWARFUnit &Unit,
const uint64_t Index) {
const DWARFSection &StrOffsetsSection = Unit.getStringOffsetSection();
const std::optional<StrOffsetsContributionDescriptor> &Contr =
Unit.getStringOffsetsTableContribution();
if (!Contr) {
BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not get "
"StringOffsetsTableContribution for unit at offset: "
<< Twine::utohexstr(Unit.getOffset()) << ".\n";
return 0;
}
const uint8_t DwarfOffsetByteSize = Contr->getDwarfOffsetByteSize();
return support::endian::read32le(StrOffsetsSection.Data.data() + Contr->Base +
Index * DwarfOffsetByteSize);
}
static uint64_t getEntryID(const BOLTDWARF5AccelTableData &Entry) {
return reinterpret_cast<uint64_t>(&Entry);
}
uint32_t DWARF5AcceleratorTable::getUnitID(const DWARFUnit &Unit,
const std::optional<uint64_t> &DWOID,
bool &IsTU) {
IsTU = Unit.isTypeUnit();
if (IsTU) {
if (DWOID) {
const uint64_t TUHash = cast<DWARFTypeUnit>(&Unit)->getTypeHash();
auto Iter = TUHashToIndexMap.find(TUHash);
assert(Iter != TUHashToIndexMap.end() && "Could not find TU hash in map");
return Iter->second;
}
return LocalTUList.size() - 1;
}
if (Preallocated && DWOID) {
auto Iter = CUOffsetsToPatch.find(*DWOID);
assert(Iter != CUOffsetsToPatch.end() && "DWO ID not preallocated");
return Iter->second;
}
return CUList.size() - 1;
}
std::optional<std::string> DWARF5AcceleratorTable::getName(
DWARFUnit &Unit, const std::optional<uint64_t> &DWOID,
const std::string &NameToUse, DIEValue ValName) {
if ((!ValName || ValName.getForm() == dwarf::DW_FORM_string) &&
NameToUse.empty())
return std::nullopt;
std::string Name = "";
uint64_t NameIndexOffset = 0;
if (NameToUse.empty()) {
NameIndexOffset = ValName.getDIEInteger().getValue();
if (ValName.getForm() != dwarf::DW_FORM_strp)
NameIndexOffset = getNameOffset(BC, Unit, NameIndexOffset);
// Counts on strings end with '\0'.
Name = std::string(&StrSection.data()[NameIndexOffset]);
} else {
Name = NameToUse;
}
auto &It = Entries[Name];
if (It.Values.empty()) {
if (DWOID && NameToUse.empty()) {
// For DWO Unit the offset is in the .debug_str.dwo section.
// Need to find offset for the name in the .debug_str section.
llvm::hash_code Hash = llvm::hash_value(llvm::StringRef(Name));
auto ItCache = StrCacheToOffsetMap.find(Hash);
if (ItCache == StrCacheToOffsetMap.end())
NameIndexOffset = MainBinaryStrWriter.addString(Name);
else
NameIndexOffset = ItCache->second;
}
if (!NameToUse.empty())
NameIndexOffset = MainBinaryStrWriter.addString(Name);
It.StrOffset = NameIndexOffset;
// This is the same hash function used in DWARF5AccelTableData.
It.HashValue = caseFoldingDjbHash(Name);
}
return Name;
}
std::optional<BOLTDWARF5AccelTableData *> DWARF5AcceleratorTable::addEntry(
DWARFUnit &DU, const DIE &CurrDie, const std::optional<uint64_t> &DWOID,
const std::optional<BOLTDWARF5AccelTableData *> &Parent,
const std::optional<std::string> &Name,
const uint32_t NumberParentsInChain) {
if (!Name)
return std::nullopt;
auto &It = Entries[*Name];
bool IsTU = false;
uint32_t DieTag = CurrDie.getTag();
uint32_t UnitID = getUnitID(DU, DWOID, IsTU);
std::optional<unsigned> SecondIndex = std::nullopt;
if (IsTU && DWOID) {
auto Iter = CUOffsetsToPatch.find(*DWOID);
if (Iter == CUOffsetsToPatch.end())
BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
"DWO ID in CU offsets for second Unit Index "
<< *Name << ". For DIE at offset: "
<< Twine::utohexstr(CurrentUnitOffset + CurrDie.getOffset())
<< ".\n";
SecondIndex = Iter->second;
}
std::optional<uint64_t> ParentOffset =
(Parent ? std::optional<uint64_t>(getEntryID(**Parent)) : std::nullopt);
// This will be only populated in writeEntry, in order to keep only the parent
// entries, and keep the footprint down.
if (ParentOffset)
EntryRelativeOffsets.insert({*ParentOffset, 0});
bool IsParentRoot = false;
// If there is no parent and no valid Entries in parent chain this is a root
// to be marked with a flag.
if (!Parent && !NumberParentsInChain)
IsParentRoot = true;
It.Values.push_back(new (Allocator) BOLTDWARF5AccelTableData(
CurrDie.getOffset(), ParentOffset, DieTag, UnitID, IsParentRoot, IsTU,
SecondIndex));
return It.Values.back();
}
std::optional<BOLTDWARF5AccelTableData *>
DWARF5AcceleratorTable::processReferencedDie(
DWARFUnit &Unit, const DIE &Die, const std::optional<uint64_t> &DWOID,
const std::optional<BOLTDWARF5AccelTableData *> &Parent,
const std::string &NameToUse, const uint32_t NumberParentsInChain,
const dwarf::Attribute &Attr) {
DIEValue Value = Die.findAttribute(Attr);
if (!Value)
return std::nullopt;
auto getReferenceDie = [&](const DIEValue &Value, const DIE *RefDieUsed)
-> std::optional<std::pair<DWARFUnit *, const DIE *>> {
if (!Value)
return std::nullopt;
if (Value.getForm() == dwarf::DW_FORM_ref_addr) {
auto CrossCUEntry = getCrossCUDie(Value.getDIEInteger().getValue());
if (!CrossCUEntry) {
BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
"referenced DIE in CrossCUDies for "
<< Twine::utohexstr(Value.getDIEInteger().getValue())
<< ".\n";
return std::nullopt;
}
return CrossCUEntry;
}
const DIEEntry &DIEENtry = Value.getDIEEntry();
return {{&Unit, &DIEENtry.getEntry()}};
};
DIEValue AttrValLinkageName;
DIEValue AttrValName = Die.findAttribute(dwarf::Attribute::DW_AT_name);
DWARFUnit *RefUnit = &Unit;
const DIE *RefDieUsed = &Die;
// It is possible to have DW_TAG_subprogram only with DW_AT_linkage_name that
// DW_AT_abstract_origin/DW_AT_specification point to.
while (!AttrValName) {
std::optional<std::pair<DWARFUnit *, const DIE *>> RefDUDie =
getReferenceDie(Value, RefDieUsed);
if (!RefDUDie)
break;
RefUnit = RefDUDie->first;
const DIE &RefDie = *RefDUDie->second;
RefDieUsed = &RefDie;
if (!AttrValLinkageName)
AttrValLinkageName =
RefDie.findAttribute(dwarf::Attribute::DW_AT_linkage_name);
AttrValName = RefDie.findAttribute(dwarf::Attribute::DW_AT_name);
Value = RefDie.findAttribute(dwarf::Attribute::DW_AT_abstract_origin);
if (!Value)
Value = RefDie.findAttribute(dwarf::Attribute::DW_AT_specification);
}
addEntry(Unit, Die, DWOID, Parent,
getName(*RefUnit, DWOID, NameToUse, AttrValLinkageName),
NumberParentsInChain);
return addEntry(Unit, Die, DWOID, Parent,
getName(*RefUnit, DWOID, NameToUse, AttrValName),
NumberParentsInChain);
}
std::optional<BOLTDWARF5AccelTableData *>
DWARF5AcceleratorTable::addAccelTableEntry(
DWARFUnit &Unit, const DIE &Die, const std::optional<uint64_t> &DWOID,
const uint32_t NumberParentsInChain,
std::optional<BOLTDWARF5AccelTableData *> &Parent) {
if (Unit.getVersion() < 5 || !NeedToCreate)
return std::nullopt;
std::string NameToUse = "";
if (!canProcess(Unit, Die, NameToUse, false))
return std::nullopt;
// Adds a Unit to either CU, LocalTU or ForeignTU list the first time we
// encounter it.
// Invoking it here so that we don't add Units that don't have any entries.
if (&Unit != CurrentUnit) {
CurrentUnit = &Unit;
addUnit(Unit, DWOID);
}
// Minor optimization not to add entry twice for DW_TAG_namespace if it has no
// DW_AT_name.
std::optional<BOLTDWARF5AccelTableData *> LinkageEntry = std::nullopt;
DIEValue NameVal = Die.findAttribute(dwarf::Attribute::DW_AT_name);
DIEValue LinkageNameVal =
Die.findAttribute(dwarf::Attribute::DW_AT_linkage_name);
if (!(Die.getTag() == dwarf::DW_TAG_namespace && !NameVal))
LinkageEntry = addEntry(Unit, Die, DWOID, Parent,
getName(Unit, DWOID, NameToUse, LinkageNameVal),
NumberParentsInChain);
std::optional<BOLTDWARF5AccelTableData *> NameEntry =
addEntry(Unit, Die, DWOID, Parent,
getName(Unit, DWOID, NameToUse, NameVal), NumberParentsInChain);
if (NameEntry)
return NameEntry;
// The DIE doesn't have DW_AT_name or DW_AT_linkage_name, so we need to see if
// we can follow other attributes to find them. For the purposes of
// determining whether a debug information entry has a particular
// attribute (such as DW_AT_name), if debug information entry A has a
// DW_AT_specification or DW_AT_abstract_origin attribute pointing to another
// debug information entry B, any attributes of B are considered to be
// part of A.
if (std::optional<BOLTDWARF5AccelTableData *> Entry = processReferencedDie(
Unit, Die, DWOID, Parent, NameToUse, NumberParentsInChain,
dwarf::Attribute::DW_AT_abstract_origin))
return *Entry;
if (std::optional<BOLTDWARF5AccelTableData *> Entry = processReferencedDie(
Unit, Die, DWOID, Parent, NameToUse, NumberParentsInChain,
dwarf::Attribute::DW_AT_specification))
return *Entry;
// This point can be hit by DW_TAG_varialbe that has no DW_AT_name.
return std::nullopt;
}
/// Algorithm from llvm implementation.
void DWARF5AcceleratorTable::computeBucketCount() {
// First get the number of unique hashes.
std::vector<uint32_t> Uniques;
Uniques.reserve(Entries.size());
for (const auto &E : Entries)
Uniques.push_back(E.second.HashValue);
array_pod_sort(Uniques.begin(), Uniques.end());
std::vector<uint32_t>::iterator P = llvm::unique(Uniques);
UniqueHashCount = std::distance(Uniques.begin(), P);
if (UniqueHashCount > 1024)
BucketCount = UniqueHashCount / 4;
else if (UniqueHashCount > 16)
BucketCount = UniqueHashCount / 2;
else
BucketCount = std::max<uint32_t>(UniqueHashCount, 1);
}
/// Returns a stable compile-unit sort key for a debug_names entry.
static unsigned getCompileUnitKey(const BOLTDWARF5AccelTableData *Entry) {
if (Entry->getSecondUnitID())
return *Entry->getSecondUnitID();
if (!Entry->isTU())
return Entry->getUnitID();
return std::numeric_limits<unsigned>::max();
}
static unsigned getTypeUnitKey(const BOLTDWARF5AccelTableData *Entry) {
return Entry->isTU() ? Entry->getUnitID()
: std::numeric_limits<unsigned>::max();
}
/// Bucket code as in: AccelTableBase::finalize()
void DWARF5AcceleratorTable::finalize() {
if (!NeedToCreate)
return;
// Figure out how many buckets we need, then compute the bucket contents and
// the final ordering. The hashes and offsets can be emitted by walking these
// data structures.
computeBucketCount();
// Compute bucket contents and final ordering.
Buckets.resize(BucketCount);
for (auto &E : Entries) {
uint32_t Bucket = E.second.HashValue % BucketCount;
Buckets[Bucket].push_back(&E.second);
}
// Sort the contents of the buckets by hash value so that hash collisions end
// up together. Stable sort makes testing easier and doesn't cost much more.
for (HashList &Bucket : Buckets) {
llvm::stable_sort(Bucket, [](const HashData *LHS, const HashData *RHS) {
return LHS->HashValue < RHS->HashValue;
});
for (HashData *H : Bucket)
llvm::stable_sort(H->Values, [](const BOLTDWARF5AccelTableData *LHS,
const BOLTDWARF5AccelTableData *RHS) {
// Sort entries by (DieOffset, CompileUnitID, TypeUnitID) to ensure
// deterministic output order across multi-threaded processing runs.
return std::make_tuple(LHS->getDieOffset(), getCompileUnitKey(LHS),
getTypeUnitKey(LHS)) <
std::make_tuple(RHS->getDieOffset(), getCompileUnitKey(RHS),
getTypeUnitKey(RHS));
});
}
CUIndexForm = DIEInteger::BestForm(/*IsSigned*/ false, CUList.size() - 1);
TUIndexForm = DIEInteger::BestForm(
/*IsSigned*/ false, LocalTUList.size() + ForeignTUList.size() - 1);
const dwarf::FormParams FormParams{5, 4, dwarf::DwarfFormat::DWARF32, false};
CUIndexEncodingSize = *dwarf::getFixedFormByteSize(CUIndexForm, FormParams);
TUIndexEncodingSize = *dwarf::getFixedFormByteSize(TUIndexForm, FormParams);
}
std::optional<DWARF5AccelTable::UnitIndexAndEncoding>
DWARF5AcceleratorTable::getIndexForEntry(
const BOLTDWARF5AccelTableData &Value) const {
// The foreign TU list immediately follows the local TU list and they both
// use the same index, so that if there are N local TU entries, the index for
// the first foreign TU is N.
if (Value.isTU())
return {{(Value.getSecondUnitID() ? (unsigned)LocalTUList.size() : 0) +
Value.getUnitID(),
{dwarf::DW_IDX_type_unit, TUIndexForm}}};
if (CUList.size() > 1)
return {{Value.getUnitID(), {dwarf::DW_IDX_compile_unit, CUIndexForm}}};
return std::nullopt;
}
std::optional<DWARF5AccelTable::UnitIndexAndEncoding>
DWARF5AcceleratorTable::getSecondIndexForEntry(
const BOLTDWARF5AccelTableData &Value) const {
if (Value.isTU() && CUList.size() > 1 && Value.getSecondUnitID())
return {
{*Value.getSecondUnitID(), {dwarf::DW_IDX_compile_unit, CUIndexForm}}};
return std::nullopt;
}
void DWARF5AcceleratorTable::populateAbbrevsMap() {
for (auto &Bucket : getBuckets()) {
for (DWARF5AcceleratorTable::HashData *Hash : Bucket) {
for (BOLTDWARF5AccelTableData *Value : Hash->Values) {
const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
getIndexForEntry(*Value);
// For entries that need to refer to the foreign type units and to
// the CU.
const std::optional<DWARF5AccelTable::UnitIndexAndEncoding>
SecondEntryRet = getSecondIndexForEntry(*Value);
DebugNamesAbbrev Abbrev(Value->getDieTag());
if (EntryRet)
Abbrev.addAttribute(EntryRet->Encoding);
if (SecondEntryRet)
Abbrev.addAttribute(SecondEntryRet->Encoding);
Abbrev.addAttribute({dwarf::DW_IDX_die_offset, dwarf::DW_FORM_ref4});
if (std::optional<uint64_t> Offset = Value->getParentDieOffset())
Abbrev.addAttribute({dwarf::DW_IDX_parent, dwarf::DW_FORM_ref4});
else if (Value->isParentRoot())
Abbrev.addAttribute(
{dwarf::DW_IDX_parent, dwarf::DW_FORM_flag_present});
FoldingSetNodeID ID;
Abbrev.Profile(ID);
void *InsertPos;
if (DebugNamesAbbrev *Existing =
AbbreviationsSet.FindNodeOrInsertPos(ID, InsertPos)) {
Value->setAbbrevNumber(Existing->getNumber());
continue;
}
DebugNamesAbbrev *NewAbbrev =
new (Alloc) DebugNamesAbbrev(std::move(Abbrev));
AbbreviationsVector.push_back(NewAbbrev);
NewAbbrev->setNumber(AbbreviationsVector.size());
AbbreviationsSet.InsertNode(NewAbbrev, InsertPos);
Value->setAbbrevNumber(NewAbbrev->getNumber());
}
}
}
}
void DWARF5AcceleratorTable::writeEntry(BOLTDWARF5AccelTableData &Entry) {
const uint64_t EntryID = getEntryID(Entry);
if (EntryRelativeOffsets.contains(EntryID))
EntryRelativeOffsets[EntryID] = EntriesBuffer->size();
const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
getIndexForEntry(Entry);
// For forgeign type (FTU) units that need to refer to the FTU and to the CU.
const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> SecondEntryRet =
getSecondIndexForEntry(Entry);
const unsigned AbbrevIndex = Entry.getAbbrevNumber() - 1;
assert(AbbrevIndex < AbbreviationsVector.size() &&
"Entry abbrev index is outside of abbreviations vector range.");
const DebugNamesAbbrev *Abbrev = AbbreviationsVector[AbbrevIndex];
encodeULEB128(Entry.getAbbrevNumber(), *Entriestream);
auto writeIndex = [&](uint32_t Index, uint32_t IndexSize) -> void {
switch (IndexSize) {
default:
llvm_unreachable("Unsupported Index Size!");
break;
case 1:
support::endian::write(*Entriestream, static_cast<uint8_t>(Index),
llvm::endianness::little);
break;
case 2:
support::endian::write(*Entriestream, static_cast<uint16_t>(Index),
llvm::endianness::little);
break;
case 4:
support::endian::write(*Entriestream, static_cast<uint32_t>(Index),
llvm::endianness::little);
break;
};
};
for (const DebugNamesAbbrev::AttributeEncoding &AttrEnc :
Abbrev->getAttributes()) {
switch (AttrEnc.Index) {
default: {
llvm_unreachable("Unexpected index attribute!");
break;
}
case dwarf::DW_IDX_compile_unit: {
const unsigned CUIndex =
SecondEntryRet ? SecondEntryRet->Index : EntryRet->Index;
writeIndex(CUIndex, CUIndexEncodingSize);
break;
}
case dwarf::DW_IDX_type_unit: {
writeIndex(EntryRet->Index, TUIndexEncodingSize);
break;
}
case dwarf::DW_IDX_die_offset: {
assert(AttrEnc.Form == dwarf::DW_FORM_ref4);
support::endian::write(*Entriestream,
static_cast<uint32_t>(Entry.getDieOffset()),
llvm::endianness::little);
break;
}
case dwarf::DW_IDX_parent: {
assert(
(AttrEnc.Form == dwarf::DW_FORM_ref4 && Entry.getParentDieOffset()) ||
AttrEnc.Form == dwarf::DW_FORM_flag_present);
if (std::optional<uint64_t> ParentOffset = Entry.getParentDieOffset()) {
Entry.setPatchOffset(EntriesBuffer->size());
support::endian::write(*Entriestream, static_cast<uint32_t>(UINT32_MAX),
llvm::endianness::little);
}
break;
}
}
}
}
void DWARF5AcceleratorTable::writeEntries() {
for (auto &Bucket : getBuckets()) {
for (DWARF5AcceleratorTable::HashData *Hash : Bucket) {
Hash->EntryOffset = EntriesBuffer->size();
for (BOLTDWARF5AccelTableData *Value : Hash->Values) {
writeEntry(*Value);
}
support::endian::write(*Entriestream, static_cast<uint8_t>(0),
llvm::endianness::little);
}
}
// Patching parent offsets.
for (auto &Bucket : getBuckets()) {
for (DWARF5AcceleratorTable::HashData *Hash : Bucket) {
for (BOLTDWARF5AccelTableData *Entry : Hash->Values) {
std::optional<uint64_t> ParentOffset = Entry->getParentDieOffset();
if (!ParentOffset)
continue;
if (const auto Iter = EntryRelativeOffsets.find(*ParentOffset);
Iter != EntryRelativeOffsets.end()) {
const uint64_t PatchOffset = Entry->getPatchOffset();
uint32_t RelativeOffset = Iter->second;
memcpy(&EntriesBuffer->data()[PatchOffset], &RelativeOffset,
sizeof(uint32_t));
} else {
BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
"entry with offset "
<< *ParentOffset << "\n";
}
}
}
}
}
void DWARF5AcceleratorTable::writeAugmentationString() {
// String needs to be multiple of 4 bytes.
*AugStringtream << "BOLT";
AugmentationStringSize = AugStringBuffer->size();
}
/// Calculates size of .debug_names header without Length field.
static constexpr uint32_t getDebugNamesHeaderSize() {
constexpr uint16_t VersionLength = sizeof(uint16_t);
constexpr uint16_t PaddingLength = sizeof(uint16_t);
constexpr uint32_t CompUnitCountLength = sizeof(uint32_t);
constexpr uint32_t LocalTypeUnitCountLength = sizeof(uint32_t);
constexpr uint32_t ForeignTypeUnitCountLength = sizeof(uint32_t);
constexpr uint32_t BucketCountLength = sizeof(uint32_t);
constexpr uint32_t NameCountLength = sizeof(uint32_t);
constexpr uint32_t AbbrevTableSizeLength = sizeof(uint32_t);
constexpr uint32_t AugmentationStringSizeLength = sizeof(uint32_t);
return VersionLength + PaddingLength + CompUnitCountLength +
LocalTypeUnitCountLength + ForeignTypeUnitCountLength +
BucketCountLength + NameCountLength + AbbrevTableSizeLength +
AugmentationStringSizeLength;
}
void DWARF5AcceleratorTable::emitHeader() const {
constexpr uint32_t HeaderSize = getDebugNamesHeaderSize();
// Header Length
support::endian::write(*FullTableStream,
static_cast<uint32_t>(HeaderSize + StrBuffer->size() +
AugmentationStringSize),
llvm::endianness::little);
// Version
support::endian::write(*FullTableStream, static_cast<uint16_t>(5),
llvm::endianness::little);
// Padding
support::endian::write(*FullTableStream, static_cast<uint16_t>(0),
llvm::endianness::little);
// Compilation Unit Count
support::endian::write(*FullTableStream, static_cast<uint32_t>(CUList.size()),
llvm::endianness::little);
// Local Type Unit Count
support::endian::write(*FullTableStream,
static_cast<uint32_t>(LocalTUList.size()),
llvm::endianness::little);
// Foreign Type Unit Count
support::endian::write(*FullTableStream,
static_cast<uint32_t>(ForeignTUList.size()),
llvm::endianness::little);
// Bucket Count
support::endian::write(*FullTableStream, static_cast<uint32_t>(BucketCount),
llvm::endianness::little);
// Name Count
support::endian::write(*FullTableStream,
static_cast<uint32_t>(Entries.size()),
llvm::endianness::little);
// Abbrev Table Size
support::endian::write(*FullTableStream,
static_cast<uint32_t>(AbbrevTableSize),
llvm::endianness::little);
// Augmentation String Size
support::endian::write(*FullTableStream,
static_cast<uint32_t>(AugmentationStringSize),
llvm::endianness::little);
emitAugmentationString();
FullTableStream->write(StrBuffer->data(), StrBuffer->size());
}
void DWARF5AcceleratorTable::emitCUList() const {
for (const uint32_t CUID : CUList)
support::endian::write(*StrStream, CUID, llvm::endianness::little);
}
void DWARF5AcceleratorTable::emitTUList() const {
for (const uint32_t TUID : LocalTUList)
support::endian::write(*StrStream, TUID, llvm::endianness::little);
for (const uint64_t TUID : ForeignTUList)
support::endian::write(*StrStream, TUID, llvm::endianness::little);
}
void DWARF5AcceleratorTable::emitBuckets() const {
uint32_t Index = 1;
for (const auto &Bucket : enumerate(getBuckets())) {
const uint32_t TempIndex = Bucket.value().empty() ? 0 : Index;
support::endian::write(*StrStream, TempIndex, llvm::endianness::little);
Index += Bucket.value().size();
}
}
void DWARF5AcceleratorTable::emitHashes() const {
for (const auto &Bucket : getBuckets()) {
for (const DWARF5AcceleratorTable::HashData *Hash : Bucket)
support::endian::write(*StrStream, Hash->HashValue,
llvm::endianness::little);
}
}
void DWARF5AcceleratorTable::emitStringOffsets() const {
for (const auto &Bucket : getBuckets()) {
for (const DWARF5AcceleratorTable::HashData *Hash : Bucket)
support::endian::write(*StrStream, static_cast<uint32_t>(Hash->StrOffset),
llvm::endianness::little);
}
}
void DWARF5AcceleratorTable::emitOffsets() const {
for (const auto &Bucket : getBuckets()) {
for (const DWARF5AcceleratorTable::HashData *Hash : Bucket)
support::endian::write(*StrStream,
static_cast<uint32_t>(Hash->EntryOffset),
llvm::endianness::little);
}
}
void DWARF5AcceleratorTable::emitAbbrevs() {
const uint32_t AbbrevTableStart = StrBuffer->size();
for (const auto *Abbrev : AbbreviationsVector) {
encodeULEB128(Abbrev->getNumber(), *StrStream);
encodeULEB128(Abbrev->getDieTag(), *StrStream);
for (const auto &AttrEnc : Abbrev->getAttributes()) {
encodeULEB128(AttrEnc.Index, *StrStream);
encodeULEB128(AttrEnc.Form, *StrStream);
}
encodeULEB128(0, *StrStream);
encodeULEB128(0, *StrStream);
}
encodeULEB128(0, *StrStream);
AbbrevTableSize = StrBuffer->size() - AbbrevTableStart;
}
void DWARF5AcceleratorTable::emitData() {
StrStream->write(EntriesBuffer->data(), EntriesBuffer->size());
}
void DWARF5AcceleratorTable::emitAugmentationString() const {
FullTableStream->write(AugStringBuffer->data(), AugStringBuffer->size());
}
void DWARF5AcceleratorTable::emitAccelTable() {
if (!NeedToCreate)
return;
finalize();
populateAbbrevsMap();
writeEntries();
writeAugmentationString();
emitCUList();
emitTUList();
emitBuckets();
emitHashes();
emitStringOffsets();
emitOffsets();
emitAbbrevs();
emitData();
emitHeader();
}
} // namespace bolt
} // namespace llvm