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 | |
13 | using namespace llvm; |
14 | using namespace dwarf_linker; |
15 | using namespace dwarf_linker::parallel; |
16 | |
17 | TypeUnit::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 | |
41 | void 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 | |
130 | void 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 | |
239 | uint64_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 | |
264 | uint32_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 | |
303 | std::pair<dwarf::Form, uint8_t> |
304 | TypeUnit::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 | |
317 | uint8_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 | |
336 | Error 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 | |