1//===- DWARFLinkerTypeUnit.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 "DWARFLinkerTypeUnit.h"
10#include "DIEGenerator.h"
11#include "llvm/Support/LEB128.h"
12
13using namespace llvm;
14using namespace dwarf_linker;
15using namespace dwarf_linker::parallel;
16
17TypeUnit::TypeUnit(LinkingGlobalData &GlobalData, unsigned ID,
18 std::optional<uint16_t> Language, dwarf::FormParams Format,
19 endianness Endianess)
20 : DwarfUnit(GlobalData, ID, ""), Language(Language),
21 AcceleratorRecords(&GlobalData.getAllocator()) {
22
23 UnitName = "__artificial_type_unit";
24
25 setOutputFormat(Format, Endianness: Endianess);
26
27 // Create line table prologue.
28 LineTable.Prologue.FormParams = getFormParams();
29 LineTable.Prologue.MinInstLength = 1;
30 LineTable.Prologue.MaxOpsPerInst = 1;
31 LineTable.Prologue.DefaultIsStmt = 1;
32 LineTable.Prologue.LineBase = -5;
33 LineTable.Prologue.LineRange = 14;
34 LineTable.Prologue.OpcodeBase = 13;
35 LineTable.Prologue.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0,
36 0, 0, 1, 0, 0, 1};
37
38 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugInfo);
39}
40
41void TypeUnit::createDIETree(BumpPtrAllocator &Allocator) {
42 prepareDataForTreeCreation();
43
44 // TaskGroup is created here as internal code has calls to
45 // PerThreadBumpPtrAllocator which should be called from the task group task.
46 llvm::parallel::TaskGroup TG;
47 TG.spawn(f: [&]() {
48 SectionDescriptor &DebugInfoSection =
49 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugInfo);
50 SectionDescriptor &DebugLineSection =
51 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugLine);
52
53 DIEGenerator DIETreeGenerator(Allocator, *this);
54 OffsetsPtrVector PatchesOffsets;
55
56 // Create a Die for artificial compilation unit for types.
57 DIE *UnitDIE = DIETreeGenerator.createDIE(DieTag: dwarf::DW_TAG_compile_unit, OutOffset: 0);
58 uint64_t OutOffset = getDebugInfoHeaderSize();
59 UnitDIE->setOffset(OutOffset);
60
61 SmallString<200> ProducerString;
62 ProducerString += "llvm DWARFLinkerParallel library version ";
63 DebugInfoSection.notePatchWithOffsetUpdate(
64 Patch: DebugStrPatch{
65 {.PatchOffset: OutOffset},
66 .String: GlobalData.getStringPool().insert(NewValue: ProducerString.str()).first},
67 PatchesOffsetsList&: PatchesOffsets);
68 OutOffset += DIETreeGenerator
69 .addStringPlaceholderAttribute(Attr: dwarf::DW_AT_producer,
70 AttrForm: dwarf::DW_FORM_strp)
71 .second;
72
73 if (Language) {
74 OutOffset += DIETreeGenerator
75 .addScalarAttribute(Attr: dwarf::DW_AT_language,
76 AttrForm: dwarf::DW_FORM_data2, Value: *Language)
77 .second;
78 }
79
80 DebugInfoSection.notePatchWithOffsetUpdate(
81 Patch: DebugStrPatch{{.PatchOffset: OutOffset},
82 .String: GlobalData.getStringPool().insert(NewValue: getUnitName()).first},
83 PatchesOffsetsList&: PatchesOffsets);
84 OutOffset += DIETreeGenerator
85 .addStringPlaceholderAttribute(Attr: dwarf::DW_AT_name,
86 AttrForm: dwarf::DW_FORM_strp)
87 .second;
88
89 if (!LineTable.Prologue.FileNames.empty()) {
90 DebugInfoSection.notePatchWithOffsetUpdate(
91 Patch: DebugOffsetPatch{OutOffset, &DebugLineSection}, PatchesOffsetsList&: PatchesOffsets);
92
93 OutOffset += DIETreeGenerator
94 .addScalarAttribute(Attr: dwarf::DW_AT_stmt_list,
95 AttrForm: dwarf::DW_FORM_sec_offset, Value: 0xbaddef)
96 .second;
97 }
98
99 DebugInfoSection.notePatchWithOffsetUpdate(
100 Patch: DebugStrPatch{{.PatchOffset: OutOffset}, .String: GlobalData.getStringPool().insert(NewValue: "").first},
101 PatchesOffsetsList&: PatchesOffsets);
102 OutOffset += DIETreeGenerator
103 .addStringPlaceholderAttribute(Attr: dwarf::DW_AT_comp_dir,
104 AttrForm: dwarf::DW_FORM_strp)
105 .second;
106
107 if (!DebugStringIndexMap.empty()) {
108 // Type unit is assumed to be emitted first. Thus we can use direct value
109 // for DW_AT_str_offsets_base attribute(No need to fix it up with unit
110 // offset value).
111 OutOffset += DIETreeGenerator
112 .addScalarAttribute(Attr: dwarf::DW_AT_str_offsets_base,
113 AttrForm: dwarf::DW_FORM_sec_offset,
114 Value: getDebugStrOffsetsHeaderSize())
115 .second;
116 }
117
118 UnitDIE->setSize(OutOffset - UnitDIE->getOffset() + 1);
119 OutOffset =
120 finalizeTypeEntryRec(OutOffset: UnitDIE->getOffset(), OutDIE: UnitDIE, Entry: Types.getRoot());
121
122 // Update patch offsets.
123 for (uint64_t *OffsetPtr : PatchesOffsets)
124 *OffsetPtr += getULEB128Size(Value: UnitDIE->getAbbrevNumber());
125
126 setOutUnitDIE(UnitDIE);
127 });
128}
129
130void TypeUnit::prepareDataForTreeCreation() {
131 SectionDescriptor &DebugInfoSection =
132 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugInfo);
133
134 // Type unit data created parallelly. So the order of data is not
135 // deterministic. Order data here if we need deterministic output.
136
137 llvm::parallel::TaskGroup TG;
138
139 if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
140 TG.spawn(f: [&]() {
141 // Sort types to have a deterministic output.
142 Types.sortTypes();
143 });
144 }
145
146 TG.spawn(f: [&]() {
147 if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
148 // Sort decl type patches to have a deterministic output.
149 std::function<bool(const DebugTypeDeclFilePatch &LHS,
150 const DebugTypeDeclFilePatch &RHS)>
151 PatchesComparator = [&](const DebugTypeDeclFilePatch &LHS,
152 const DebugTypeDeclFilePatch &RHS) {
153 return LHS.Directory->first() < RHS.Directory->first() ||
154 (!(RHS.Directory->first() < LHS.Directory->first()) &&
155 LHS.FilePath->first() < RHS.FilePath->first());
156 };
157 // Sort patches to have a deterministic output.
158 DebugInfoSection.ListDebugTypeDeclFilePatch.sort(Comparator: PatchesComparator);
159 }
160
161 // Update DW_AT_decl_file attribute
162 dwarf::Form DeclFileForm =
163 getScalarFormForValue(
164 Value: DebugInfoSection.ListDebugTypeDeclFilePatch.size())
165 .first;
166
167 DebugInfoSection.ListDebugTypeDeclFilePatch.forEach(
168 Handler: [&](DebugTypeDeclFilePatch &Patch) {
169 TypeEntryBody *TypeEntry = Patch.TypeName->getValue().load();
170 assert(TypeEntry &&
171 formatv("No data for type {0}", Patch.TypeName->getKey())
172 .str()
173 .c_str());
174 if (&TypeEntry->getFinalDie() != Patch.Die)
175 return;
176
177 uint32_t FileIdx =
178 addFileNameIntoLinetable(Dir: Patch.Directory, FileName: Patch.FilePath);
179
180 unsigned DIESize = Patch.Die->getSize();
181 DIEGenerator DIEGen(Patch.Die, Types.getThreadLocalAllocator(),
182 *this);
183
184 DIESize += DIEGen
185 .addScalarAttribute(Attr: dwarf::DW_AT_decl_file,
186 AttrForm: DeclFileForm, Value: FileIdx)
187 .second;
188 Patch.Die->setSize(DIESize);
189 });
190 });
191
192 if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
193 // Sort patches to have a deterministic output.
194 TG.spawn(f: [&]() {
195 forEach(Handler: [&](SectionDescriptor &OutSection) {
196 std::function<bool(const DebugStrPatch &LHS, const DebugStrPatch &RHS)>
197 StrPatchesComparator =
198 [&](const DebugStrPatch &LHS, const DebugStrPatch &RHS) {
199 return LHS.String->getKey() < RHS.String->getKey();
200 };
201 OutSection.ListDebugStrPatch.sort(Comparator: StrPatchesComparator);
202
203 std::function<bool(const DebugTypeStrPatch &LHS,
204 const DebugTypeStrPatch &RHS)>
205 TypeStrPatchesComparator = [&](const DebugTypeStrPatch &LHS,
206 const DebugTypeStrPatch &RHS) {
207 return LHS.String->getKey() < RHS.String->getKey();
208 };
209 OutSection.ListDebugTypeStrPatch.sort(Comparator: TypeStrPatchesComparator);
210 });
211 });
212 }
213
214 if (!GlobalData.getOptions().AllowNonDeterministicOutput) {
215 // Sort patches to have a deterministic output.
216 TG.spawn(f: [&]() {
217 forEach(Handler: [&](SectionDescriptor &OutSection) {
218 std::function<bool(const DebugLineStrPatch &LHS,
219 const DebugLineStrPatch &RHS)>
220 LineStrPatchesComparator = [&](const DebugLineStrPatch &LHS,
221 const DebugLineStrPatch &RHS) {
222 return LHS.String->getKey() < RHS.String->getKey();
223 };
224 OutSection.ListDebugLineStrPatch.sort(Comparator: LineStrPatchesComparator);
225
226 std::function<bool(const DebugTypeLineStrPatch &LHS,
227 const DebugTypeLineStrPatch &RHS)>
228 TypeLineStrPatchesComparator =
229 [&](const DebugTypeLineStrPatch &LHS,
230 const DebugTypeLineStrPatch &RHS) {
231 return LHS.String->getKey() < RHS.String->getKey();
232 };
233 OutSection.ListDebugTypeLineStrPatch.sort(Comparator: TypeLineStrPatchesComparator);
234 });
235 });
236 }
237}
238
239uint64_t TypeUnit::finalizeTypeEntryRec(uint64_t OutOffset, DIE *OutDIE,
240 TypeEntry *Entry) {
241 bool HasChildren = !Entry->getValue().load()->Children.empty();
242 DIEGenerator DIEGen(OutDIE, Types.getThreadLocalAllocator(), *this);
243 OutOffset += DIEGen.finalizeAbbreviations(CHILDREN_yes: HasChildren, OffsetsList: nullptr);
244 OutOffset += OutDIE->getSize() - 1;
245
246 if (HasChildren) {
247 Entry->getValue().load()->Children.forEach(Handler: [&](TypeEntry *ChildEntry) {
248 DIE *ChildDIE = &ChildEntry->getValue().load()->getFinalDie();
249 DIEGen.addChild(Child: ChildDIE);
250
251 ChildDIE->setOffset(OutOffset);
252
253 OutOffset = finalizeTypeEntryRec(OutOffset, OutDIE: ChildDIE, Entry: ChildEntry);
254 });
255
256 // End of children marker.
257 OutOffset += sizeof(int8_t);
258 }
259
260 OutDIE->setSize(OutOffset - OutDIE->getOffset());
261 return OutOffset;
262}
263
264uint32_t TypeUnit::addFileNameIntoLinetable(StringEntry *Dir,
265 StringEntry *FileName) {
266 uint32_t DirIdx = 0;
267
268 if (Dir->first() == "") {
269 DirIdx = 0;
270 } else {
271 DirectoriesMapTy::iterator DirEntry = DirectoriesMap.find(x: Dir);
272 if (DirEntry == DirectoriesMap.end()) {
273 // We currently do not support more than UINT32_MAX directories.
274 assert(LineTable.Prologue.IncludeDirectories.size() < UINT32_MAX);
275 DirIdx = LineTable.Prologue.IncludeDirectories.size();
276 DirectoriesMap.insert(x: {Dir, DirIdx});
277 LineTable.Prologue.IncludeDirectories.push_back(
278 x: DWARFFormValue::createFromPValue(F: dwarf::DW_FORM_string,
279 V: Dir->getKeyData()));
280 } else {
281 DirIdx = DirEntry->second;
282 }
283
284 if (getVersion() < 5)
285 DirIdx++;
286 }
287
288 auto [FileEntry, Inserted] = FileNamesMap.try_emplace(
289 k: {FileName, DirIdx}, args: LineTable.Prologue.FileNames.size());
290 if (Inserted) {
291 // We currently do not support more than UINT32_MAX files.
292 assert(LineTable.Prologue.FileNames.size() < UINT32_MAX);
293 LineTable.Prologue.FileNames.push_back(x: DWARFDebugLine::FileNameEntry());
294 LineTable.Prologue.FileNames.back().Name = DWARFFormValue::createFromPValue(
295 F: dwarf::DW_FORM_string, V: FileName->getKeyData());
296 LineTable.Prologue.FileNames.back().DirIdx = DirIdx;
297 }
298
299 uint32_t FileIdx = FileEntry->second;
300 return getVersion() < 5 ? FileIdx + 1 : FileIdx;
301}
302
303std::pair<dwarf::Form, uint8_t>
304TypeUnit::getScalarFormForValue(uint64_t Value) const {
305 if (Value > 0xFFFFFFFF)
306 return std::make_pair(x: dwarf::DW_FORM_data8, y: 8);
307
308 if (Value > 0xFFFF)
309 return std::make_pair(x: dwarf::DW_FORM_data4, y: 4);
310
311 if (Value > 0xFF)
312 return std::make_pair(x: dwarf::DW_FORM_data2, y: 2);
313
314 return std::make_pair(x: dwarf::DW_FORM_data1, y: 1);
315}
316
317uint8_t TypeUnit::getSizeByAttrForm(dwarf::Form Form) const {
318 if (Form == dwarf::DW_FORM_data1)
319 return 1;
320
321 if (Form == dwarf::DW_FORM_data2)
322 return 2;
323
324 if (Form == dwarf::DW_FORM_data4)
325 return 4;
326
327 if (Form == dwarf::DW_FORM_data8)
328 return 8;
329
330 if (Form == dwarf::DW_FORM_data16)
331 return 16;
332
333 llvm_unreachable("Unsupported Attr Form");
334}
335
336Error TypeUnit::finishCloningAndEmit(const Triple &TargetTriple) {
337 BumpPtrAllocator Allocator;
338 createDIETree(Allocator);
339
340 if (getOutUnitDIE() == nullptr)
341 return Error::success();
342
343 // Create sections ahead so that they should not be created asynchronously
344 // later.
345 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugInfo);
346 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugLine);
347 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugStrOffsets);
348 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugAbbrev);
349 if (llvm::is_contained(Range: GlobalData.getOptions().AccelTables,
350 Element: DWARFLinker::AccelTableKind::Pub)) {
351 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugPubNames);
352 getOrCreateSectionDescriptor(SectionKind: DebugSectionKind::DebugPubTypes);
353 }
354
355 SmallVector<std::function<Error(void)>> Tasks;
356
357 // Add task for emitting .debug_line section.
358 if (!LineTable.Prologue.FileNames.empty()) {
359 Tasks.push_back(
360 Elt: [&]() -> Error { return emitDebugLine(TargetTriple, OutLineTable: LineTable); });
361 }
362
363 // Add task for emitting .debug_info section.
364 Tasks.push_back(Elt: [&]() -> Error { return emitDebugInfo(TargetTriple); });
365
366 // Add task for emitting Pub accelerator sections.
367 if (llvm::is_contained(Range: GlobalData.getOptions().AccelTables,
368 Element: DWARFLinker::AccelTableKind::Pub)) {
369 Tasks.push_back(Elt: [&]() -> Error {
370 emitPubAccelerators();
371 return Error::success();
372 });
373 }
374
375 // Add task for emitting .debug_str_offsets section.
376 Tasks.push_back(Elt: [&]() -> Error { return emitDebugStringOffsetSection(); });
377
378 // Add task for emitting .debug_abbr section.
379 Tasks.push_back(Elt: [&]() -> Error { return emitAbbreviations(); });
380
381 if (auto Err = parallelForEachError(
382 R&: Tasks, Fn: [&](std::function<Error(void)> F) { return F(); }))
383 return Err;
384
385 return Error::success();
386}
387