mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 18:06:05 +00:00

Previously when an entry was skipped in parent chain a child will point to the next valid entry in the chain. After discussion in https://github.com/llvm/llvm-project/pull/91808 this is not very useful. Changed implemenation so that all the children of the entry that is skipped won't have DW_IDX_parent.
750 lines
29 KiB
C++
750 lines
29 KiB
C++
//===- 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/DWARFExpression.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include <cstdint>
|
|
#include <optional>
|
|
|
|
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(), 0);
|
|
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 occured on "
|
|
<< CStr << " at offset : 0x" << Twine::utohexstr(StrOffset)
|
|
<< ". Previous string offset is: 0x"
|
|
<< Twine::utohexstr(R.first->second) << ".\n";
|
|
StrOffset = Offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
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::addUnit(DWARFUnit &Unit,
|
|
const std::optional<uint64_t> &DWOID) {
|
|
constexpr uint32_t BADCUOFFSET = 0xBADBAD;
|
|
StrSection = Unit.getStringSection();
|
|
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 pre-emptively.
|
|
auto Iter = CUOffsetsToPatch.insert({*DWOID, CUList.size()});
|
|
if (Iter.second)
|
|
CUList.push_back(BADCUOFFSET);
|
|
ForeignTUList.push_back(cast<DWARFTypeUnit>(&Unit)->getTypeHash());
|
|
} 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());
|
|
ArrayRef<uint8_t> Expr = ArrayRef<uint8_t>(Sblock);
|
|
DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()),
|
|
Unit.getContext().isLittleEndian(), 0);
|
|
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)
|
|
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:
|
|
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);
|
|
}
|
|
|
|
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 = "";
|
|
|
|
auto getUnitID = [&](const DWARFUnit &Unit, bool &IsTU,
|
|
uint32_t &DieTag) -> uint32_t {
|
|
IsTU = Unit.isTypeUnit();
|
|
DieTag = Die.getTag();
|
|
if (IsTU) {
|
|
if (DWOID)
|
|
return ForeignTUList.size() - 1;
|
|
return LocalTUList.size() - 1;
|
|
}
|
|
return CUList.size() - 1;
|
|
};
|
|
|
|
if (!canProcess(Unit, Die, NameToUse, false))
|
|
return std::nullopt;
|
|
|
|
// Addes 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);
|
|
}
|
|
|
|
auto getName = [&](DIEValue ValName) -> std::optional<std::string> {
|
|
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 the same hash function used in DWARF5AccelTableData.
|
|
It.HashValue = caseFoldingDjbHash(Name);
|
|
}
|
|
return Name;
|
|
};
|
|
|
|
auto addEntry =
|
|
[&](DIEValue ValName) -> std::optional<BOLTDWARF5AccelTableData *> {
|
|
std::optional<std::string> Name = getName(ValName);
|
|
if (!Name)
|
|
return std::nullopt;
|
|
|
|
auto &It = Entries[*Name];
|
|
bool IsTU = false;
|
|
uint32_t DieTag = 0;
|
|
uint32_t UnitID = getUnitID(Unit, IsTU, DieTag);
|
|
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 + Die.getOffset())
|
|
<< ".\n";
|
|
SecondIndex = Iter->second;
|
|
}
|
|
std::optional<uint64_t> ParentOffset =
|
|
(Parent ? std::optional<uint64_t>(getEntryID(**Parent)) : std::nullopt);
|
|
// This will be populated later in writeEntry.
|
|
// This way only parent entries get tracked.
|
|
// Keeping memory 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(
|
|
Die.getOffset(), ParentOffset, DieTag, UnitID, IsParentRoot, IsTU,
|
|
SecondIndex));
|
|
return It.Values.back();
|
|
};
|
|
|
|
// Minor optimization not to add entry twice for DW_TAG_namespace if it has no
|
|
// DW_AT_name.
|
|
if (!(Die.getTag() == dwarf::DW_TAG_namespace &&
|
|
!Die.findAttribute(dwarf::Attribute::DW_AT_name)))
|
|
addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_linkage_name));
|
|
// For the purposes of determining whether a debugging information entry has a
|
|
// particular attribute (such as DW_AT_name), if debugging information entry A
|
|
// has a DW_AT_specification or DW_AT_abstract_origin attribute pointing to
|
|
// another debugging information entry B, any attributes of B are considered
|
|
// to be part of A.
|
|
auto processReferencedDie = [&](const dwarf::Attribute &Attr)
|
|
-> std::optional<BOLTDWARF5AccelTableData *> {
|
|
const DIEValue Value = Die.findAttribute(Attr);
|
|
if (!Value)
|
|
return std::nullopt;
|
|
const DIE *EntryDie = nullptr;
|
|
if (Value.getForm() == dwarf::DW_FORM_ref_addr) {
|
|
auto Iter = CrossCUDies.find(Value.getDIEInteger().getValue());
|
|
if (Iter == CrossCUDies.end()) {
|
|
BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
|
|
"referenced DIE in CrossCUDies for "
|
|
<< Twine::utohexstr(Value.getDIEInteger().getValue())
|
|
<< ".\n";
|
|
return std::nullopt;
|
|
}
|
|
EntryDie = Iter->second;
|
|
} else {
|
|
const DIEEntry &DIEENtry = Value.getDIEEntry();
|
|
EntryDie = &DIEENtry.getEntry();
|
|
}
|
|
|
|
addEntry(EntryDie->findAttribute(dwarf::Attribute::DW_AT_linkage_name));
|
|
return addEntry(EntryDie->findAttribute(dwarf::Attribute::DW_AT_name));
|
|
};
|
|
|
|
if (std::optional<BOLTDWARF5AccelTableData *> Entry =
|
|
processReferencedDie(dwarf::Attribute::DW_AT_abstract_origin))
|
|
return *Entry;
|
|
if (std::optional<BOLTDWARF5AccelTableData *> Entry =
|
|
processReferencedDie(dwarf::Attribute::DW_AT_specification))
|
|
return *Entry;
|
|
|
|
return addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_name));
|
|
}
|
|
|
|
/// 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 =
|
|
std::unique(Uniques.begin(), Uniques.end());
|
|
|
|
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);
|
|
}
|
|
|
|
/// 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) {
|
|
return LHS->getDieOffset() < RHS->getDieOffset();
|
|
});
|
|
}
|
|
|
|
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.find(EntryID) != EntryRelativeOffsets.end())
|
|
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 *Ptr = reinterpret_cast<uint32_t *>(
|
|
&EntriesBuffer.get()->data()[PatchOffset]);
|
|
*Ptr = Iter->second;
|
|
} 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 AugmentationStringSizeLenght = sizeof(uint32_t);
|
|
return VersionLength + PaddingLength + CompUnitCountLength +
|
|
LocalTypeUnitCountLength + ForeignTypeUnitCountLength +
|
|
BucketCountLength + NameCountLength + AbbrevTableSizeLength +
|
|
AugmentationStringSizeLenght;
|
|
}
|
|
|
|
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
|