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 | |
18 | namespace llvm { |
19 | namespace bolt { |
20 | DWARF5AcceleratorTable::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 | |
67 | void 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 | |
82 | void 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. |
114 | static 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 | |
149 | bool 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 | |
186 | bool 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. |
199 | static 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 | |
216 | static uint64_t getEntryID(const BOLTDWARF5AccelTableData &Entry) { |
217 | return reinterpret_cast<uint64_t>(&Entry); |
218 | } |
219 | |
220 | std::optional<BOLTDWARF5AccelTableData *> |
221 | DWARF5AcceleratorTable::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. |
366 | void 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() |
387 | void 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 | |
423 | std::optional<DWARF5AccelTable::UnitIndexAndEncoding> |
424 | DWARF5AcceleratorTable::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 | |
438 | std::optional<DWARF5AccelTable::UnitIndexAndEncoding> |
439 | DWARF5AcceleratorTable::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 | |
447 | void 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 | |
487 | void 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 | |
561 | void 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 | |
595 | void 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. |
602 | static constexpr uint32_t () { |
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 | |
618 | void DWARF5AcceleratorTable::() const { |
619 | constexpr uint32_t = 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 | |
662 | void DWARF5AcceleratorTable::emitCUList() const { |
663 | for (const uint32_t CUID : CUList) |
664 | support::endian::write(os&: *StrStream, value: CUID, endian: llvm::endianness::little); |
665 | } |
666 | void 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 | } |
673 | void 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 | } |
681 | void 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 | } |
688 | void 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 | } |
695 | void 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 | } |
703 | void 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 | } |
718 | void DWARF5AcceleratorTable::emitData() { |
719 | StrStream->write(Ptr: EntriesBuffer->data(), Size: EntriesBuffer->size()); |
720 | } |
721 | void DWARF5AcceleratorTable::emitAugmentationString() const { |
722 | FullTableStream->write(Ptr: AugStringBuffer->data(), Size: AugStringBuffer->size()); |
723 | } |
724 | void 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 | |