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