xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- AppleDWARFIndex.cpp -----------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "Plugins/SymbolFile/DWARF/AppleDWARFIndex.h"
100b57cec5SDimitry Andric #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
110b57cec5SDimitry Andric #include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
120b57cec5SDimitry Andric #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "lldb/Core/Module.h"
150b57cec5SDimitry Andric #include "lldb/Symbol/Function.h"
1606c3fb27SDimitry Andric #include "llvm/Support/DJB.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric using namespace lldb_private;
190b57cec5SDimitry Andric using namespace lldb;
2081ad6265SDimitry Andric using namespace lldb_private::dwarf;
215f757f3fSDimitry Andric using namespace lldb_private::plugin::dwarf;
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
240b57cec5SDimitry Andric     Module &module, DWARFDataExtractor apple_names,
250b57cec5SDimitry Andric     DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
260b57cec5SDimitry Andric     DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) {
2706c3fb27SDimitry Andric 
2806c3fb27SDimitry Andric   llvm::DataExtractor llvm_debug_str = debug_str.GetAsLLVM();
2906c3fb27SDimitry Andric 
3006c3fb27SDimitry Andric   auto apple_names_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
3106c3fb27SDimitry Andric       apple_names.GetAsLLVMDWARF(), llvm_debug_str);
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric   auto apple_namespaces_table_up =
3406c3fb27SDimitry Andric       std::make_unique<llvm::AppleAcceleratorTable>(
3506c3fb27SDimitry Andric           apple_namespaces.GetAsLLVMDWARF(), llvm_debug_str);
360b57cec5SDimitry Andric 
3706c3fb27SDimitry Andric   auto apple_types_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
3806c3fb27SDimitry Andric       apple_types.GetAsLLVMDWARF(), llvm_debug_str);
390b57cec5SDimitry Andric 
4006c3fb27SDimitry Andric   auto apple_objc_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
4106c3fb27SDimitry Andric       apple_objc.GetAsLLVMDWARF(), llvm_debug_str);
4206c3fb27SDimitry Andric 
4306c3fb27SDimitry Andric   auto extract_and_check = [](auto &TablePtr) {
4406c3fb27SDimitry Andric     if (auto E = TablePtr->extract()) {
4506c3fb27SDimitry Andric       llvm::consumeError(std::move(E));
4606c3fb27SDimitry Andric       TablePtr.reset();
4706c3fb27SDimitry Andric     }
4806c3fb27SDimitry Andric   };
4906c3fb27SDimitry Andric 
5006c3fb27SDimitry Andric   extract_and_check(apple_names_table_up);
5106c3fb27SDimitry Andric   extract_and_check(apple_namespaces_table_up);
5206c3fb27SDimitry Andric   extract_and_check(apple_types_table_up);
5306c3fb27SDimitry Andric   extract_and_check(apple_objc_table_up);
545f757f3fSDimitry Andric   assert(apple_names.GetByteSize() == 0 || apple_names.GetSharedDataBuffer());
555f757f3fSDimitry Andric   assert(apple_namespaces.GetByteSize() == 0 ||
565f757f3fSDimitry Andric          apple_namespaces.GetSharedDataBuffer());
575f757f3fSDimitry Andric   assert(apple_types.GetByteSize() == 0 || apple_types.GetSharedDataBuffer());
585f757f3fSDimitry Andric   assert(apple_objc.GetByteSize() == 0 || apple_objc.GetSharedDataBuffer());
590b57cec5SDimitry Andric 
60e8d8bef9SDimitry Andric   if (apple_names_table_up || apple_namespaces_table_up ||
61e8d8bef9SDimitry Andric       apple_types_table_up || apple_objc_table_up)
629dba64beSDimitry Andric     return std::make_unique<AppleDWARFIndex>(
630b57cec5SDimitry Andric         module, std::move(apple_names_table_up),
640b57cec5SDimitry Andric         std::move(apple_namespaces_table_up), std::move(apple_types_table_up),
655f757f3fSDimitry Andric         std::move(apple_objc_table_up), apple_names.GetSharedDataBuffer(),
665f757f3fSDimitry Andric         apple_namespaces.GetSharedDataBuffer(),
675f757f3fSDimitry Andric         apple_types.GetSharedDataBuffer(), apple_objc.GetSharedDataBuffer());
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   return nullptr;
700b57cec5SDimitry Andric }
710b57cec5SDimitry Andric 
7206c3fb27SDimitry Andric /// Returns true if `tag` is a class_type of structure_type tag.
7306c3fb27SDimitry Andric static bool IsClassOrStruct(dw_tag_t tag) {
7406c3fb27SDimitry Andric   return tag == DW_TAG_class_type || tag == DW_TAG_structure_type;
7506c3fb27SDimitry Andric }
7606c3fb27SDimitry Andric 
7706c3fb27SDimitry Andric /// Returns true if `entry` has an extractable DW_ATOM_qual_name_hash and it
7806c3fb27SDimitry Andric /// matches `expected_hash`.
7906c3fb27SDimitry Andric static bool
8006c3fb27SDimitry Andric EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry,
8106c3fb27SDimitry Andric                          uint32_t expected_hash) {
8206c3fb27SDimitry Andric   std::optional<llvm::DWARFFormValue> form_value =
8306c3fb27SDimitry Andric       entry.lookup(dwarf::DW_ATOM_qual_name_hash);
8406c3fb27SDimitry Andric   if (!form_value)
8506c3fb27SDimitry Andric     return false;
8606c3fb27SDimitry Andric   std::optional<uint64_t> hash = form_value->getAsUnsignedConstant();
8706c3fb27SDimitry Andric   return hash && (*hash == expected_hash);
8806c3fb27SDimitry Andric }
8906c3fb27SDimitry Andric 
9006c3fb27SDimitry Andric /// Returns true if `entry` has an extractable DW_ATOM_die_tag and it matches
9106c3fb27SDimitry Andric /// `expected_tag`. We also consider it a match if the tags are different but
9206c3fb27SDimitry Andric /// in the set of {TAG_class_type, TAG_struct_type}.
9306c3fb27SDimitry Andric static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry,
9406c3fb27SDimitry Andric                                 dw_tag_t expected_tag) {
9506c3fb27SDimitry Andric   std::optional<llvm::DWARFFormValue> form_value =
9606c3fb27SDimitry Andric       entry.lookup(dwarf::DW_ATOM_die_tag);
9706c3fb27SDimitry Andric   if (!form_value)
9806c3fb27SDimitry Andric     return false;
9906c3fb27SDimitry Andric   std::optional<uint64_t> maybe_tag = form_value->getAsUnsignedConstant();
10006c3fb27SDimitry Andric   if (!maybe_tag)
10106c3fb27SDimitry Andric     return false;
10206c3fb27SDimitry Andric   auto tag = static_cast<dw_tag_t>(*maybe_tag);
10306c3fb27SDimitry Andric   return tag == expected_tag ||
10406c3fb27SDimitry Andric          (IsClassOrStruct(tag) && IsClassOrStruct(expected_tag));
10506c3fb27SDimitry Andric }
10606c3fb27SDimitry Andric 
10706c3fb27SDimitry Andric /// Returns true if `entry` has an extractable DW_ATOM_type_flags and the flag
10806c3fb27SDimitry Andric /// "DW_FLAG_type_implementation" is set.
10906c3fb27SDimitry Andric static bool
11006c3fb27SDimitry Andric HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry) {
11106c3fb27SDimitry Andric   std::optional<llvm::DWARFFormValue> form_value =
11206c3fb27SDimitry Andric       entry.lookup(dwarf::DW_ATOM_type_flags);
11306c3fb27SDimitry Andric   if (!form_value)
11406c3fb27SDimitry Andric     return false;
11506c3fb27SDimitry Andric   std::optional<uint64_t> Flags = form_value->getAsUnsignedConstant();
11606c3fb27SDimitry Andric   return Flags &&
11706c3fb27SDimitry Andric          (*Flags & llvm::dwarf::AcceleratorTable::DW_FLAG_type_implementation);
11806c3fb27SDimitry Andric }
11906c3fb27SDimitry Andric 
12006c3fb27SDimitry Andric void AppleDWARFIndex::SearchFor(const llvm::AppleAcceleratorTable &table,
12106c3fb27SDimitry Andric                                 llvm::StringRef name,
12206c3fb27SDimitry Andric                                 llvm::function_ref<bool(DWARFDIE die)> callback,
12306c3fb27SDimitry Andric                                 std::optional<dw_tag_t> search_for_tag,
12406c3fb27SDimitry Andric                                 std::optional<uint32_t> search_for_qualhash) {
12506c3fb27SDimitry Andric   auto converted_cb = DIERefCallback(callback, name);
12606c3fb27SDimitry Andric   for (const auto &entry : table.equal_range(name)) {
12706c3fb27SDimitry Andric     if (search_for_qualhash &&
12806c3fb27SDimitry Andric         !EntryHasMatchingQualhash(entry, *search_for_qualhash))
12906c3fb27SDimitry Andric       continue;
13006c3fb27SDimitry Andric     if (search_for_tag && !EntryHasMatchingTag(entry, *search_for_tag))
13106c3fb27SDimitry Andric       continue;
13206c3fb27SDimitry Andric     if (!converted_cb(entry))
13306c3fb27SDimitry Andric       break;
13406c3fb27SDimitry Andric   }
13506c3fb27SDimitry Andric }
13606c3fb27SDimitry Andric 
1375ffd83dbSDimitry Andric void AppleDWARFIndex::GetGlobalVariables(
1385ffd83dbSDimitry Andric     ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {
1395ffd83dbSDimitry Andric   if (!m_apple_names_up)
1405ffd83dbSDimitry Andric     return;
14106c3fb27SDimitry Andric   SearchFor(*m_apple_names_up, basename, callback);
1420b57cec5SDimitry Andric }
1430b57cec5SDimitry Andric 
1445ffd83dbSDimitry Andric void AppleDWARFIndex::GetGlobalVariables(
1455ffd83dbSDimitry Andric     const RegularExpression &regex,
1465ffd83dbSDimitry Andric     llvm::function_ref<bool(DWARFDIE die)> callback) {
1470b57cec5SDimitry Andric   if (!m_apple_names_up)
1480b57cec5SDimitry Andric     return;
1490b57cec5SDimitry Andric 
15006c3fb27SDimitry Andric   DIERefCallbackImpl converted_cb = DIERefCallback(callback, regex.GetText());
15106c3fb27SDimitry Andric 
15206c3fb27SDimitry Andric   for (const auto &entry : m_apple_names_up->entries())
15306c3fb27SDimitry Andric     if (std::optional<llvm::StringRef> name = entry.readName();
15406c3fb27SDimitry Andric         name && Mangled(*name).NameMatches(regex))
15506c3fb27SDimitry Andric       if (!converted_cb(entry.BaseEntry))
15606c3fb27SDimitry Andric         return;
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric 
1595ffd83dbSDimitry Andric void AppleDWARFIndex::GetGlobalVariables(
160349cc55cSDimitry Andric     DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
1610b57cec5SDimitry Andric   if (!m_apple_names_up)
1620b57cec5SDimitry Andric     return;
1630b57cec5SDimitry Andric 
164349cc55cSDimitry Andric   const DWARFUnit &non_skeleton_cu = cu.GetNonSkeletonUnit();
16506c3fb27SDimitry Andric   dw_offset_t lower_bound = non_skeleton_cu.GetOffset();
16606c3fb27SDimitry Andric   dw_offset_t upper_bound = non_skeleton_cu.GetNextUnitOffset();
16706c3fb27SDimitry Andric   auto is_in_range = [lower_bound, upper_bound](std::optional<uint32_t> val) {
16806c3fb27SDimitry Andric     return val.has_value() && *val >= lower_bound && *val < upper_bound;
16906c3fb27SDimitry Andric   };
17006c3fb27SDimitry Andric 
17106c3fb27SDimitry Andric   DIERefCallbackImpl converted_cb = DIERefCallback(callback);
17206c3fb27SDimitry Andric   for (auto entry : m_apple_names_up->entries()) {
17306c3fb27SDimitry Andric     if (is_in_range(entry.BaseEntry.getDIESectionOffset()))
17406c3fb27SDimitry Andric       if (!converted_cb(entry.BaseEntry))
17506c3fb27SDimitry Andric         return;
17606c3fb27SDimitry Andric   }
1770b57cec5SDimitry Andric }
1780b57cec5SDimitry Andric 
1795ffd83dbSDimitry Andric void AppleDWARFIndex::GetObjCMethods(
1805ffd83dbSDimitry Andric     ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) {
1815ffd83dbSDimitry Andric   if (!m_apple_objc_up)
1825ffd83dbSDimitry Andric     return;
18306c3fb27SDimitry Andric   SearchFor(*m_apple_objc_up, class_name, callback);
1840b57cec5SDimitry Andric }
1850b57cec5SDimitry Andric 
1865ffd83dbSDimitry Andric void AppleDWARFIndex::GetCompleteObjCClass(
1875ffd83dbSDimitry Andric     ConstString class_name, bool must_be_implementation,
1885ffd83dbSDimitry Andric     llvm::function_ref<bool(DWARFDIE die)> callback) {
1895ffd83dbSDimitry Andric   if (!m_apple_types_up)
1905ffd83dbSDimitry Andric     return;
19106c3fb27SDimitry Andric 
19206c3fb27SDimitry Andric   llvm::SmallVector<DIERef> decl_dies;
19306c3fb27SDimitry Andric   auto converted_cb = DIERefCallback(callback, class_name);
19406c3fb27SDimitry Andric 
19506c3fb27SDimitry Andric   for (const auto &entry : m_apple_types_up->equal_range(class_name)) {
19606c3fb27SDimitry Andric     if (HasImplementationFlag(entry)) {
19706c3fb27SDimitry Andric       converted_cb(entry);
19806c3fb27SDimitry Andric       return;
19906c3fb27SDimitry Andric     }
20006c3fb27SDimitry Andric 
20106c3fb27SDimitry Andric     decl_dies.emplace_back(std::nullopt, DIERef::Section::DebugInfo,
20206c3fb27SDimitry Andric                            *entry.getDIESectionOffset());
20306c3fb27SDimitry Andric   }
20406c3fb27SDimitry Andric 
20506c3fb27SDimitry Andric   if (must_be_implementation)
20606c3fb27SDimitry Andric     return;
20706c3fb27SDimitry Andric   for (DIERef ref : decl_dies)
20806c3fb27SDimitry Andric     if (!converted_cb(ref))
20906c3fb27SDimitry Andric       return;
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric 
2125ffd83dbSDimitry Andric void AppleDWARFIndex::GetTypes(
2135ffd83dbSDimitry Andric     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
2145ffd83dbSDimitry Andric   if (!m_apple_types_up)
2155ffd83dbSDimitry Andric     return;
21606c3fb27SDimitry Andric   SearchFor(*m_apple_types_up, name, callback);
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric 
2195ffd83dbSDimitry Andric void AppleDWARFIndex::GetTypes(
2205ffd83dbSDimitry Andric     const DWARFDeclContext &context,
2215ffd83dbSDimitry Andric     llvm::function_ref<bool(DWARFDIE die)> callback) {
2220b57cec5SDimitry Andric   if (!m_apple_types_up)
2230b57cec5SDimitry Andric     return;
2240b57cec5SDimitry Andric 
2251fd87a68SDimitry Andric   Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
22606c3fb27SDimitry Andric   const bool entries_have_tag =
22706c3fb27SDimitry Andric       m_apple_types_up->containsAtomType(DW_ATOM_die_tag);
22806c3fb27SDimitry Andric   const bool entries_have_qual_hash =
22906c3fb27SDimitry Andric       m_apple_types_up->containsAtomType(DW_ATOM_qual_name_hash);
2309dba64beSDimitry Andric 
23106c3fb27SDimitry Andric   llvm::StringRef expected_name = context[0].name;
23206c3fb27SDimitry Andric 
23306c3fb27SDimitry Andric   if (entries_have_tag && entries_have_qual_hash) {
23406c3fb27SDimitry Andric     const dw_tag_t expected_tag = context[0].tag;
23506c3fb27SDimitry Andric     const uint32_t expected_qualname_hash =
23606c3fb27SDimitry Andric         llvm::djbHash(context.GetQualifiedName());
2370b57cec5SDimitry Andric     if (log)
2380b57cec5SDimitry Andric       m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
23906c3fb27SDimitry Andric     SearchFor(*m_apple_types_up, expected_name, callback, expected_tag,
24006c3fb27SDimitry Andric                expected_qualname_hash);
2419dba64beSDimitry Andric     return;
2429dba64beSDimitry Andric   }
2439dba64beSDimitry Andric 
24406c3fb27SDimitry Andric   // Historically, if there are no tags, we also ignore qual_hash (why?)
24506c3fb27SDimitry Andric   if (!entries_have_tag) {
24606c3fb27SDimitry Andric     SearchFor(*m_apple_names_up, expected_name, callback);
24706c3fb27SDimitry Andric     return;
24806c3fb27SDimitry Andric   }
24906c3fb27SDimitry Andric 
25006c3fb27SDimitry Andric   // We have a tag but no qual hash.
25106c3fb27SDimitry Andric 
2529dba64beSDimitry Andric   // When searching for a scoped type (for example,
2539dba64beSDimitry Andric   // "std::vector<int>::const_iterator") searching for the innermost
2549dba64beSDimitry Andric   // name alone ("const_iterator") could yield many false
2559dba64beSDimitry Andric   // positives. By searching for the parent type ("vector<int>")
2569dba64beSDimitry Andric   // first we can avoid extracting type DIEs from object files that
2579dba64beSDimitry Andric   // would fail the filter anyway.
25806c3fb27SDimitry Andric   if ((context.GetSize() > 1) && IsClassOrStruct(context[1].tag))
25906c3fb27SDimitry Andric     if (m_apple_types_up->equal_range(context[1].name).empty())
2609dba64beSDimitry Andric       return;
2619dba64beSDimitry Andric 
2620b57cec5SDimitry Andric   if (log)
2630b57cec5SDimitry Andric     m_module.LogMessage(log, "FindByNameAndTag()");
26406c3fb27SDimitry Andric   const dw_tag_t expected_tag = context[0].tag;
26506c3fb27SDimitry Andric   SearchFor(*m_apple_types_up, expected_name, callback, expected_tag);
2669dba64beSDimitry Andric   return;
2679dba64beSDimitry Andric }
2689dba64beSDimitry Andric 
2695ffd83dbSDimitry Andric void AppleDWARFIndex::GetNamespaces(
2705ffd83dbSDimitry Andric     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
2715ffd83dbSDimitry Andric   if (!m_apple_namespaces_up)
2725ffd83dbSDimitry Andric     return;
27306c3fb27SDimitry Andric   SearchFor(*m_apple_namespaces_up, name, callback);
2740b57cec5SDimitry Andric }
2750b57cec5SDimitry Andric 
2765ffd83dbSDimitry Andric void AppleDWARFIndex::GetFunctions(
277bdd1243dSDimitry Andric     const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf,
278bdd1243dSDimitry Andric     const CompilerDeclContext &parent_decl_ctx,
2795ffd83dbSDimitry Andric     llvm::function_ref<bool(DWARFDIE die)> callback) {
28006c3fb27SDimitry Andric   if (!m_apple_names_up)
28106c3fb27SDimitry Andric     return;
28206c3fb27SDimitry Andric 
283bdd1243dSDimitry Andric   ConstString name = lookup_info.GetLookupName();
28406c3fb27SDimitry Andric   for (const auto &entry : m_apple_names_up->equal_range(name)) {
28506c3fb27SDimitry Andric     DIERef die_ref(std::nullopt, DIERef::Section::DebugInfo,
28606c3fb27SDimitry Andric                    *entry.getDIESectionOffset());
287*0fca6ea1SDimitry Andric     DWARFDIE die = dwarf.GetDIE(die_ref);
288*0fca6ea1SDimitry Andric     if (!die) {
289*0fca6ea1SDimitry Andric       ReportInvalidDIERef(die_ref, name);
290*0fca6ea1SDimitry Andric       continue;
291*0fca6ea1SDimitry Andric     }
292*0fca6ea1SDimitry Andric     if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, callback))
29306c3fb27SDimitry Andric       return;
29406c3fb27SDimitry Andric   }
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric 
2975ffd83dbSDimitry Andric void AppleDWARFIndex::GetFunctions(
2985ffd83dbSDimitry Andric     const RegularExpression &regex,
2995ffd83dbSDimitry Andric     llvm::function_ref<bool(DWARFDIE die)> callback) {
30006c3fb27SDimitry Andric   return GetGlobalVariables(regex, callback);
3010b57cec5SDimitry Andric }
3020b57cec5SDimitry Andric 
3030b57cec5SDimitry Andric void AppleDWARFIndex::Dump(Stream &s) {
3040b57cec5SDimitry Andric   if (m_apple_names_up)
3050b57cec5SDimitry Andric     s.PutCString(".apple_names index present\n");
3060b57cec5SDimitry Andric   if (m_apple_namespaces_up)
3070b57cec5SDimitry Andric     s.PutCString(".apple_namespaces index present\n");
3080b57cec5SDimitry Andric   if (m_apple_types_up)
3090b57cec5SDimitry Andric     s.PutCString(".apple_types index present\n");
3100b57cec5SDimitry Andric   if (m_apple_objc_up)
3110b57cec5SDimitry Andric     s.PutCString(".apple_objc index present\n");
3120b57cec5SDimitry Andric   // TODO: Dump index contents
3130b57cec5SDimitry Andric }
314