xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- DebugNamesDWARFIndex.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 "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h"
10 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
11 #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
12 #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
13 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Utility/RegularExpression.h"
16 #include "lldb/Utility/Stream.h"
17 #include "llvm/ADT/Sequence.h"
18 #include <optional>
19 
20 using namespace lldb_private;
21 using namespace lldb;
22 using namespace lldb_private::dwarf;
23 using namespace lldb_private::plugin::dwarf;
24 
25 llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
Create(Module & module,DWARFDataExtractor debug_names,DWARFDataExtractor debug_str,SymbolFileDWARF & dwarf)26 DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,
27                              DWARFDataExtractor debug_str,
28                              SymbolFileDWARF &dwarf) {
29   auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVMDWARF(),
30                                                debug_str.GetAsLLVM());
31   if (llvm::Error E = index_up->extract())
32     return std::move(E);
33 
34   return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(
35       module, std::move(index_up), debug_names, debug_str, dwarf));
36 }
37 
38 llvm::DenseSet<uint64_t>
GetTypeUnitSignatures(const DebugNames & debug_names)39 DebugNamesDWARFIndex::GetTypeUnitSignatures(const DebugNames &debug_names) {
40   llvm::DenseSet<uint64_t> result;
41   for (const DebugNames::NameIndex &ni : debug_names) {
42     const uint32_t num_tus = ni.getForeignTUCount();
43     for (uint32_t tu = 0; tu < num_tus; ++tu)
44       result.insert(ni.getForeignTUSignature(tu));
45   }
46   return result;
47 }
48 
49 llvm::DenseSet<dw_offset_t>
GetUnits(const DebugNames & debug_names)50 DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
51   llvm::DenseSet<dw_offset_t> result;
52   for (const DebugNames::NameIndex &ni : debug_names) {
53     const uint32_t num_cus = ni.getCUCount();
54     for (uint32_t cu = 0; cu < num_cus; ++cu)
55       result.insert(ni.getCUOffset(cu));
56     const uint32_t num_tus = ni.getLocalTUCount();
57     for (uint32_t tu = 0; tu < num_tus; ++tu)
58       result.insert(ni.getLocalTUOffset(tu));
59   }
60   return result;
61 }
62 
63 std::optional<DWARFTypeUnit *>
GetForeignTypeUnit(const DebugNames::Entry & entry) const64 DebugNamesDWARFIndex::GetForeignTypeUnit(const DebugNames::Entry &entry) const {
65   std::optional<uint64_t> type_sig = entry.getForeignTUTypeSignature();
66   if (!type_sig.has_value())
67     return std::nullopt;
68 
69   // Ask the entry for the skeleton compile unit offset and fetch the .dwo
70   // file from it and get the type unit by signature from there. If we find
71   // the type unit in the .dwo file, we don't need to check that the
72   // DW_AT_dwo_name matches because each .dwo file can have its own type unit.
73   std::optional<uint64_t> cu_offset = entry.getRelatedCUOffset();
74   if (!cu_offset)
75     return nullptr; // Return NULL, this is a type unit, but couldn't find it.
76 
77   DWARFUnit *cu =
78       m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);
79   if (!cu)
80     return nullptr; // Return NULL, this is a type unit, but couldn't find it.
81 
82   auto dwp_sp = m_debug_info.GetDwpSymbolFile();
83   if (!dwp_sp) {
84     // No .dwp file, we need to load the .dwo file.
85     DWARFUnit &dwo_cu = cu->GetNonSkeletonUnit();
86     // We don't need the check if the type unit matches the .dwo file if we have
87     // a .dwo file (not a .dwp), so we can just return the value here.
88     if (!dwo_cu.IsDWOUnit())
89       return nullptr; // We weren't able to load the .dwo file.
90     return dwo_cu.GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash(
91         *type_sig);
92   }
93   // We have a .dwp file, just get the type unit from there. We need to verify
94   // that the type unit that ended up in the final .dwp file is the right type
95   // unit. Type units have signatures which are the same across multiple .dwo
96   // files, but only one of those type units will end up in the .dwp file. The
97   // contents of type units for the same type can be different in different .dwo
98   // files, which means the DIE offsets might not be the same between two
99   // different type units. So we need to determine if this accelerator table
100   // matches the type unit that ended up in the .dwp file. If it doesn't match,
101   // then we need to ignore this accelerator table entry as the type unit that
102   // is in the .dwp file will have its own index. In order to determine if the
103   // type unit that ended up in a .dwp file matches this DebugNames::Entry, we
104   // need to find the skeleton compile unit for this entry.
105   DWARFTypeUnit *foreign_tu = dwp_sp->DebugInfo().GetTypeUnitForHash(*type_sig);
106   if (!foreign_tu)
107     return nullptr; // Return NULL, this is a type unit, but couldn't find it.
108 
109   DWARFBaseDIE cu_die = cu->GetUnitDIEOnly();
110   DWARFBaseDIE tu_die = foreign_tu->GetUnitDIEOnly();
111   llvm::StringRef cu_dwo_name =
112       cu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr);
113   llvm::StringRef tu_dwo_name =
114       tu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr);
115   if (cu_dwo_name == tu_dwo_name)
116     return foreign_tu; // We found a match!
117   return nullptr; // Return NULL, this is a type unit, but couldn't find it.
118 }
119 
120 DWARFUnit *
GetNonSkeletonUnit(const DebugNames::Entry & entry) const121 DebugNamesDWARFIndex::GetNonSkeletonUnit(const DebugNames::Entry &entry) const {
122 
123   if (std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry))
124     return foreign_tu.value();
125 
126   // Look for a DWARF unit offset (CU offset or local TU offset) as they are
127   // both offsets into the .debug_info section.
128   std::optional<uint64_t> unit_offset = entry.getCUOffset();
129   if (!unit_offset)
130     unit_offset = entry.getLocalTUOffset();
131   if (unit_offset) {
132     if (DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo,
133                                                      *unit_offset))
134       return &cu->GetNonSkeletonUnit();
135   }
136   return nullptr;
137 }
138 
GetDIE(const DebugNames::Entry & entry) const139 DWARFDIE DebugNamesDWARFIndex::GetDIE(const DebugNames::Entry &entry) const {
140   DWARFUnit *unit = GetNonSkeletonUnit(entry);
141   std::optional<uint64_t> die_offset = entry.getDIEUnitOffset();
142   if (!unit || !die_offset)
143     return DWARFDIE();
144   if (DWARFDIE die = unit->GetDIE(unit->GetOffset() + *die_offset))
145     return die;
146 
147   m_module.ReportErrorIfModifyDetected(
148       "the DWARF debug information has been modified (bad offset {0:x} in "
149       "debug_names section)\n",
150       *die_offset);
151   return DWARFDIE();
152 }
153 
ProcessEntry(const DebugNames::Entry & entry,llvm::function_ref<bool (DWARFDIE die)> callback)154 bool DebugNamesDWARFIndex::ProcessEntry(
155     const DebugNames::Entry &entry,
156     llvm::function_ref<bool(DWARFDIE die)> callback) {
157   DWARFDIE die = GetDIE(entry);
158   if (!die)
159     return true;
160   // Clang used to erroneously emit index entries for declaration DIEs in case
161   // when the definition is in a type unit (llvm.org/pr77696).
162   if (die.IsStructUnionOrClass() &&
163       die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))
164     return true;
165   return callback(die);
166 }
167 
MaybeLogLookupError(llvm::Error error,const DebugNames::NameIndex & ni,llvm::StringRef name)168 void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,
169                                                const DebugNames::NameIndex &ni,
170                                                llvm::StringRef name) {
171   // Ignore SentinelErrors, log everything else.
172   LLDB_LOG_ERROR(
173       GetLog(DWARFLog::Lookups),
174       handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),
175       "Failed to parse index entries for index at {1:x}, name {2}: {0}",
176       ni.getUnitOffset(), name);
177 }
178 
GetGlobalVariables(ConstString basename,llvm::function_ref<bool (DWARFDIE die)> callback)179 void DebugNamesDWARFIndex::GetGlobalVariables(
180     ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {
181   for (const DebugNames::Entry &entry :
182        m_debug_names_up->equal_range(basename.GetStringRef())) {
183     if (entry.tag() != DW_TAG_variable)
184       continue;
185 
186     if (!ProcessEntry(entry, callback))
187       return;
188   }
189 
190   m_fallback.GetGlobalVariables(basename, callback);
191 }
192 
GetGlobalVariables(const RegularExpression & regex,llvm::function_ref<bool (DWARFDIE die)> callback)193 void DebugNamesDWARFIndex::GetGlobalVariables(
194     const RegularExpression &regex,
195     llvm::function_ref<bool(DWARFDIE die)> callback) {
196   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
197     for (DebugNames::NameTableEntry nte: ni) {
198       Mangled mangled_name(nte.getString());
199       if (!mangled_name.NameMatches(regex))
200         continue;
201 
202       uint64_t entry_offset = nte.getEntryOffset();
203       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
204       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
205         if (entry_or->tag() != DW_TAG_variable)
206           continue;
207 
208         if (!ProcessEntry(*entry_or, callback))
209           return;
210       }
211       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
212     }
213   }
214 
215   m_fallback.GetGlobalVariables(regex, callback);
216 }
217 
GetGlobalVariables(DWARFUnit & cu,llvm::function_ref<bool (DWARFDIE die)> callback)218 void DebugNamesDWARFIndex::GetGlobalVariables(
219     DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
220   uint64_t cu_offset = cu.GetOffset();
221   bool found_entry_for_cu = false;
222   for (const DebugNames::NameIndex &ni : *m_debug_names_up) {
223     // Check if this name index contains an entry for the given CU.
224     bool cu_matches = false;
225     for (uint32_t i = 0; i < ni.getCUCount(); ++i) {
226       if (ni.getCUOffset(i) == cu_offset) {
227         cu_matches = true;
228         break;
229       }
230     }
231     if (!cu_matches)
232       continue;
233 
234     for (DebugNames::NameTableEntry nte : ni) {
235       uint64_t entry_offset = nte.getEntryOffset();
236       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
237       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
238         if (entry_or->tag() != DW_TAG_variable)
239           continue;
240         if (entry_or->getCUOffset() != cu_offset)
241           continue;
242 
243         found_entry_for_cu = true;
244         if (!ProcessEntry(*entry_or, callback))
245           return;
246       }
247       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
248     }
249   }
250   // If no name index for that particular CU was found, fallback to
251   // creating the manual index.
252   if (!found_entry_for_cu)
253     m_fallback.GetGlobalVariables(cu, callback);
254 }
255 
GetCompleteObjCClass(ConstString class_name,bool must_be_implementation,llvm::function_ref<bool (DWARFDIE die)> callback)256 void DebugNamesDWARFIndex::GetCompleteObjCClass(
257     ConstString class_name, bool must_be_implementation,
258     llvm::function_ref<bool(DWARFDIE die)> callback) {
259   // Keep a list of incomplete types as fallback for when we don't find the
260   // complete type.
261   std::vector<DWARFDIE> incomplete_types;
262 
263   for (const DebugNames::Entry &entry :
264        m_debug_names_up->equal_range(class_name.GetStringRef())) {
265     if (entry.tag() != DW_TAG_structure_type &&
266         entry.tag() != DW_TAG_class_type)
267       continue;
268 
269     DWARFDIE die = GetDIE(entry);
270     if (!die) {
271       // Report invalid
272       continue;
273     }
274     DWARFUnit *cu = die.GetCU();
275     if (!cu->Supports_DW_AT_APPLE_objc_complete_type()) {
276       incomplete_types.push_back(die);
277       continue;
278     }
279 
280     if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
281       // If we find the complete version we're done.
282       callback(die);
283       return;
284     }
285     incomplete_types.push_back(die);
286   }
287 
288   for (DWARFDIE die : incomplete_types)
289     if (!callback(die))
290       return;
291 
292   m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback);
293 }
294 
295 namespace {
296 using Entry = llvm::DWARFDebugNames::Entry;
297 
298 /// If `entry` and all of its parents have an `IDX_parent`, use that information
299 /// to build and return a list of at most `max_parents` parent Entries.
300 /// `entry` itself is not included in the list.
301 /// If any parent does not have an `IDX_parent`, or the Entry data is corrupted,
302 /// nullopt is returned.
303 std::optional<llvm::SmallVector<Entry, 4>>
getParentChain(Entry entry,uint32_t max_parents)304 getParentChain(Entry entry, uint32_t max_parents) {
305   llvm::SmallVector<Entry, 4> parent_entries;
306 
307   do {
308     if (!entry.hasParentInformation())
309       return std::nullopt;
310 
311     llvm::Expected<std::optional<Entry>> parent = entry.getParentDIEEntry();
312     if (!parent) {
313       // Bad data.
314       LLDB_LOG_ERROR(
315           GetLog(DWARFLog::Lookups), parent.takeError(),
316           "Failed to extract parent entry from a non-empty IDX_parent");
317       return std::nullopt;
318     }
319 
320     // Last parent in the chain.
321     if (!parent->has_value())
322       break;
323 
324     parent_entries.push_back(**parent);
325     entry = **parent;
326   } while (parent_entries.size() < max_parents);
327 
328   return parent_entries;
329 }
330 } // namespace
331 
GetFullyQualifiedType(const DWARFDeclContext & context,llvm::function_ref<bool (DWARFDIE die)> callback)332 void DebugNamesDWARFIndex::GetFullyQualifiedType(
333     const DWARFDeclContext &context,
334     llvm::function_ref<bool(DWARFDIE die)> callback) {
335   if (context.GetSize() == 0)
336     return;
337 
338   llvm::StringRef leaf_name = context[0].name;
339   llvm::SmallVector<llvm::StringRef> parent_names;
340   for (auto idx : llvm::seq<int>(1, context.GetSize()))
341     parent_names.emplace_back(context[idx].name);
342 
343   // For each entry, grab its parent chain and check if we have a match.
344   for (const DebugNames::Entry &entry :
345        m_debug_names_up->equal_range(leaf_name)) {
346     if (!isType(entry.tag()))
347       continue;
348 
349     // If we get a NULL foreign_tu back, the entry doesn't match the type unit
350     // in the .dwp file, or we were not able to load the .dwo file or the DWO ID
351     // didn't match.
352     std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry);
353     if (foreign_tu && foreign_tu.value() == nullptr)
354       continue;
355 
356     // Grab at most one extra parent, subsequent parents are not necessary to
357     // test equality.
358     std::optional<llvm::SmallVector<Entry, 4>> parent_chain =
359         getParentChain(entry, parent_names.size() + 1);
360 
361     if (!parent_chain) {
362       // Fallback: use the base class implementation.
363       if (!ProcessEntry(entry, [&](DWARFDIE die) {
364             return GetFullyQualifiedTypeImpl(context, die, callback);
365           }))
366         return;
367       continue;
368     }
369 
370     if (SameParentChain(parent_names, *parent_chain) &&
371         !ProcessEntry(entry, callback))
372       return;
373   }
374 }
375 
SameParentChain(llvm::ArrayRef<llvm::StringRef> parent_names,llvm::ArrayRef<DebugNames::Entry> parent_entries) const376 bool DebugNamesDWARFIndex::SameParentChain(
377     llvm::ArrayRef<llvm::StringRef> parent_names,
378     llvm::ArrayRef<DebugNames::Entry> parent_entries) const {
379 
380   if (parent_entries.size() != parent_names.size())
381     return false;
382 
383   auto SameAsEntryATName = [this](llvm::StringRef name,
384                                   const DebugNames::Entry &entry) {
385     // Peek at the AT_name of `entry` and test equality to `name`.
386     auto maybe_dieoffset = entry.getDIEUnitOffset();
387     if (!maybe_dieoffset)
388       return false;
389     DWARFUnit *unit = GetNonSkeletonUnit(entry);
390     if (!unit)
391       return false;
392     return name == unit->PeekDIEName(unit->GetOffset() + *maybe_dieoffset);
393   };
394 
395   // If the AT_name of any parent fails to match the expected name, we don't
396   // have a match.
397   for (auto [parent_name, parent_entry] :
398        llvm::zip_equal(parent_names, parent_entries))
399     if (!SameAsEntryATName(parent_name, parent_entry))
400       return false;
401   return true;
402 }
403 
GetTypes(ConstString name,llvm::function_ref<bool (DWARFDIE die)> callback)404 void DebugNamesDWARFIndex::GetTypes(
405     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
406   for (const DebugNames::Entry &entry :
407        m_debug_names_up->equal_range(name.GetStringRef())) {
408     if (isType(entry.tag())) {
409       if (!ProcessEntry(entry, callback))
410         return;
411     }
412   }
413 
414   m_fallback.GetTypes(name, callback);
415 }
416 
GetTypes(const DWARFDeclContext & context,llvm::function_ref<bool (DWARFDIE die)> callback)417 void DebugNamesDWARFIndex::GetTypes(
418     const DWARFDeclContext &context,
419     llvm::function_ref<bool(DWARFDIE die)> callback) {
420   auto name = context[0].name;
421   for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) {
422     if (entry.tag() == context[0].tag) {
423       if (!ProcessEntry(entry, callback))
424         return;
425     }
426   }
427 
428   m_fallback.GetTypes(context, callback);
429 }
430 
GetNamespaces(ConstString name,llvm::function_ref<bool (DWARFDIE die)> callback)431 void DebugNamesDWARFIndex::GetNamespaces(
432     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
433   for (const DebugNames::Entry &entry :
434        m_debug_names_up->equal_range(name.GetStringRef())) {
435     lldb_private::dwarf::Tag entry_tag = entry.tag();
436     if (entry_tag == DW_TAG_namespace ||
437         entry_tag == DW_TAG_imported_declaration) {
438       if (!ProcessEntry(entry, callback))
439         return;
440     }
441   }
442 
443   m_fallback.GetNamespaces(name, callback);
444 }
445 
GetFunctions(const Module::LookupInfo & lookup_info,SymbolFileDWARF & dwarf,const CompilerDeclContext & parent_decl_ctx,llvm::function_ref<bool (DWARFDIE die)> callback)446 void DebugNamesDWARFIndex::GetFunctions(
447     const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf,
448     const CompilerDeclContext &parent_decl_ctx,
449     llvm::function_ref<bool(DWARFDIE die)> callback) {
450   ConstString name = lookup_info.GetLookupName();
451   std::set<DWARFDebugInfoEntry *> seen;
452   for (const DebugNames::Entry &entry :
453        m_debug_names_up->equal_range(name.GetStringRef())) {
454     Tag tag = entry.tag();
455     if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
456       continue;
457 
458     if (DWARFDIE die = GetDIE(entry)) {
459       if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx,
460                               [&](DWARFDIE die) {
461                                 if (!seen.insert(die.GetDIE()).second)
462                                   return true;
463                                 return callback(die);
464                               }))
465         return;
466     }
467   }
468 
469   m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback);
470 }
471 
GetFunctions(const RegularExpression & regex,llvm::function_ref<bool (DWARFDIE die)> callback)472 void DebugNamesDWARFIndex::GetFunctions(
473     const RegularExpression &regex,
474     llvm::function_ref<bool(DWARFDIE die)> callback) {
475   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
476     for (DebugNames::NameTableEntry nte: ni) {
477       if (!regex.Execute(nte.getString()))
478         continue;
479 
480       uint64_t entry_offset = nte.getEntryOffset();
481       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
482       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
483         Tag tag = entry_or->tag();
484         if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
485           continue;
486 
487         if (!ProcessEntry(*entry_or, callback))
488           return;
489       }
490       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
491     }
492   }
493 
494   m_fallback.GetFunctions(regex, callback);
495 }
496 
Dump(Stream & s)497 void DebugNamesDWARFIndex::Dump(Stream &s) {
498   m_fallback.Dump(s);
499 
500   std::string data;
501   llvm::raw_string_ostream os(data);
502   m_debug_names_up->dump(os);
503   s.PutCString(os.str());
504 }
505