1 | //===-- DWARFDIE.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 "DWARFDIE.h" |
10 | |
11 | #include "DWARFASTParser.h" |
12 | #include "DWARFDebugInfo.h" |
13 | #include "DWARFDebugInfoEntry.h" |
14 | #include "DWARFDeclContext.h" |
15 | #include "DWARFUnit.h" |
16 | |
17 | #include "llvm/ADT/iterator.h" |
18 | |
19 | using namespace lldb_private; |
20 | using namespace lldb_private::dwarf; |
21 | using namespace lldb_private::plugin::dwarf; |
22 | |
23 | namespace { |
24 | |
25 | /// Iterate through all DIEs elaborating (i.e. reachable by a chain of |
26 | /// DW_AT_specification and DW_AT_abstract_origin attributes) a given DIE. For |
27 | /// convenience, the starting die is included in the sequence as the first |
28 | /// item. |
29 | class ElaboratingDIEIterator |
30 | : public llvm::iterator_facade_base< |
31 | ElaboratingDIEIterator, std::input_iterator_tag, DWARFDIE, |
32 | std::ptrdiff_t, DWARFDIE *, DWARFDIE *> { |
33 | |
34 | // The operating invariant is: top of m_worklist contains the "current" item |
35 | // and the rest of the list are items yet to be visited. An empty worklist |
36 | // means we've reached the end. |
37 | // Infinite recursion is prevented by maintaining a list of seen DIEs. |
38 | // Container sizes are optimized for the case of following DW_AT_specification |
39 | // and DW_AT_abstract_origin just once. |
40 | llvm::SmallVector<DWARFDIE, 2> m_worklist; |
41 | llvm::SmallSet<DWARFDebugInfoEntry *, 3> m_seen; |
42 | |
43 | void Next() { |
44 | assert(!m_worklist.empty() && "Incrementing end iterator?" ); |
45 | |
46 | // Pop the current item from the list. |
47 | DWARFDIE die = m_worklist.back(); |
48 | m_worklist.pop_back(); |
49 | |
50 | // And add back any items that elaborate it. |
51 | for (dw_attr_t attr : {DW_AT_specification, DW_AT_abstract_origin}) { |
52 | if (DWARFDIE d = die.GetReferencedDIE(attr)) |
53 | if (m_seen.insert(Ptr: die.GetDIE()).second) |
54 | m_worklist.push_back(Elt: d); |
55 | } |
56 | } |
57 | |
58 | public: |
59 | /// An iterator starting at die d. |
60 | explicit ElaboratingDIEIterator(DWARFDIE d) : m_worklist(1, d) {} |
61 | |
62 | /// End marker |
63 | ElaboratingDIEIterator() = default; |
64 | |
65 | const DWARFDIE &operator*() const { return m_worklist.back(); } |
66 | ElaboratingDIEIterator &operator++() { |
67 | Next(); |
68 | return *this; |
69 | } |
70 | |
71 | friend bool operator==(const ElaboratingDIEIterator &a, |
72 | const ElaboratingDIEIterator &b) { |
73 | if (a.m_worklist.empty() || b.m_worklist.empty()) |
74 | return a.m_worklist.empty() == b.m_worklist.empty(); |
75 | return a.m_worklist.back() == b.m_worklist.back(); |
76 | } |
77 | }; |
78 | |
79 | llvm::iterator_range<ElaboratingDIEIterator> |
80 | elaborating_dies(const DWARFDIE &die) { |
81 | return llvm::make_range(x: ElaboratingDIEIterator(die), |
82 | y: ElaboratingDIEIterator()); |
83 | } |
84 | } // namespace |
85 | |
86 | DWARFDIE |
87 | DWARFDIE::GetParent() const { |
88 | if (IsValid()) |
89 | return DWARFDIE(m_cu, m_die->GetParent()); |
90 | else |
91 | return DWARFDIE(); |
92 | } |
93 | |
94 | DWARFDIE |
95 | DWARFDIE::GetFirstChild() const { |
96 | if (IsValid()) |
97 | return DWARFDIE(m_cu, m_die->GetFirstChild()); |
98 | else |
99 | return DWARFDIE(); |
100 | } |
101 | |
102 | DWARFDIE |
103 | DWARFDIE::GetSibling() const { |
104 | if (IsValid()) |
105 | return DWARFDIE(m_cu, m_die->GetSibling()); |
106 | else |
107 | return DWARFDIE(); |
108 | } |
109 | |
110 | DWARFDIE |
111 | DWARFDIE::GetReferencedDIE(const dw_attr_t attr) const { |
112 | if (IsValid()) |
113 | return m_die->GetAttributeValueAsReference(cu: GetCU(), attr); |
114 | else |
115 | return {}; |
116 | } |
117 | |
118 | DWARFDIE |
119 | DWARFDIE::GetDIE(dw_offset_t die_offset) const { |
120 | if (IsValid()) |
121 | return m_cu->GetDIE(die_offset); |
122 | else |
123 | return DWARFDIE(); |
124 | } |
125 | |
126 | DWARFDIE |
127 | DWARFDIE::GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const { |
128 | if (IsValid()) { |
129 | DWARFUnit *cu = GetCU(); |
130 | const bool check_specification_or_abstract_origin = true; |
131 | DWARFFormValue form_value; |
132 | if (m_die->GetAttributeValue(cu, attr, formValue&: form_value, end_attr_offset_ptr: nullptr, |
133 | check_specification_or_abstract_origin)) |
134 | return form_value.Reference(); |
135 | } |
136 | return DWARFDIE(); |
137 | } |
138 | |
139 | DWARFDIE |
140 | DWARFDIE::LookupDeepestBlock(lldb::addr_t address) const { |
141 | if (!IsValid()) |
142 | return DWARFDIE(); |
143 | |
144 | DWARFDIE result; |
145 | bool check_children = false; |
146 | bool match_addr_range = false; |
147 | switch (Tag()) { |
148 | case DW_TAG_class_type: |
149 | case DW_TAG_namespace: |
150 | case DW_TAG_structure_type: |
151 | case DW_TAG_common_block: |
152 | check_children = true; |
153 | break; |
154 | case DW_TAG_compile_unit: |
155 | case DW_TAG_module: |
156 | case DW_TAG_catch_block: |
157 | case DW_TAG_subprogram: |
158 | case DW_TAG_try_block: |
159 | case DW_TAG_partial_unit: |
160 | match_addr_range = true; |
161 | break; |
162 | case DW_TAG_lexical_block: |
163 | case DW_TAG_inlined_subroutine: |
164 | check_children = true; |
165 | match_addr_range = true; |
166 | break; |
167 | default: |
168 | break; |
169 | } |
170 | |
171 | if (match_addr_range) { |
172 | DWARFRangeList ranges = |
173 | m_die->GetAttributeAddressRanges(cu: m_cu, /*check_hi_lo_pc=*/true); |
174 | if (ranges.FindEntryThatContains(addr: address)) { |
175 | check_children = true; |
176 | switch (Tag()) { |
177 | default: |
178 | break; |
179 | |
180 | case DW_TAG_inlined_subroutine: // Inlined Function |
181 | case DW_TAG_lexical_block: // Block { } in code |
182 | result = *this; |
183 | break; |
184 | } |
185 | } else { |
186 | check_children = false; |
187 | } |
188 | } |
189 | |
190 | if (check_children) { |
191 | for (DWARFDIE child : children()) { |
192 | if (DWARFDIE child_result = child.LookupDeepestBlock(address)) |
193 | return child_result; |
194 | } |
195 | } |
196 | return result; |
197 | } |
198 | |
199 | const char *DWARFDIE::GetMangledName() const { |
200 | if (IsValid()) |
201 | return m_die->GetMangledName(cu: m_cu); |
202 | else |
203 | return nullptr; |
204 | } |
205 | |
206 | const char *DWARFDIE::GetPubname() const { |
207 | if (IsValid()) |
208 | return m_die->GetPubname(cu: m_cu); |
209 | else |
210 | return nullptr; |
211 | } |
212 | |
213 | // GetName |
214 | // |
215 | // Get value of the DW_AT_name attribute and place that value into the supplied |
216 | // stream object. If the DIE is a NULL object "NULL" is placed into the stream, |
217 | // and if no DW_AT_name attribute exists for the DIE then nothing is printed. |
218 | void DWARFDIE::GetName(Stream &s) const { |
219 | if (!IsValid()) |
220 | return; |
221 | if (GetDIE()->IsNULL()) { |
222 | s.PutCString(cstr: "NULL" ); |
223 | return; |
224 | } |
225 | const char *name = GetDIE()->GetAttributeValueAsString(cu: GetCU(), attr: DW_AT_name, fail_value: nullptr, check_specification_or_abstract_origin: true); |
226 | if (!name) |
227 | return; |
228 | s.PutCString(cstr: name); |
229 | } |
230 | |
231 | // AppendTypeName |
232 | // |
233 | // Follows the type name definition down through all needed tags to end up with |
234 | // a fully qualified type name and dump the results to the supplied stream. |
235 | // This is used to show the name of types given a type identifier. |
236 | void DWARFDIE::AppendTypeName(Stream &s) const { |
237 | if (!IsValid()) |
238 | return; |
239 | if (GetDIE()->IsNULL()) { |
240 | s.PutCString(cstr: "NULL" ); |
241 | return; |
242 | } |
243 | if (const char *name = GetPubname()) { |
244 | s.PutCString(cstr: name); |
245 | return; |
246 | } |
247 | switch (Tag()) { |
248 | case DW_TAG_array_type: |
249 | break; // print out a "[]" after printing the full type of the element |
250 | // below |
251 | case DW_TAG_base_type: |
252 | s.PutCString(cstr: "base " ); |
253 | break; |
254 | case DW_TAG_class_type: |
255 | s.PutCString(cstr: "class " ); |
256 | break; |
257 | case DW_TAG_const_type: |
258 | s.PutCString(cstr: "const " ); |
259 | break; |
260 | case DW_TAG_enumeration_type: |
261 | s.PutCString(cstr: "enum " ); |
262 | break; |
263 | case DW_TAG_file_type: |
264 | s.PutCString(cstr: "file " ); |
265 | break; |
266 | case DW_TAG_interface_type: |
267 | s.PutCString(cstr: "interface " ); |
268 | break; |
269 | case DW_TAG_packed_type: |
270 | s.PutCString(cstr: "packed " ); |
271 | break; |
272 | case DW_TAG_pointer_type: |
273 | break; // print out a '*' after printing the full type below |
274 | case DW_TAG_ptr_to_member_type: |
275 | break; // print out a '*' after printing the full type below |
276 | case DW_TAG_reference_type: |
277 | break; // print out a '&' after printing the full type below |
278 | case DW_TAG_restrict_type: |
279 | s.PutCString(cstr: "restrict " ); |
280 | break; |
281 | case DW_TAG_set_type: |
282 | s.PutCString(cstr: "set " ); |
283 | break; |
284 | case DW_TAG_shared_type: |
285 | s.PutCString(cstr: "shared " ); |
286 | break; |
287 | case DW_TAG_string_type: |
288 | s.PutCString(cstr: "string " ); |
289 | break; |
290 | case DW_TAG_structure_type: |
291 | s.PutCString(cstr: "struct " ); |
292 | break; |
293 | case DW_TAG_subrange_type: |
294 | s.PutCString(cstr: "subrange " ); |
295 | break; |
296 | case DW_TAG_subroutine_type: |
297 | s.PutCString(cstr: "function " ); |
298 | break; |
299 | case DW_TAG_thrown_type: |
300 | s.PutCString(cstr: "thrown " ); |
301 | break; |
302 | case DW_TAG_union_type: |
303 | s.PutCString(cstr: "union " ); |
304 | break; |
305 | case DW_TAG_unspecified_type: |
306 | s.PutCString(cstr: "unspecified " ); |
307 | break; |
308 | case DW_TAG_volatile_type: |
309 | s.PutCString(cstr: "volatile " ); |
310 | break; |
311 | case DW_TAG_LLVM_ptrauth_type: { |
312 | unsigned key = GetAttributeValueAsUnsigned(attr: DW_AT_LLVM_ptrauth_key, fail_value: 0); |
313 | bool isAddressDiscriminated = GetAttributeValueAsUnsigned( |
314 | attr: DW_AT_LLVM_ptrauth_address_discriminated, fail_value: 0); |
315 | unsigned = |
316 | GetAttributeValueAsUnsigned(attr: DW_AT_LLVM_ptrauth_extra_discriminator, fail_value: 0); |
317 | bool isaPointer = |
318 | GetAttributeValueAsUnsigned(attr: DW_AT_LLVM_ptrauth_isa_pointer, fail_value: 0); |
319 | bool authenticatesNullValues = GetAttributeValueAsUnsigned( |
320 | attr: DW_AT_LLVM_ptrauth_authenticates_null_values, fail_value: 0); |
321 | unsigned authenticationMode = |
322 | GetAttributeValueAsUnsigned(attr: DW_AT_LLVM_ptrauth_authentication_mode, fail_value: 3); |
323 | |
324 | s.Printf(format: "__ptrauth(%d, %d, 0x0%x, %d, %d, %d)" , key, |
325 | isAddressDiscriminated, extraDiscriminator, isaPointer, |
326 | authenticatesNullValues, authenticationMode); |
327 | break; |
328 | } |
329 | default: |
330 | return; |
331 | } |
332 | |
333 | // Follow the DW_AT_type if possible |
334 | if (DWARFDIE next_die = GetAttributeValueAsReferenceDIE(attr: DW_AT_type)) |
335 | next_die.AppendTypeName(s); |
336 | |
337 | switch (Tag()) { |
338 | case DW_TAG_array_type: |
339 | s.PutCString(cstr: "[]" ); |
340 | break; |
341 | case DW_TAG_pointer_type: |
342 | s.PutChar(ch: '*'); |
343 | break; |
344 | case DW_TAG_ptr_to_member_type: |
345 | s.PutChar(ch: '*'); |
346 | break; |
347 | case DW_TAG_reference_type: |
348 | s.PutChar(ch: '&'); |
349 | break; |
350 | default: |
351 | break; |
352 | } |
353 | } |
354 | |
355 | lldb_private::Type *DWARFDIE::ResolveType() const { |
356 | if (IsValid()) |
357 | return GetDWARF()->ResolveType(die: *this, assert_not_being_parsed: true); |
358 | else |
359 | return nullptr; |
360 | } |
361 | |
362 | lldb_private::Type *DWARFDIE::ResolveTypeUID(const DWARFDIE &die) const { |
363 | if (SymbolFileDWARF *dwarf = GetDWARF()) |
364 | return dwarf->ResolveTypeUID(die, assert_not_being_parsed: true); |
365 | return nullptr; |
366 | } |
367 | |
368 | std::vector<DWARFDIE> DWARFDIE::GetDeclContextDIEs() const { |
369 | if (!IsValid()) |
370 | return {}; |
371 | |
372 | std::vector<DWARFDIE> result; |
373 | DWARFDIE parent = GetParentDeclContextDIE(); |
374 | while (parent.IsValid() && parent.GetDIE() != GetDIE()) { |
375 | result.push_back(x: std::move(parent)); |
376 | parent = parent.GetParentDeclContextDIE(); |
377 | } |
378 | |
379 | return result; |
380 | } |
381 | |
382 | static std::vector<lldb_private::CompilerContext> |
383 | GetDeclContextImpl(llvm::SmallSet<lldb::user_id_t, 4> &seen, DWARFDIE die) { |
384 | std::vector<lldb_private::CompilerContext> context; |
385 | // Stop if we hit a cycle. |
386 | if (!die || !seen.insert(V: die.GetID()).second) |
387 | return context; |
388 | |
389 | // Handle outline member function DIEs by following the specification. |
390 | if (DWARFDIE spec = die.GetReferencedDIE(attr: DW_AT_specification)) |
391 | return GetDeclContextImpl(seen, die: spec); |
392 | |
393 | // Get the parent context chain. |
394 | context = GetDeclContextImpl(seen, die: die.GetParent()); |
395 | |
396 | // Add this DIE's contribution at the end of the chain. |
397 | auto push_ctx = [&](CompilerContextKind kind, llvm::StringRef name) { |
398 | context.push_back(x: {kind, ConstString(name)}); |
399 | }; |
400 | switch (die.Tag()) { |
401 | case DW_TAG_module: |
402 | push_ctx(CompilerContextKind::Module, die.GetName()); |
403 | break; |
404 | case DW_TAG_namespace: |
405 | push_ctx(CompilerContextKind::Namespace, die.GetName()); |
406 | break; |
407 | case DW_TAG_structure_type: |
408 | push_ctx(CompilerContextKind::Struct, die.GetName()); |
409 | break; |
410 | case DW_TAG_union_type: |
411 | push_ctx(CompilerContextKind::Union, die.GetName()); |
412 | break; |
413 | case DW_TAG_class_type: |
414 | push_ctx(CompilerContextKind::Class, die.GetName()); |
415 | break; |
416 | case DW_TAG_enumeration_type: |
417 | push_ctx(CompilerContextKind::Enum, die.GetName()); |
418 | break; |
419 | case DW_TAG_subprogram: |
420 | push_ctx(CompilerContextKind::Function, die.GetName()); |
421 | break; |
422 | case DW_TAG_variable: |
423 | push_ctx(CompilerContextKind::Variable, die.GetPubname()); |
424 | break; |
425 | case DW_TAG_typedef: |
426 | push_ctx(CompilerContextKind::Typedef, die.GetName()); |
427 | break; |
428 | default: |
429 | break; |
430 | } |
431 | return context; |
432 | } |
433 | |
434 | std::vector<lldb_private::CompilerContext> DWARFDIE::GetDeclContext() const { |
435 | llvm::SmallSet<lldb::user_id_t, 4> seen; |
436 | return GetDeclContextImpl(seen, die: *this); |
437 | } |
438 | |
439 | std::vector<lldb_private::CompilerContext> |
440 | DWARFDIE::GetTypeLookupContext() const { |
441 | std::vector<lldb_private::CompilerContext> context; |
442 | // If there is no name, then there is no need to look anything up for this |
443 | // DIE. |
444 | const char *name = GetName(); |
445 | if (!name || !name[0]) |
446 | return context; |
447 | const dw_tag_t tag = Tag(); |
448 | if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit) |
449 | return context; |
450 | DWARFDIE parent = GetParent(); |
451 | if (parent) |
452 | context = parent.GetTypeLookupContext(); |
453 | auto push_ctx = [&](CompilerContextKind kind, llvm::StringRef name) { |
454 | context.push_back(x: {kind, ConstString(name)}); |
455 | }; |
456 | switch (tag) { |
457 | case DW_TAG_namespace: |
458 | push_ctx(CompilerContextKind::Namespace, name); |
459 | break; |
460 | case DW_TAG_structure_type: |
461 | push_ctx(CompilerContextKind::Struct, name); |
462 | break; |
463 | case DW_TAG_union_type: |
464 | push_ctx(CompilerContextKind::Union, name); |
465 | break; |
466 | case DW_TAG_class_type: |
467 | push_ctx(CompilerContextKind::Class, name); |
468 | break; |
469 | case DW_TAG_enumeration_type: |
470 | push_ctx(CompilerContextKind::Enum, name); |
471 | break; |
472 | case DW_TAG_variable: |
473 | push_ctx(CompilerContextKind::Variable, GetPubname()); |
474 | break; |
475 | case DW_TAG_typedef: |
476 | push_ctx(CompilerContextKind::Typedef, name); |
477 | break; |
478 | case DW_TAG_base_type: |
479 | push_ctx(CompilerContextKind::Builtin, name); |
480 | break; |
481 | default: |
482 | break; |
483 | } |
484 | return context; |
485 | } |
486 | |
487 | DWARFDIE |
488 | DWARFDIE::GetParentDeclContextDIE() const { |
489 | if (IsValid()) |
490 | return m_die->GetParentDeclContextDIE(cu: m_cu); |
491 | else |
492 | return DWARFDIE(); |
493 | } |
494 | |
495 | bool DWARFDIE::IsStructUnionOrClass() const { |
496 | const dw_tag_t tag = Tag(); |
497 | return tag == DW_TAG_class_type || tag == DW_TAG_structure_type || |
498 | tag == DW_TAG_union_type; |
499 | } |
500 | |
501 | bool DWARFDIE::IsMethod() const { |
502 | for (DWARFDIE d : elaborating_dies(die: *this)) |
503 | if (d.GetParent().IsStructUnionOrClass()) |
504 | return true; |
505 | return false; |
506 | } |
507 | |
508 | bool DWARFDIE::GetDIENamesAndRanges( |
509 | const char *&name, const char *&mangled, DWARFRangeList &ranges, |
510 | std::optional<int> &decl_file, std::optional<int> &decl_line, |
511 | std::optional<int> &decl_column, std::optional<int> &call_file, |
512 | std::optional<int> &call_line, std::optional<int> &call_column, |
513 | lldb_private::DWARFExpressionList *frame_base) const { |
514 | if (IsValid()) { |
515 | return m_die->GetDIENamesAndRanges( |
516 | cu: GetCU(), name, mangled, rangeList&: ranges, decl_file, decl_line, decl_column, |
517 | call_file, call_line, call_column, frame_base); |
518 | } else |
519 | return false; |
520 | } |
521 | |
522 | llvm::iterator_range<DWARFDIE::child_iterator> DWARFDIE::children() const { |
523 | return llvm::make_range(x: child_iterator(*this), y: child_iterator()); |
524 | } |
525 | |