1//===-- AppleDWARFIndex.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/AppleDWARFIndex.h"
10#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
11#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
12#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
13
14#include "lldb/Core/Module.h"
15#include "lldb/Symbol/Function.h"
16#include "llvm/Support/DJB.h"
17
18using namespace lldb_private;
19using namespace lldb;
20using namespace lldb_private::dwarf;
21using namespace lldb_private::plugin::dwarf;
22
23std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
24 Module &module, DWARFDataExtractor apple_names,
25 DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
26 DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) {
27
28 llvm::DataExtractor llvm_debug_str = debug_str.GetAsLLVM();
29
30 auto apple_names_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
31 args: apple_names.GetAsLLVMDWARF(), args&: llvm_debug_str);
32
33 auto apple_namespaces_table_up =
34 std::make_unique<llvm::AppleAcceleratorTable>(
35 args: apple_namespaces.GetAsLLVMDWARF(), args&: llvm_debug_str);
36
37 auto apple_types_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
38 args: apple_types.GetAsLLVMDWARF(), args&: llvm_debug_str);
39
40 auto apple_objc_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
41 args: apple_objc.GetAsLLVMDWARF(), args&: llvm_debug_str);
42
43 auto extract_and_check = [](auto &TablePtr) {
44 if (auto E = TablePtr->extract()) {
45 llvm::consumeError(Err: std::move(E));
46 TablePtr.reset();
47 }
48 };
49
50 extract_and_check(apple_names_table_up);
51 extract_and_check(apple_namespaces_table_up);
52 extract_and_check(apple_types_table_up);
53 extract_and_check(apple_objc_table_up);
54 assert(apple_names.GetByteSize() == 0 || apple_names.GetSharedDataBuffer());
55 assert(apple_namespaces.GetByteSize() == 0 ||
56 apple_namespaces.GetSharedDataBuffer());
57 assert(apple_types.GetByteSize() == 0 || apple_types.GetSharedDataBuffer());
58 assert(apple_objc.GetByteSize() == 0 || apple_objc.GetSharedDataBuffer());
59
60 if (apple_names_table_up || apple_namespaces_table_up ||
61 apple_types_table_up || apple_objc_table_up)
62 return std::make_unique<AppleDWARFIndex>(
63 args&: module, args: std::move(apple_names_table_up),
64 args: std::move(apple_namespaces_table_up), args: std::move(apple_types_table_up),
65 args: std::move(apple_objc_table_up), args&: apple_names.GetSharedDataBuffer(),
66 args&: apple_namespaces.GetSharedDataBuffer(),
67 args&: apple_types.GetSharedDataBuffer(), args&: apple_objc.GetSharedDataBuffer());
68
69 return nullptr;
70}
71
72/// Returns true if `tag` is a class_type of structure_type tag.
73static bool IsClassOrStruct(dw_tag_t tag) {
74 return tag == DW_TAG_class_type || tag == DW_TAG_structure_type;
75}
76
77/// Returns true if `entry` has an extractable DW_ATOM_qual_name_hash and it
78/// matches `expected_hash`.
79static bool
80EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry,
81 uint32_t expected_hash) {
82 std::optional<llvm::DWARFFormValue> form_value =
83 entry.lookup(Atom: dwarf::DW_ATOM_qual_name_hash);
84 if (!form_value)
85 return false;
86 std::optional<uint64_t> hash = form_value->getAsUnsignedConstant();
87 return hash && (*hash == expected_hash);
88}
89
90/// Returns true if `entry` has an extractable DW_ATOM_die_tag and it matches
91/// `expected_tag`. We also consider it a match if the tags are different but
92/// in the set of {TAG_class_type, TAG_struct_type}.
93static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry,
94 dw_tag_t expected_tag) {
95 std::optional<llvm::DWARFFormValue> form_value =
96 entry.lookup(Atom: dwarf::DW_ATOM_die_tag);
97 if (!form_value)
98 return false;
99 std::optional<uint64_t> maybe_tag = form_value->getAsUnsignedConstant();
100 if (!maybe_tag)
101 return false;
102 auto tag = static_cast<dw_tag_t>(*maybe_tag);
103 return tag == expected_tag ||
104 (IsClassOrStruct(tag) && IsClassOrStruct(tag: expected_tag));
105}
106
107/// Returns true if `entry` has an extractable DW_ATOM_type_flags and the flag
108/// "DW_FLAG_type_implementation" is set.
109static bool
110HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry) {
111 std::optional<llvm::DWARFFormValue> form_value =
112 entry.lookup(Atom: dwarf::DW_ATOM_type_flags);
113 if (!form_value)
114 return false;
115 std::optional<uint64_t> Flags = form_value->getAsUnsignedConstant();
116 return Flags &&
117 (*Flags & llvm::dwarf::AcceleratorTable::DW_FLAG_type_implementation);
118}
119
120void AppleDWARFIndex::SearchFor(const llvm::AppleAcceleratorTable &table,
121 llvm::StringRef name,
122 llvm::function_ref<bool(DWARFDIE die)> callback,
123 std::optional<dw_tag_t> search_for_tag,
124 std::optional<uint32_t> search_for_qualhash) {
125 auto converted_cb = DIERefCallback(callback, name);
126 for (const auto &entry : table.equal_range(Key: name)) {
127 if (search_for_qualhash &&
128 !EntryHasMatchingQualhash(entry, expected_hash: *search_for_qualhash))
129 continue;
130 if (search_for_tag && !EntryHasMatchingTag(entry, expected_tag: *search_for_tag))
131 continue;
132 if (!converted_cb(entry))
133 break;
134 }
135}
136
137void AppleDWARFIndex::GetGlobalVariables(
138 ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {
139 if (!m_apple_names_up)
140 return;
141 SearchFor(table: *m_apple_names_up, name: basename, callback);
142}
143
144void AppleDWARFIndex::GetGlobalVariables(
145 const RegularExpression &regex,
146 llvm::function_ref<bool(DWARFDIE die)> callback) {
147 if (!m_apple_names_up)
148 return;
149
150 DIERefCallbackImpl converted_cb = DIERefCallback(callback, name: regex.GetText());
151
152 for (const auto &entry : m_apple_names_up->entries())
153 if (std::optional<llvm::StringRef> name = entry.readName();
154 name && Mangled(*name).NameMatches(regex))
155 if (!converted_cb(entry.BaseEntry))
156 return;
157}
158
159void AppleDWARFIndex::GetGlobalVariables(
160 DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
161 if (!m_apple_names_up)
162 return;
163
164 const DWARFUnit &non_skeleton_cu = cu.GetNonSkeletonUnit();
165 dw_offset_t lower_bound = non_skeleton_cu.GetOffset();
166 dw_offset_t upper_bound = non_skeleton_cu.GetNextUnitOffset();
167 auto is_in_range = [lower_bound, upper_bound](std::optional<uint32_t> val) {
168 return val.has_value() && *val >= lower_bound && *val < upper_bound;
169 };
170
171 DIERefCallbackImpl converted_cb = DIERefCallback(callback);
172 for (auto entry : m_apple_names_up->entries()) {
173 if (is_in_range(entry.BaseEntry.getDIESectionOffset()))
174 if (!converted_cb(entry.BaseEntry))
175 return;
176 }
177}
178
179void AppleDWARFIndex::GetObjCMethods(
180 ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) {
181 if (!m_apple_objc_up)
182 return;
183 SearchFor(table: *m_apple_objc_up, name: class_name, callback);
184}
185
186void AppleDWARFIndex::GetCompleteObjCClass(
187 ConstString class_name, bool must_be_implementation,
188 llvm::function_ref<bool(DWARFDIE die)> callback) {
189 if (!m_apple_types_up)
190 return;
191
192 llvm::SmallVector<DIERef> decl_dies;
193 auto converted_cb = DIERefCallback(callback, name: class_name);
194
195 for (const auto &entry : m_apple_types_up->equal_range(Key: class_name)) {
196 if (HasImplementationFlag(entry)) {
197 converted_cb(entry);
198 return;
199 }
200
201 decl_dies.emplace_back(Args: std::nullopt, Args: DIERef::Section::DebugInfo,
202 Args: *entry.getDIESectionOffset());
203 }
204
205 if (must_be_implementation)
206 return;
207 for (DIERef ref : decl_dies)
208 if (!converted_cb(ref))
209 return;
210}
211
212void AppleDWARFIndex::GetTypes(
213 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
214 if (!m_apple_types_up)
215 return;
216 SearchFor(table: *m_apple_types_up, name, callback);
217}
218
219void AppleDWARFIndex::GetTypes(
220 const DWARFDeclContext &context,
221 llvm::function_ref<bool(DWARFDIE die)> callback) {
222 if (!m_apple_types_up)
223 return;
224
225 Log *log = GetLog(mask: DWARFLog::TypeCompletion | DWARFLog::Lookups);
226 const bool entries_have_tag =
227 m_apple_types_up->containsAtomType(AtomTy: DW_ATOM_die_tag);
228 const bool entries_have_qual_hash =
229 m_apple_types_up->containsAtomType(AtomTy: DW_ATOM_qual_name_hash);
230
231 llvm::StringRef expected_name = context[0].name;
232
233 if (entries_have_tag && entries_have_qual_hash) {
234 const dw_tag_t expected_tag = context[0].tag;
235 const uint32_t expected_qualname_hash =
236 llvm::djbHash(Buffer: context.GetQualifiedName());
237 if (log)
238 m_module.LogMessage(log, format: "FindByNameAndTagAndQualifiedNameHash()");
239 SearchFor(table: *m_apple_types_up, name: expected_name, callback, search_for_tag: expected_tag,
240 search_for_qualhash: expected_qualname_hash);
241 return;
242 }
243
244 // Historically, if there are no tags, we also ignore qual_hash (why?)
245 if (!entries_have_tag) {
246 SearchFor(table: *m_apple_names_up, name: expected_name, callback);
247 return;
248 }
249
250 // We have a tag but no qual hash.
251
252 // When searching for a scoped type (for example,
253 // "std::vector<int>::const_iterator") searching for the innermost
254 // name alone ("const_iterator") could yield many false
255 // positives. By searching for the parent type ("vector<int>")
256 // first we can avoid extracting type DIEs from object files that
257 // would fail the filter anyway.
258 if ((context.GetSize() > 1) && IsClassOrStruct(tag: context[1].tag))
259 if (m_apple_types_up->equal_range(Key: context[1].name).empty())
260 return;
261
262 if (log)
263 m_module.LogMessage(log, format: "FindByNameAndTag()");
264 const dw_tag_t expected_tag = context[0].tag;
265 SearchFor(table: *m_apple_types_up, name: expected_name, callback, search_for_tag: expected_tag);
266 return;
267}
268
269void AppleDWARFIndex::GetNamespaces(
270 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
271 if (!m_apple_namespaces_up)
272 return;
273 SearchFor(table: *m_apple_namespaces_up, name, callback);
274}
275
276void AppleDWARFIndex::GetFunctions(
277 const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf,
278 const CompilerDeclContext &parent_decl_ctx,
279 llvm::function_ref<bool(DWARFDIE die)> callback) {
280 if (!m_apple_names_up)
281 return;
282
283 ConstString name = lookup_info.GetLookupName();
284 for (const auto &entry : m_apple_names_up->equal_range(Key: name)) {
285 DIERef die_ref(std::nullopt, DIERef::Section::DebugInfo,
286 *entry.getDIESectionOffset());
287 if (!ProcessFunctionDIE(lookup_info, ref: die_ref, dwarf, parent_decl_ctx,
288 callback))
289 return;
290 }
291}
292
293void AppleDWARFIndex::GetFunctions(
294 const RegularExpression &regex,
295 llvm::function_ref<bool(DWARFDIE die)> callback) {
296 return GetGlobalVariables(regex, callback);
297}
298
299void AppleDWARFIndex::Dump(Stream &s) {
300 if (m_apple_names_up)
301 s.PutCString(cstr: ".apple_names index present\n");
302 if (m_apple_namespaces_up)
303 s.PutCString(cstr: ".apple_namespaces index present\n");
304 if (m_apple_types_up)
305 s.PutCString(cstr: ".apple_types index present\n");
306 if (m_apple_objc_up)
307 s.PutCString(cstr: ".apple_objc index present\n");
308 // TODO: Dump index contents
309}
310

source code of lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp