1//===- bolt/Rewrite/DebugNames.cpp -------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "bolt/Core/DebugNames.h"
10#include "bolt/Core/BinaryContext.h"
11#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
12#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
13#include "llvm/Support/EndianStream.h"
14#include "llvm/Support/LEB128.h"
15#include <cstdint>
16#include <optional>
17
18namespace llvm {
19namespace bolt {
20DWARF5AcceleratorTable::DWARF5AcceleratorTable(
21 const bool CreateDebugNames, BinaryContext &BC,
22 DebugStrWriter &MainBinaryStrWriter)
23 : BC(BC), MainBinaryStrWriter(MainBinaryStrWriter) {
24 NeedToCreate = CreateDebugNames || BC.getDebugNamesSection();
25 if (!NeedToCreate)
26 return;
27 FullTableBuffer = std::make_unique<DebugStrBufferVector>();
28 FullTableStream = std::make_unique<raw_svector_ostream>(args&: *FullTableBuffer);
29 StrBuffer = std::make_unique<DebugStrBufferVector>();
30 StrStream = std::make_unique<raw_svector_ostream>(args&: *StrBuffer);
31 EntriesBuffer = std::make_unique<DebugStrBufferVector>();
32 Entriestream = std::make_unique<raw_svector_ostream>(args&: *EntriesBuffer);
33 AugStringBuffer = std::make_unique<DebugStrBufferVector>();
34 AugStringtream = std::make_unique<raw_svector_ostream>(args&: *AugStringBuffer);
35
36 // Binary has split-dwarf CUs.
37 // Even thought for non-skeleton-cu all names are in .debug_str.dwo section,
38 // for the .debug_names contributions they are in .debug_str section.
39 if (BC.getNumDWOCUs()) {
40 DataExtractor StrData(BC.DwCtx->getDWARFObj().getStrSection(),
41 BC.DwCtx->isLittleEndian(), 0);
42 uint64_t Offset = 0;
43 uint64_t StrOffset = 0;
44 while (StrData.isValidOffset(offset: Offset)) {
45 Error Err = Error::success();
46 const char *CStr = StrData.getCStr(OffsetPtr: &Offset, Err: &Err);
47 if (Err) {
48 NeedToCreate = false;
49 BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: Could not extract "
50 "string from .debug_str section at offset: "
51 << Twine::utohexstr(Val: StrOffset) << ".\n";
52 return;
53 }
54 auto R = StrCacheToOffsetMap.try_emplace(
55 Key: llvm::hash_value(S: llvm::StringRef(CStr)), Args&: StrOffset);
56 if (!R.second)
57 BC.errs()
58 << "BOLT-WARNING: [internal-dwarf-error]: collision occured on "
59 << CStr << " at offset : 0x" << Twine::utohexstr(Val: StrOffset)
60 << ". Previous string offset is: 0x"
61 << Twine::utohexstr(Val: R.first->second) << ".\n";
62 StrOffset = Offset;
63 }
64 }
65}
66
67void DWARF5AcceleratorTable::setCurrentUnit(DWARFUnit &Unit,
68 const uint64_t UnitStartOffset) {
69 CurrentUnit = nullptr;
70 CurrentUnitOffset = UnitStartOffset;
71 std::optional<uint64_t> DWOID = Unit.getDWOId();
72 // We process skeleton CUs after DWO Units for it.
73 // Patching offset in CU list to correct one.
74 if (!Unit.isDWOUnit() && DWOID) {
75 auto Iter = CUOffsetsToPatch.find(Val: *DWOID);
76 // Check in case no entries were added from non skeleton DWO section.
77 if (Iter != CUOffsetsToPatch.end())
78 CUList[Iter->second] = UnitStartOffset;
79 }
80}
81
82void DWARF5AcceleratorTable::addUnit(DWARFUnit &Unit,
83 const std::optional<uint64_t> &DWOID) {
84 constexpr uint32_t BADCUOFFSET = 0xBADBAD;
85 StrSection = Unit.getStringSection();
86 if (Unit.isTypeUnit()) {
87 if (DWOID) {
88 // We adding an entry for a DWO TU. The DWO CU might not have any entries,
89 // so need to add it to the list pre-emptively.
90 auto Iter = CUOffsetsToPatch.insert(KV: {*DWOID, CUList.size()});
91 if (Iter.second)
92 CUList.push_back(Elt: BADCUOFFSET);
93 ForeignTUList.push_back(Elt: cast<DWARFTypeUnit>(Val: &Unit)->getTypeHash());
94 } else {
95 LocalTUList.push_back(Elt: CurrentUnitOffset);
96 }
97 } else {
98 if (DWOID) {
99 // This is a path for split dwarf without type units.
100 // We process DWO Units before Skeleton CU. So at this point we don't know
101 // the offset of Skeleton CU. Adding CULit index to a map to patch later
102 // with the correct offset.
103 auto Iter = CUOffsetsToPatch.insert(KV: {*DWOID, CUList.size()});
104 if (Iter.second)
105 CUList.push_back(Elt: BADCUOFFSET);
106 } else {
107 CUList.push_back(Elt: CurrentUnitOffset);
108 }
109 }
110}
111
112// Returns true if DW_TAG_variable should be included in .debug-names based on
113// section 6.1.1.1 for DWARF5 spec.
114static bool shouldIncludeVariable(const DWARFUnit &Unit, const DIE &Die) {
115 if (Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_declaration))
116 return false;
117 const DIEValue LocAttrInfo =
118 Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_location);
119 if (!LocAttrInfo)
120 return false;
121 if (!(doesFormBelongToClass(Form: LocAttrInfo.getForm(), FC: DWARFFormValue::FC_Exprloc,
122 DwarfVersion: Unit.getVersion()) ||
123 doesFormBelongToClass(Form: LocAttrInfo.getForm(), FC: DWARFFormValue::FC_Block,
124 DwarfVersion: Unit.getVersion())))
125 return false;
126 std::vector<uint8_t> Sblock;
127 auto constructVect =
128 [&](const DIEValueList::const_value_range &Iter) -> void {
129 for (const DIEValue &Val : Iter)
130 Sblock.push_back(x: Val.getDIEInteger().getValue());
131 };
132 if (doesFormBelongToClass(Form: LocAttrInfo.getForm(), FC: DWARFFormValue::FC_Exprloc,
133 DwarfVersion: Unit.getVersion()))
134 constructVect(LocAttrInfo.getDIELoc().values());
135 else
136 constructVect(LocAttrInfo.getDIEBlock().values());
137 ArrayRef<uint8_t> Expr = ArrayRef<uint8_t>(Sblock);
138 DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()),
139 Unit.getContext().isLittleEndian(), 0);
140 DWARFExpression LocExpr(Data, Unit.getAddressByteSize(),
141 Unit.getFormParams().Format);
142 for (const DWARFExpression::Operation &Expr : LocExpr)
143 if (Expr.getCode() == dwarf::DW_OP_addrx ||
144 Expr.getCode() == dwarf::DW_OP_form_tls_address)
145 return true;
146 return false;
147}
148
149bool static canProcess(const DWARFUnit &Unit, const DIE &Die,
150 std::string &NameToUse, const bool TagsOnly) {
151 switch (Die.getTag()) {
152 case dwarf::DW_TAG_base_type:
153 case dwarf::DW_TAG_class_type:
154 case dwarf::DW_TAG_enumeration_type:
155 case dwarf::DW_TAG_imported_declaration:
156 case dwarf::DW_TAG_pointer_type:
157 case dwarf::DW_TAG_structure_type:
158 case dwarf::DW_TAG_typedef:
159 case dwarf::DW_TAG_unspecified_type:
160 if (TagsOnly || Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_name))
161 return true;
162 return false;
163 case dwarf::DW_TAG_namespace:
164 // According to DWARF5 spec namespaces without DW_AT_name needs to have
165 // "(anonymous namespace)"
166 if (!Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_name))
167 NameToUse = "(anonymous namespace)";
168 return true;
169 case dwarf::DW_TAG_inlined_subroutine:
170 case dwarf::DW_TAG_label:
171 case dwarf::DW_TAG_subprogram:
172 if (TagsOnly || Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_low_pc) ||
173 Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_high_pc) ||
174 Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_ranges) ||
175 Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_entry_pc))
176 return true;
177 return false;
178 case dwarf::DW_TAG_variable:
179 return TagsOnly || shouldIncludeVariable(Unit, Die);
180 default:
181 break;
182 }
183 return false;
184}
185
186bool DWARF5AcceleratorTable::canGenerateEntryWithCrossCUReference(
187 const DWARFUnit &Unit, const DIE &Die,
188 const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
189 if (!isCreated())
190 return false;
191 std::string NameToUse = "";
192 if (!canProcess(Unit, Die, NameToUse, TagsOnly: true))
193 return false;
194 return (AttrSpec.Attr == dwarf::Attribute::DW_AT_abstract_origin ||
195 AttrSpec.Attr == dwarf::Attribute::DW_AT_specification) &&
196 AttrSpec.Form == dwarf::DW_FORM_ref_addr;
197}
198/// Returns name offset in String Offset section.
199static uint64_t getNameOffset(BinaryContext &BC, DWARFUnit &Unit,
200 const uint64_t Index) {
201 const DWARFSection &StrOffsetsSection = Unit.getStringOffsetSection();
202 const std::optional<StrOffsetsContributionDescriptor> &Contr =
203 Unit.getStringOffsetsTableContribution();
204 if (!Contr) {
205 BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not get "
206 "StringOffsetsTableContribution for unit at offset: "
207 << Twine::utohexstr(Val: Unit.getOffset()) << ".\n";
208 return 0;
209 }
210
211 const uint8_t DwarfOffsetByteSize = Contr->getDwarfOffsetByteSize();
212 return support::endian::read32le(P: StrOffsetsSection.Data.data() + Contr->Base +
213 Index * DwarfOffsetByteSize);
214}
215
216static uint64_t getEntryID(const BOLTDWARF5AccelTableData &Entry) {
217 return reinterpret_cast<uint64_t>(&Entry);
218}
219
220std::optional<BOLTDWARF5AccelTableData *>
221DWARF5AcceleratorTable::addAccelTableEntry(
222 DWARFUnit &Unit, const DIE &Die, const std::optional<uint64_t> &DWOID,
223 std::optional<BOLTDWARF5AccelTableData *> &Parent) {
224 if (Unit.getVersion() < 5 || !NeedToCreate)
225 return std::nullopt;
226 std::string NameToUse = "";
227
228 auto getUnitID = [&](const DWARFUnit &Unit, bool &IsTU,
229 uint32_t &DieTag) -> uint32_t {
230 IsTU = Unit.isTypeUnit();
231 DieTag = Die.getTag();
232 if (IsTU) {
233 if (DWOID)
234 return ForeignTUList.size() - 1;
235 return LocalTUList.size() - 1;
236 }
237 return CUList.size() - 1;
238 };
239
240 if (!canProcess(Unit, Die, NameToUse, TagsOnly: false))
241 return std::nullopt;
242
243 // Addes a Unit to either CU, LocalTU or ForeignTU list the first time we
244 // encounter it.
245 // Invoking it here so that we don't add Units that don't have any entries.
246 if (&Unit != CurrentUnit) {
247 CurrentUnit = &Unit;
248 addUnit(Unit, DWOID);
249 }
250
251 auto getName = [&](DIEValue ValName) -> std::optional<std::string> {
252 if ((!ValName || ValName.getForm() == dwarf::DW_FORM_string) &&
253 NameToUse.empty())
254 return std::nullopt;
255 std::string Name = "";
256 uint64_t NameIndexOffset = 0;
257 if (NameToUse.empty()) {
258 NameIndexOffset = ValName.getDIEInteger().getValue();
259 if (ValName.getForm() != dwarf::DW_FORM_strp)
260 NameIndexOffset = getNameOffset(BC, Unit, Index: NameIndexOffset);
261 // Counts on strings end with '\0'.
262 Name = std::string(&StrSection.data()[NameIndexOffset]);
263 } else {
264 Name = NameToUse;
265 }
266 auto &It = Entries[Name];
267 if (It.Values.empty()) {
268 if (DWOID && NameToUse.empty()) {
269 // For DWO Unit the offset is in the .debug_str.dwo section.
270 // Need to find offset for the name in the .debug_str section.
271 llvm::hash_code Hash = llvm::hash_value(S: llvm::StringRef(Name));
272 auto ItCache = StrCacheToOffsetMap.find(Val: Hash);
273 if (ItCache == StrCacheToOffsetMap.end())
274 NameIndexOffset = MainBinaryStrWriter.addString(Str: Name);
275 else
276 NameIndexOffset = ItCache->second;
277 }
278 if (!NameToUse.empty())
279 NameIndexOffset = MainBinaryStrWriter.addString(Str: Name);
280 It.StrOffset = NameIndexOffset;
281 // This the same hash function used in DWARF5AccelTableData.
282 It.HashValue = caseFoldingDjbHash(Buffer: Name);
283 }
284 return Name;
285 };
286
287 auto addEntry =
288 [&](DIEValue ValName) -> std::optional<BOLTDWARF5AccelTableData *> {
289 std::optional<std::string> Name = getName(ValName);
290 if (!Name)
291 return std::nullopt;
292
293 auto &It = Entries[*Name];
294 bool IsTU = false;
295 uint32_t DieTag = 0;
296 uint32_t UnitID = getUnitID(Unit, IsTU, DieTag);
297 std::optional<unsigned> SecondIndex = std::nullopt;
298 if (IsTU && DWOID) {
299 auto Iter = CUOffsetsToPatch.find(Val: *DWOID);
300 if (Iter == CUOffsetsToPatch.end())
301 BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
302 "DWO ID in CU offsets for second Unit Index "
303 << *Name << ". For DIE at offset: "
304 << Twine::utohexstr(Val: CurrentUnitOffset + Die.getOffset())
305 << ".\n";
306 SecondIndex = Iter->second;
307 }
308 std::optional<uint64_t> ParentOffset =
309 (Parent ? std::optional<uint64_t>(getEntryID(Entry: **Parent)) : std::nullopt);
310 // This will be populated later in writeEntry.
311 // This way only parent entries get tracked.
312 // Keeping memory footprint down.
313 if (ParentOffset)
314 EntryRelativeOffsets.insert(KV: {*ParentOffset, 0});
315 It.Values.push_back(x: new (Allocator) BOLTDWARF5AccelTableData(
316 Die.getOffset(), ParentOffset, DieTag, UnitID, IsTU, SecondIndex));
317 return It.Values.back();
318 };
319
320 // Minor optimization not to add entry twice for DW_TAG_namespace if it has no
321 // DW_AT_name.
322 if (!(Die.getTag() == dwarf::DW_TAG_namespace &&
323 !Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_name)))
324 addEntry(Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_linkage_name));
325 // For the purposes of determining whether a debugging information entry has a
326 // particular attribute (such as DW_AT_name), if debugging information entry A
327 // has a DW_AT_specification or DW_AT_abstract_origin attribute pointing to
328 // another debugging information entry B, any attributes of B are considered
329 // to be part of A.
330 auto processReferencedDie = [&](const dwarf::Attribute &Attr)
331 -> std::optional<BOLTDWARF5AccelTableData *> {
332 const DIEValue Value = Die.findAttribute(Attribute: Attr);
333 if (!Value)
334 return std::nullopt;
335 const DIE *EntryDie = nullptr;
336 if (Value.getForm() == dwarf::DW_FORM_ref_addr) {
337 auto Iter = CrossCUDies.find(Val: Value.getDIEInteger().getValue());
338 if (Iter == CrossCUDies.end()) {
339 BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
340 "referenced DIE in CrossCUDies for "
341 << Twine::utohexstr(Val: Value.getDIEInteger().getValue())
342 << ".\n";
343 return std::nullopt;
344 }
345 EntryDie = Iter->second;
346 } else {
347 const DIEEntry &DIEENtry = Value.getDIEEntry();
348 EntryDie = &DIEENtry.getEntry();
349 }
350
351 addEntry(EntryDie->findAttribute(Attribute: dwarf::Attribute::DW_AT_linkage_name));
352 return addEntry(EntryDie->findAttribute(Attribute: dwarf::Attribute::DW_AT_name));
353 };
354
355 if (std::optional<BOLTDWARF5AccelTableData *> Entry =
356 processReferencedDie(dwarf::Attribute::DW_AT_abstract_origin))
357 return *Entry;
358 if (std::optional<BOLTDWARF5AccelTableData *> Entry =
359 processReferencedDie(dwarf::Attribute::DW_AT_specification))
360 return *Entry;
361
362 return addEntry(Die.findAttribute(Attribute: dwarf::Attribute::DW_AT_name));
363}
364
365/// Algorithm from llvm implementation.
366void DWARF5AcceleratorTable::computeBucketCount() {
367 // First get the number of unique hashes.
368 std::vector<uint32_t> Uniques;
369 Uniques.reserve(n: Entries.size());
370 for (const auto &E : Entries)
371 Uniques.push_back(x: E.second.HashValue);
372 array_pod_sort(Start: Uniques.begin(), End: Uniques.end());
373 std::vector<uint32_t>::iterator P =
374 std::unique(first: Uniques.begin(), last: Uniques.end());
375
376 UniqueHashCount = std::distance(first: Uniques.begin(), last: P);
377
378 if (UniqueHashCount > 1024)
379 BucketCount = UniqueHashCount / 4;
380 else if (UniqueHashCount > 16)
381 BucketCount = UniqueHashCount / 2;
382 else
383 BucketCount = std::max<uint32_t>(a: UniqueHashCount, b: 1);
384}
385
386/// Bucket code as in: AccelTableBase::finalize()
387void DWARF5AcceleratorTable::finalize() {
388 if (!NeedToCreate)
389 return;
390 // Figure out how many buckets we need, then compute the bucket contents and
391 // the final ordering. The hashes and offsets can be emitted by walking these
392 // data structures.
393 computeBucketCount();
394
395 // Compute bucket contents and final ordering.
396 Buckets.resize(new_size: BucketCount);
397 for (auto &E : Entries) {
398 uint32_t Bucket = E.second.HashValue % BucketCount;
399 Buckets[Bucket].push_back(x: &E.second);
400 }
401
402 // Sort the contents of the buckets by hash value so that hash collisions end
403 // up together. Stable sort makes testing easier and doesn't cost much more.
404 for (HashList &Bucket : Buckets) {
405 llvm::stable_sort(Range&: Bucket, C: [](const HashData *LHS, const HashData *RHS) {
406 return LHS->HashValue < RHS->HashValue;
407 });
408 for (HashData *H : Bucket)
409 llvm::stable_sort(Range&: H->Values, C: [](const BOLTDWARF5AccelTableData *LHS,
410 const BOLTDWARF5AccelTableData *RHS) {
411 return LHS->getDieOffset() < RHS->getDieOffset();
412 });
413 }
414
415 CUIndexForm = DIEInteger::BestForm(/*IsSigned*/ false, Int: CUList.size() - 1);
416 TUIndexForm = DIEInteger::BestForm(
417 /*IsSigned*/ false, Int: LocalTUList.size() + ForeignTUList.size() - 1);
418 const dwarf::FormParams FormParams{.Version: 5, .AddrSize: 4, .Format: dwarf::DwarfFormat::DWARF32, .DwarfUsesRelocationsAcrossSections: false};
419 CUIndexEncodingSize = *dwarf::getFixedFormByteSize(Form: CUIndexForm, Params: FormParams);
420 TUIndexEncodingSize = *dwarf::getFixedFormByteSize(Form: TUIndexForm, Params: FormParams);
421}
422
423std::optional<DWARF5AccelTable::UnitIndexAndEncoding>
424DWARF5AcceleratorTable::getIndexForEntry(
425 const BOLTDWARF5AccelTableData &Value) const {
426 // The foreign TU list immediately follows the local TU list and they both
427 // use the same index, so that if there are N local TU entries, the index for
428 // the first foreign TU is N.
429 if (Value.isTU())
430 return {{.Index: (Value.getSecondUnitID() ? (unsigned)LocalTUList.size() : 0) +
431 Value.getUnitID(),
432 .Encoding: {.Index: dwarf::DW_IDX_type_unit, .Form: TUIndexForm}}};
433 if (CUList.size() > 1)
434 return {{.Index: Value.getUnitID(), .Encoding: {.Index: dwarf::DW_IDX_compile_unit, .Form: CUIndexForm}}};
435 return std::nullopt;
436}
437
438std::optional<DWARF5AccelTable::UnitIndexAndEncoding>
439DWARF5AcceleratorTable::getSecondIndexForEntry(
440 const BOLTDWARF5AccelTableData &Value) const {
441 if (Value.isTU() && CUList.size() > 1 && Value.getSecondUnitID())
442 return {
443 {.Index: *Value.getSecondUnitID(), .Encoding: {.Index: dwarf::DW_IDX_compile_unit, .Form: CUIndexForm}}};
444 return std::nullopt;
445}
446
447void DWARF5AcceleratorTable::populateAbbrevsMap() {
448 for (auto &Bucket : getBuckets()) {
449 for (DWARF5AcceleratorTable::HashData *Hash : Bucket) {
450 for (BOLTDWARF5AccelTableData *Value : Hash->Values) {
451 const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
452 getIndexForEntry(Value: *Value);
453 // For entries that need to refer to the foreign type units and to
454 // the CU.
455 const std::optional<DWARF5AccelTable::UnitIndexAndEncoding>
456 SecondEntryRet = getSecondIndexForEntry(Value: *Value);
457 DebugNamesAbbrev Abbrev(Value->getDieTag());
458 if (EntryRet)
459 Abbrev.addAttribute(Attr: EntryRet->Encoding);
460 if (SecondEntryRet)
461 Abbrev.addAttribute(Attr: SecondEntryRet->Encoding);
462 Abbrev.addAttribute(Attr: {.Index: dwarf::DW_IDX_die_offset, .Form: dwarf::DW_FORM_ref4});
463 if (std::optional<uint64_t> Offset = Value->getParentDieOffset())
464 Abbrev.addAttribute(Attr: {.Index: dwarf::DW_IDX_parent, .Form: dwarf::DW_FORM_ref4});
465 else
466 Abbrev.addAttribute(
467 Attr: {.Index: dwarf::DW_IDX_parent, .Form: dwarf::DW_FORM_flag_present});
468 FoldingSetNodeID ID;
469 Abbrev.Profile(ID);
470 void *InsertPos;
471 if (DebugNamesAbbrev *Existing =
472 AbbreviationsSet.FindNodeOrInsertPos(ID, InsertPos)) {
473 Value->setAbbrevNumber(Existing->getNumber());
474 continue;
475 }
476 DebugNamesAbbrev *NewAbbrev =
477 new (Alloc) DebugNamesAbbrev(std::move(Abbrev));
478 AbbreviationsVector.push_back(Elt: NewAbbrev);
479 NewAbbrev->setNumber(AbbreviationsVector.size());
480 AbbreviationsSet.InsertNode(N: NewAbbrev, InsertPos);
481 Value->setAbbrevNumber(NewAbbrev->getNumber());
482 }
483 }
484 }
485}
486
487void DWARF5AcceleratorTable::writeEntry(BOLTDWARF5AccelTableData &Entry) {
488 const uint64_t EntryID = getEntryID(Entry);
489 if (EntryRelativeOffsets.find(Val: EntryID) != EntryRelativeOffsets.end())
490 EntryRelativeOffsets[EntryID] = EntriesBuffer->size();
491
492 const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> EntryRet =
493 getIndexForEntry(Value: Entry);
494 // For forgeign type (FTU) units that need to refer to the FTU and to the CU.
495 const std::optional<DWARF5AccelTable::UnitIndexAndEncoding> SecondEntryRet =
496 getSecondIndexForEntry(Value: Entry);
497 const unsigned AbbrevIndex = Entry.getAbbrevNumber() - 1;
498 assert(AbbrevIndex < AbbreviationsVector.size() &&
499 "Entry abbrev index is outside of abbreviations vector range.");
500 const DebugNamesAbbrev *Abbrev = AbbreviationsVector[AbbrevIndex];
501 encodeULEB128(Value: Entry.getAbbrevNumber(), OS&: *Entriestream);
502 auto writeIndex = [&](uint32_t Index, uint32_t IndexSize) -> void {
503 switch (IndexSize) {
504 default:
505 llvm_unreachable("Unsupported Index Size!");
506 break;
507 case 1:
508 support::endian::write(os&: *Entriestream, value: static_cast<uint8_t>(Index),
509 endian: llvm::endianness::little);
510 break;
511 case 2:
512 support::endian::write(os&: *Entriestream, value: static_cast<uint16_t>(Index),
513 endian: llvm::endianness::little);
514 break;
515 case 4:
516 support::endian::write(os&: *Entriestream, value: static_cast<uint32_t>(Index),
517 endian: llvm::endianness::little);
518 break;
519 };
520 };
521
522 for (const DebugNamesAbbrev::AttributeEncoding &AttrEnc :
523 Abbrev->getAttributes()) {
524 switch (AttrEnc.Index) {
525 default: {
526 llvm_unreachable("Unexpected index attribute!");
527 break;
528 }
529 case dwarf::DW_IDX_compile_unit: {
530 const unsigned CUIndex =
531 SecondEntryRet ? SecondEntryRet->Index : EntryRet->Index;
532 writeIndex(CUIndex, CUIndexEncodingSize);
533 break;
534 }
535 case dwarf::DW_IDX_type_unit: {
536 writeIndex(EntryRet->Index, TUIndexEncodingSize);
537 break;
538 }
539 case dwarf::DW_IDX_die_offset: {
540 assert(AttrEnc.Form == dwarf::DW_FORM_ref4);
541 support::endian::write(os&: *Entriestream,
542 value: static_cast<uint32_t>(Entry.getDieOffset()),
543 endian: llvm::endianness::little);
544 break;
545 }
546 case dwarf::DW_IDX_parent: {
547 assert(
548 (AttrEnc.Form == dwarf::DW_FORM_ref4 && Entry.getParentDieOffset()) ||
549 AttrEnc.Form == dwarf::DW_FORM_flag_present);
550 if (std::optional<uint64_t> ParentOffset = Entry.getParentDieOffset()) {
551 Entry.setPatchOffset(EntriesBuffer->size());
552 support::endian::write(os&: *Entriestream, value: static_cast<uint32_t>(UINT32_MAX),
553 endian: llvm::endianness::little);
554 }
555 break;
556 }
557 }
558 }
559}
560
561void DWARF5AcceleratorTable::writeEntries() {
562 for (auto &Bucket : getBuckets()) {
563 for (DWARF5AcceleratorTable::HashData *Hash : Bucket) {
564 Hash->EntryOffset = EntriesBuffer->size();
565 for (BOLTDWARF5AccelTableData *Value : Hash->Values) {
566 writeEntry(Entry&: *Value);
567 }
568 support::endian::write(os&: *Entriestream, value: static_cast<uint8_t>(0),
569 endian: llvm::endianness::little);
570 }
571 }
572 // Patching parent offsets.
573 for (auto &Bucket : getBuckets()) {
574 for (DWARF5AcceleratorTable::HashData *Hash : Bucket) {
575 for (BOLTDWARF5AccelTableData *Entry : Hash->Values) {
576 std::optional<uint64_t> ParentOffset = Entry->getParentDieOffset();
577 if (!ParentOffset)
578 continue;
579 if (const auto Iter = EntryRelativeOffsets.find(Val: *ParentOffset);
580 Iter != EntryRelativeOffsets.end()) {
581 const uint64_t PatchOffset = Entry->getPatchOffset();
582 uint32_t *Ptr = reinterpret_cast<uint32_t *>(
583 &EntriesBuffer.get()->data()[PatchOffset]);
584 *Ptr = Iter->second;
585 } else {
586 BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
587 "entry with offset "
588 << *ParentOffset << "\n";
589 }
590 }
591 }
592 }
593}
594
595void DWARF5AcceleratorTable::writeAugmentationString() {
596 // String needs to be multiple of 4 bytes.
597 *AugStringtream << "BOLT";
598 AugmentationStringSize = AugStringBuffer->size();
599}
600
601/// Calculates size of .debug_names header without Length field.
602static constexpr uint32_t getDebugNamesHeaderSize() {
603 constexpr uint16_t VersionLength = sizeof(uint16_t);
604 constexpr uint16_t PaddingLength = sizeof(uint16_t);
605 constexpr uint32_t CompUnitCountLength = sizeof(uint32_t);
606 constexpr uint32_t LocalTypeUnitCountLength = sizeof(uint32_t);
607 constexpr uint32_t ForeignTypeUnitCountLength = sizeof(uint32_t);
608 constexpr uint32_t BucketCountLength = sizeof(uint32_t);
609 constexpr uint32_t NameCountLength = sizeof(uint32_t);
610 constexpr uint32_t AbbrevTableSizeLength = sizeof(uint32_t);
611 constexpr uint32_t AugmentationStringSizeLenght = sizeof(uint32_t);
612 return VersionLength + PaddingLength + CompUnitCountLength +
613 LocalTypeUnitCountLength + ForeignTypeUnitCountLength +
614 BucketCountLength + NameCountLength + AbbrevTableSizeLength +
615 AugmentationStringSizeLenght;
616}
617
618void DWARF5AcceleratorTable::emitHeader() const {
619 constexpr uint32_t HeaderSize = getDebugNamesHeaderSize();
620 // Header Length
621 support::endian::write(os&: *FullTableStream,
622 value: static_cast<uint32_t>(HeaderSize + StrBuffer->size() +
623 AugmentationStringSize),
624 endian: llvm::endianness::little);
625 // Version
626 support::endian::write(os&: *FullTableStream, value: static_cast<uint16_t>(5),
627 endian: llvm::endianness::little);
628 // Padding
629 support::endian::write(os&: *FullTableStream, value: static_cast<uint16_t>(0),
630 endian: llvm::endianness::little);
631 // Compilation Unit Count
632 support::endian::write(os&: *FullTableStream, value: static_cast<uint32_t>(CUList.size()),
633 endian: llvm::endianness::little);
634 // Local Type Unit Count
635 support::endian::write(os&: *FullTableStream,
636 value: static_cast<uint32_t>(LocalTUList.size()),
637 endian: llvm::endianness::little);
638 // Foreign Type Unit Count
639 support::endian::write(os&: *FullTableStream,
640 value: static_cast<uint32_t>(ForeignTUList.size()),
641 endian: llvm::endianness::little);
642 // Bucket Count
643 support::endian::write(os&: *FullTableStream, value: static_cast<uint32_t>(BucketCount),
644 endian: llvm::endianness::little);
645 // Name Count
646 support::endian::write(os&: *FullTableStream,
647 value: static_cast<uint32_t>(Entries.size()),
648 endian: llvm::endianness::little);
649 // Abbrev Table Size
650 support::endian::write(os&: *FullTableStream,
651 value: static_cast<uint32_t>(AbbrevTableSize),
652 endian: llvm::endianness::little);
653 // Augmentation String Size
654 support::endian::write(os&: *FullTableStream,
655 value: static_cast<uint32_t>(AugmentationStringSize),
656 endian: llvm::endianness::little);
657
658 emitAugmentationString();
659 FullTableStream->write(Ptr: StrBuffer->data(), Size: StrBuffer->size());
660}
661
662void DWARF5AcceleratorTable::emitCUList() const {
663 for (const uint32_t CUID : CUList)
664 support::endian::write(os&: *StrStream, value: CUID, endian: llvm::endianness::little);
665}
666void DWARF5AcceleratorTable::emitTUList() const {
667 for (const uint32_t TUID : LocalTUList)
668 support::endian::write(os&: *StrStream, value: TUID, endian: llvm::endianness::little);
669
670 for (const uint64_t TUID : ForeignTUList)
671 support::endian::write(os&: *StrStream, value: TUID, endian: llvm::endianness::little);
672}
673void DWARF5AcceleratorTable::emitBuckets() const {
674 uint32_t Index = 1;
675 for (const auto &Bucket : enumerate(First: getBuckets())) {
676 const uint32_t TempIndex = Bucket.value().empty() ? 0 : Index;
677 support::endian::write(os&: *StrStream, value: TempIndex, endian: llvm::endianness::little);
678 Index += Bucket.value().size();
679 }
680}
681void DWARF5AcceleratorTable::emitHashes() const {
682 for (const auto &Bucket : getBuckets()) {
683 for (const DWARF5AcceleratorTable::HashData *Hash : Bucket)
684 support::endian::write(os&: *StrStream, value: Hash->HashValue,
685 endian: llvm::endianness::little);
686 }
687}
688void DWARF5AcceleratorTable::emitStringOffsets() const {
689 for (const auto &Bucket : getBuckets()) {
690 for (const DWARF5AcceleratorTable::HashData *Hash : Bucket)
691 support::endian::write(os&: *StrStream, value: static_cast<uint32_t>(Hash->StrOffset),
692 endian: llvm::endianness::little);
693 }
694}
695void DWARF5AcceleratorTable::emitOffsets() const {
696 for (const auto &Bucket : getBuckets()) {
697 for (const DWARF5AcceleratorTable::HashData *Hash : Bucket)
698 support::endian::write(os&: *StrStream,
699 value: static_cast<uint32_t>(Hash->EntryOffset),
700 endian: llvm::endianness::little);
701 }
702}
703void DWARF5AcceleratorTable::emitAbbrevs() {
704 const uint32_t AbbrevTableStart = StrBuffer->size();
705 for (const auto *Abbrev : AbbreviationsVector) {
706 encodeULEB128(Value: Abbrev->getNumber(), OS&: *StrStream);
707 encodeULEB128(Value: Abbrev->getDieTag(), OS&: *StrStream);
708 for (const auto &AttrEnc : Abbrev->getAttributes()) {
709 encodeULEB128(Value: AttrEnc.Index, OS&: *StrStream);
710 encodeULEB128(Value: AttrEnc.Form, OS&: *StrStream);
711 }
712 encodeULEB128(Value: 0, OS&: *StrStream);
713 encodeULEB128(Value: 0, OS&: *StrStream);
714 }
715 encodeULEB128(Value: 0, OS&: *StrStream);
716 AbbrevTableSize = StrBuffer->size() - AbbrevTableStart;
717}
718void DWARF5AcceleratorTable::emitData() {
719 StrStream->write(Ptr: EntriesBuffer->data(), Size: EntriesBuffer->size());
720}
721void DWARF5AcceleratorTable::emitAugmentationString() const {
722 FullTableStream->write(Ptr: AugStringBuffer->data(), Size: AugStringBuffer->size());
723}
724void DWARF5AcceleratorTable::emitAccelTable() {
725 if (!NeedToCreate)
726 return;
727 finalize();
728 populateAbbrevsMap();
729 writeEntries();
730 writeAugmentationString();
731 emitCUList();
732 emitTUList();
733 emitBuckets();
734 emitHashes();
735 emitStringOffsets();
736 emitOffsets();
737 emitAbbrevs();
738 emitData();
739 emitHeader();
740}
741} // namespace bolt
742} // namespace llvm
743

source code of bolt/lib/Core/DebugNames.cpp