| //===- 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 |