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 ®ex,
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 ®ex,
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