There could be multiple TUs with the same hash in various DWO files. In bigger binaries this could be in the thousands. Although they could be structurally different and we need to output Entries for all of them, for the purposes of figuring out a TU hash we only need one entry in Foreign TU list.
759 lines
30 KiB
C++
759 lines
30 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);
|
|
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());
|
|
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) {
|
|
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;
|
|
}
|
|
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
|