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
18 using namespace lldb_private;
19 using namespace lldb;
20 using namespace lldb_private::dwarf;
21 using namespace lldb_private::plugin::dwarf;
22
Create(Module & module,DWARFDataExtractor apple_names,DWARFDataExtractor apple_namespaces,DWARFDataExtractor apple_types,DWARFDataExtractor apple_objc,DWARFDataExtractor debug_str)23 std::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 apple_names.GetAsLLVMDWARF(), llvm_debug_str);
32
33 auto apple_namespaces_table_up =
34 std::make_unique<llvm::AppleAcceleratorTable>(
35 apple_namespaces.GetAsLLVMDWARF(), llvm_debug_str);
36
37 auto apple_types_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
38 apple_types.GetAsLLVMDWARF(), llvm_debug_str);
39
40 auto apple_objc_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
41 apple_objc.GetAsLLVMDWARF(), llvm_debug_str);
42
43 auto extract_and_check = [](auto &TablePtr) {
44 if (auto E = TablePtr->extract()) {
45 llvm::consumeError(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 module, std::move(apple_names_table_up),
64 std::move(apple_namespaces_table_up), std::move(apple_types_table_up),
65 std::move(apple_objc_table_up), apple_names.GetSharedDataBuffer(),
66 apple_namespaces.GetSharedDataBuffer(),
67 apple_types.GetSharedDataBuffer(), apple_objc.GetSharedDataBuffer());
68
69 return nullptr;
70 }
71
72 /// Returns true if `tag` is a class_type of structure_type tag.
IsClassOrStruct(dw_tag_t tag)73 static 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`.
79 static bool
EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry & entry,uint32_t expected_hash)80 EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry,
81 uint32_t expected_hash) {
82 std::optional<llvm::DWARFFormValue> form_value =
83 entry.lookup(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}.
EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry & entry,dw_tag_t expected_tag)93 static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry,
94 dw_tag_t expected_tag) {
95 std::optional<llvm::DWARFFormValue> form_value =
96 entry.lookup(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(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.
109 static bool
HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry & entry)110 HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry) {
111 std::optional<llvm::DWARFFormValue> form_value =
112 entry.lookup(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
SearchFor(const llvm::AppleAcceleratorTable & table,llvm::StringRef name,llvm::function_ref<bool (DWARFDIE die)> callback,std::optional<dw_tag_t> search_for_tag,std::optional<uint32_t> search_for_qualhash)120 void 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(name)) {
127 if (search_for_qualhash &&
128 !EntryHasMatchingQualhash(entry, *search_for_qualhash))
129 continue;
130 if (search_for_tag && !EntryHasMatchingTag(entry, *search_for_tag))
131 continue;
132 if (!converted_cb(entry))
133 break;
134 }
135 }
136
GetGlobalVariables(ConstString basename,llvm::function_ref<bool (DWARFDIE die)> callback)137 void AppleDWARFIndex::GetGlobalVariables(
138 ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {
139 if (!m_apple_names_up)
140 return;
141 SearchFor(*m_apple_names_up, basename, callback);
142 }
143
GetGlobalVariables(const RegularExpression & regex,llvm::function_ref<bool (DWARFDIE die)> callback)144 void AppleDWARFIndex::GetGlobalVariables(
145 const RegularExpression ®ex,
146 llvm::function_ref<bool(DWARFDIE die)> callback) {
147 if (!m_apple_names_up)
148 return;
149
150 DIERefCallbackImpl converted_cb = DIERefCallback(callback, 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
GetGlobalVariables(DWARFUnit & cu,llvm::function_ref<bool (DWARFDIE die)> callback)159 void 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
GetObjCMethods(ConstString class_name,llvm::function_ref<bool (DWARFDIE die)> callback)179 void AppleDWARFIndex::GetObjCMethods(
180 ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) {
181 if (!m_apple_objc_up)
182 return;
183 SearchFor(*m_apple_objc_up, class_name, callback);
184 }
185
GetCompleteObjCClass(ConstString class_name,bool must_be_implementation,llvm::function_ref<bool (DWARFDIE die)> callback)186 void 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, class_name);
194
195 for (const auto &entry : m_apple_types_up->equal_range(class_name)) {
196 if (HasImplementationFlag(entry)) {
197 converted_cb(entry);
198 return;
199 }
200
201 decl_dies.emplace_back(std::nullopt, DIERef::Section::DebugInfo,
202 *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
GetTypes(ConstString name,llvm::function_ref<bool (DWARFDIE die)> callback)212 void AppleDWARFIndex::GetTypes(
213 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
214 if (!m_apple_types_up)
215 return;
216 SearchFor(*m_apple_types_up, name, callback);
217 }
218
GetTypes(const DWARFDeclContext & context,llvm::function_ref<bool (DWARFDIE die)> callback)219 void 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(DWARFLog::TypeCompletion | DWARFLog::Lookups);
226 const bool entries_have_tag =
227 m_apple_types_up->containsAtomType(DW_ATOM_die_tag);
228 const bool entries_have_qual_hash =
229 m_apple_types_up->containsAtomType(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(context.GetQualifiedName());
237 if (log)
238 m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
239 SearchFor(*m_apple_types_up, expected_name, callback, expected_tag,
240 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(*m_apple_names_up, 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(context[1].tag))
259 if (m_apple_types_up->equal_range(context[1].name).empty())
260 return;
261
262 if (log)
263 m_module.LogMessage(log, "FindByNameAndTag()");
264 const dw_tag_t expected_tag = context[0].tag;
265 SearchFor(*m_apple_types_up, expected_name, callback, expected_tag);
266 return;
267 }
268
GetNamespaces(ConstString name,llvm::function_ref<bool (DWARFDIE die)> callback)269 void AppleDWARFIndex::GetNamespaces(
270 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
271 if (!m_apple_namespaces_up)
272 return;
273 SearchFor(*m_apple_namespaces_up, name, callback);
274 }
275
GetFunctions(const Module::LookupInfo & lookup_info,SymbolFileDWARF & dwarf,const CompilerDeclContext & parent_decl_ctx,llvm::function_ref<bool (DWARFDIE die)> callback)276 void 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(name)) {
285 DIERef die_ref(std::nullopt, DIERef::Section::DebugInfo,
286 *entry.getDIESectionOffset());
287 DWARFDIE die = dwarf.GetDIE(die_ref);
288 if (!die) {
289 ReportInvalidDIERef(die_ref, name);
290 continue;
291 }
292 if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, callback))
293 return;
294 }
295 }
296
GetFunctions(const RegularExpression & regex,llvm::function_ref<bool (DWARFDIE die)> callback)297 void AppleDWARFIndex::GetFunctions(
298 const RegularExpression ®ex,
299 llvm::function_ref<bool(DWARFDIE die)> callback) {
300 return GetGlobalVariables(regex, callback);
301 }
302
Dump(Stream & s)303 void AppleDWARFIndex::Dump(Stream &s) {
304 if (m_apple_names_up)
305 s.PutCString(".apple_names index present\n");
306 if (m_apple_namespaces_up)
307 s.PutCString(".apple_namespaces index present\n");
308 if (m_apple_types_up)
309 s.PutCString(".apple_types index present\n");
310 if (m_apple_objc_up)
311 s.PutCString(".apple_objc index present\n");
312 // TODO: Dump index contents
313 }
314