1 | //===-- AppleObjCRuntimeV2.h ------------------------------------*- C++ -*-===// |
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 | #ifndef LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H |
10 | #define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H |
11 | |
12 | #include <map> |
13 | #include <memory> |
14 | #include <mutex> |
15 | #include <optional> |
16 | |
17 | #include "AppleObjCRuntime.h" |
18 | #include "lldb/lldb-private.h" |
19 | |
20 | #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" |
21 | |
22 | #include "llvm/ADT/BitVector.h" |
23 | |
24 | class RemoteNXMapTable; |
25 | |
26 | namespace lldb_private { |
27 | |
28 | class AppleObjCRuntimeV2 : public AppleObjCRuntime { |
29 | public: |
30 | ~AppleObjCRuntimeV2() override = default; |
31 | |
32 | static void Initialize(); |
33 | |
34 | static void Terminate(); |
35 | |
36 | static lldb_private::LanguageRuntime * |
37 | CreateInstance(Process *process, lldb::LanguageType language); |
38 | |
39 | static llvm::StringRef GetPluginNameStatic() { return "apple-objc-v2" ; } |
40 | |
41 | LanguageRuntime *GetPreferredLanguageRuntime(ValueObject &in_value) override; |
42 | |
43 | static char ID; |
44 | |
45 | bool isA(const void *ClassID) const override { |
46 | return ClassID == &ID || AppleObjCRuntime::isA(ClassID); |
47 | } |
48 | |
49 | static bool classof(const LanguageRuntime *runtime) { |
50 | return runtime->isA(ClassID: &ID); |
51 | } |
52 | |
53 | bool GetDynamicTypeAndAddress(ValueObject &in_value, |
54 | lldb::DynamicValueType use_dynamic, |
55 | TypeAndOrName &class_type_or_name, |
56 | Address &address, Value::ValueType &value_type, |
57 | llvm::ArrayRef<uint8_t> &local_buffer) override; |
58 | |
59 | llvm::Expected<std::unique_ptr<UtilityFunction>> |
60 | CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override; |
61 | |
62 | llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } |
63 | |
64 | ObjCRuntimeVersions GetRuntimeVersion() const override { |
65 | return ObjCRuntimeVersions::eAppleObjC_V2; |
66 | } |
67 | |
68 | size_t GetByteOffsetForIvar(CompilerType &parent_ast_type, |
69 | const char *ivar_name) override; |
70 | |
71 | void UpdateISAToDescriptorMapIfNeeded() override; |
72 | |
73 | ClassDescriptorSP GetClassDescriptor(ValueObject &valobj) override; |
74 | |
75 | ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override; |
76 | |
77 | DeclVendor *GetDeclVendor() override; |
78 | |
79 | lldb::addr_t LookupRuntimeSymbol(ConstString name) override; |
80 | |
81 | EncodingToTypeSP GetEncodingToType() override; |
82 | |
83 | bool IsTaggedPointer(lldb::addr_t ptr) override; |
84 | |
85 | TaggedPointerVendor *GetTaggedPointerVendor() override { |
86 | return m_tagged_pointer_vendor_up.get(); |
87 | } |
88 | |
89 | lldb::addr_t GetTaggedPointerObfuscator(); |
90 | |
91 | /// Returns the base address for relative method list selector strings. |
92 | lldb::addr_t GetRelativeSelectorBaseAddr() { |
93 | return m_relative_selector_base; |
94 | } |
95 | |
96 | void SetRelativeSelectorBaseAddr(lldb::addr_t relative_selector_base) { |
97 | m_relative_selector_base = relative_selector_base; |
98 | } |
99 | |
100 | void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, |
101 | lldb::addr_t &cf_false) override; |
102 | |
103 | void ModulesDidLoad(const ModuleList &module_list) override; |
104 | |
105 | bool IsSharedCacheImageLoaded(uint16_t image_index); |
106 | |
107 | std::optional<uint64_t> (); |
108 | |
109 | StructuredData::ObjectSP GetLanguageSpecificData(SymbolContext sc) override; |
110 | |
111 | protected: |
112 | lldb::BreakpointResolverSP |
113 | CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp, |
114 | bool throw_bp) override; |
115 | |
116 | private: |
117 | class HashTableSignature { |
118 | public: |
119 | HashTableSignature(); |
120 | |
121 | bool NeedsUpdate(Process *process, AppleObjCRuntimeV2 *runtime, |
122 | RemoteNXMapTable &hash_table); |
123 | |
124 | void UpdateSignature(const RemoteNXMapTable &hash_table); |
125 | |
126 | protected: |
127 | uint32_t m_count = 0; |
128 | uint32_t m_num_buckets = 0; |
129 | lldb::addr_t m_buckets_ptr = 0; |
130 | }; |
131 | |
132 | class NonPointerISACache { |
133 | public: |
134 | static NonPointerISACache * |
135 | CreateInstance(AppleObjCRuntimeV2 &runtime, |
136 | const lldb::ModuleSP &objc_module_sp); |
137 | |
138 | ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(ObjCISA isa); |
139 | |
140 | private: |
141 | NonPointerISACache(AppleObjCRuntimeV2 &runtime, |
142 | const lldb::ModuleSP &objc_module_sp, |
143 | uint64_t objc_debug_isa_class_mask, |
144 | uint64_t objc_debug_isa_magic_mask, |
145 | uint64_t objc_debug_isa_magic_value, |
146 | uint64_t objc_debug_indexed_isa_magic_mask, |
147 | uint64_t objc_debug_indexed_isa_magic_value, |
148 | uint64_t objc_debug_indexed_isa_index_mask, |
149 | uint64_t objc_debug_indexed_isa_index_shift, |
150 | lldb::addr_t objc_indexed_classes); |
151 | |
152 | bool EvaluateNonPointerISA(ObjCISA isa, ObjCISA &ret_isa); |
153 | |
154 | AppleObjCRuntimeV2 &m_runtime; |
155 | std::map<ObjCISA, ObjCLanguageRuntime::ClassDescriptorSP> m_cache; |
156 | lldb::ModuleWP m_objc_module_wp; |
157 | uint64_t m_objc_debug_isa_class_mask; |
158 | uint64_t m_objc_debug_isa_magic_mask; |
159 | uint64_t m_objc_debug_isa_magic_value; |
160 | |
161 | uint64_t m_objc_debug_indexed_isa_magic_mask; |
162 | uint64_t m_objc_debug_indexed_isa_magic_value; |
163 | uint64_t m_objc_debug_indexed_isa_index_mask; |
164 | uint64_t m_objc_debug_indexed_isa_index_shift; |
165 | lldb::addr_t m_objc_indexed_classes; |
166 | |
167 | std::vector<lldb::addr_t> m_indexed_isa_cache; |
168 | |
169 | friend class AppleObjCRuntimeV2; |
170 | |
171 | NonPointerISACache(const NonPointerISACache &) = delete; |
172 | const NonPointerISACache &operator=(const NonPointerISACache &) = delete; |
173 | }; |
174 | |
175 | class TaggedPointerVendorV2 |
176 | : public ObjCLanguageRuntime::TaggedPointerVendor { |
177 | public: |
178 | ~TaggedPointerVendorV2() override = default; |
179 | |
180 | static TaggedPointerVendorV2 * |
181 | CreateInstance(AppleObjCRuntimeV2 &runtime, |
182 | const lldb::ModuleSP &objc_module_sp); |
183 | |
184 | protected: |
185 | AppleObjCRuntimeV2 &m_runtime; |
186 | |
187 | TaggedPointerVendorV2(AppleObjCRuntimeV2 &runtime) |
188 | : TaggedPointerVendor(), m_runtime(runtime) {} |
189 | |
190 | private: |
191 | TaggedPointerVendorV2(const TaggedPointerVendorV2 &) = delete; |
192 | const TaggedPointerVendorV2 & |
193 | operator=(const TaggedPointerVendorV2 &) = delete; |
194 | }; |
195 | |
196 | class TaggedPointerVendorRuntimeAssisted : public TaggedPointerVendorV2 { |
197 | public: |
198 | bool IsPossibleTaggedPointer(lldb::addr_t ptr) override; |
199 | |
200 | ObjCLanguageRuntime::ClassDescriptorSP |
201 | GetClassDescriptor(lldb::addr_t ptr) override; |
202 | |
203 | protected: |
204 | TaggedPointerVendorRuntimeAssisted( |
205 | AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, |
206 | uint32_t objc_debug_taggedpointer_slot_shift, |
207 | uint32_t objc_debug_taggedpointer_slot_mask, |
208 | uint32_t objc_debug_taggedpointer_payload_lshift, |
209 | uint32_t objc_debug_taggedpointer_payload_rshift, |
210 | lldb::addr_t objc_debug_taggedpointer_classes); |
211 | |
212 | typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache; |
213 | typedef Cache::iterator CacheIterator; |
214 | Cache m_cache; |
215 | uint64_t m_objc_debug_taggedpointer_mask; |
216 | uint32_t m_objc_debug_taggedpointer_slot_shift; |
217 | uint32_t m_objc_debug_taggedpointer_slot_mask; |
218 | uint32_t m_objc_debug_taggedpointer_payload_lshift; |
219 | uint32_t m_objc_debug_taggedpointer_payload_rshift; |
220 | lldb::addr_t m_objc_debug_taggedpointer_classes; |
221 | |
222 | friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; |
223 | |
224 | TaggedPointerVendorRuntimeAssisted( |
225 | const TaggedPointerVendorRuntimeAssisted &) = delete; |
226 | const TaggedPointerVendorRuntimeAssisted & |
227 | operator=(const TaggedPointerVendorRuntimeAssisted &) = delete; |
228 | }; |
229 | |
230 | class TaggedPointerVendorExtended |
231 | : public TaggedPointerVendorRuntimeAssisted { |
232 | public: |
233 | ObjCLanguageRuntime::ClassDescriptorSP |
234 | GetClassDescriptor(lldb::addr_t ptr) override; |
235 | |
236 | protected: |
237 | TaggedPointerVendorExtended( |
238 | AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, |
239 | uint64_t objc_debug_taggedpointer_ext_mask, |
240 | uint32_t objc_debug_taggedpointer_slot_shift, |
241 | uint32_t objc_debug_taggedpointer_ext_slot_shift, |
242 | uint32_t objc_debug_taggedpointer_slot_mask, |
243 | uint32_t objc_debug_taggedpointer_ext_slot_mask, |
244 | uint32_t objc_debug_taggedpointer_payload_lshift, |
245 | uint32_t objc_debug_taggedpointer_payload_rshift, |
246 | uint32_t objc_debug_taggedpointer_ext_payload_lshift, |
247 | uint32_t objc_debug_taggedpointer_ext_payload_rshift, |
248 | lldb::addr_t objc_debug_taggedpointer_classes, |
249 | lldb::addr_t objc_debug_taggedpointer_ext_classes); |
250 | |
251 | bool IsPossibleExtendedTaggedPointer(lldb::addr_t ptr); |
252 | |
253 | typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache; |
254 | typedef Cache::iterator CacheIterator; |
255 | Cache m_ext_cache; |
256 | uint64_t m_objc_debug_taggedpointer_ext_mask; |
257 | uint32_t m_objc_debug_taggedpointer_ext_slot_shift; |
258 | uint32_t m_objc_debug_taggedpointer_ext_slot_mask; |
259 | uint32_t m_objc_debug_taggedpointer_ext_payload_lshift; |
260 | uint32_t m_objc_debug_taggedpointer_ext_payload_rshift; |
261 | lldb::addr_t m_objc_debug_taggedpointer_ext_classes; |
262 | |
263 | friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; |
264 | |
265 | TaggedPointerVendorExtended(const TaggedPointerVendorExtended &) = delete; |
266 | const TaggedPointerVendorExtended & |
267 | operator=(const TaggedPointerVendorExtended &) = delete; |
268 | }; |
269 | |
270 | class TaggedPointerVendorLegacy : public TaggedPointerVendorV2 { |
271 | public: |
272 | bool IsPossibleTaggedPointer(lldb::addr_t ptr) override; |
273 | |
274 | ObjCLanguageRuntime::ClassDescriptorSP |
275 | GetClassDescriptor(lldb::addr_t ptr) override; |
276 | |
277 | protected: |
278 | TaggedPointerVendorLegacy(AppleObjCRuntimeV2 &runtime) |
279 | : TaggedPointerVendorV2(runtime) {} |
280 | |
281 | friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; |
282 | |
283 | TaggedPointerVendorLegacy(const TaggedPointerVendorLegacy &) = delete; |
284 | const TaggedPointerVendorLegacy & |
285 | operator=(const TaggedPointerVendorLegacy &) = delete; |
286 | }; |
287 | |
288 | struct DescriptorMapUpdateResult { |
289 | bool m_update_ran; |
290 | bool m_retry_update; |
291 | uint32_t m_num_found; |
292 | |
293 | DescriptorMapUpdateResult(bool ran, bool retry, uint32_t found) { |
294 | m_update_ran = ran; |
295 | |
296 | m_retry_update = retry; |
297 | |
298 | m_num_found = found; |
299 | } |
300 | |
301 | static DescriptorMapUpdateResult Fail() { return {false, false, 0}; } |
302 | |
303 | static DescriptorMapUpdateResult Success(uint32_t found) { |
304 | return {true, false, found}; |
305 | } |
306 | |
307 | static DescriptorMapUpdateResult Retry() { return {false, true, 0}; } |
308 | }; |
309 | |
310 | /// Abstraction to read the Objective-C class info. |
311 | class { |
312 | public: |
313 | (AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {} |
314 | std::mutex &() { return m_mutex; } |
315 | |
316 | protected: |
317 | /// The lifetime of this object is tied to that of the runtime. |
318 | AppleObjCRuntimeV2 &; |
319 | std::mutex ; |
320 | }; |
321 | |
322 | /// We can read the class info from the Objective-C runtime using |
323 | /// gdb_objc_realized_classes, objc_copyRealizedClassList or |
324 | /// objc_getRealizedClassList_trylock. The RealizedClassList variants are |
325 | /// preferred because they include lazily named classes, but they are not |
326 | /// always available or safe to call. |
327 | /// |
328 | /// We potentially need more than one helper for the same process, because we |
329 | /// may need to use gdb_objc_realized_classes until dyld is initialized and |
330 | /// then switch over to objc_copyRealizedClassList or |
331 | /// objc_getRealizedClassList_trylock for lazily named classes. |
332 | class : public ClassInfoExtractor { |
333 | public: |
334 | (AppleObjCRuntimeV2 &runtime) |
335 | : ClassInfoExtractor(runtime) {} |
336 | |
337 | DescriptorMapUpdateResult |
338 | (RemoteNXMapTable &hash_table); |
339 | |
340 | private: |
341 | enum { |
342 | , |
343 | , |
344 | |
345 | }; |
346 | |
347 | /// Compute which helper to use. If dyld is not yet fully initialized we |
348 | /// must use gdb_objc_realized_classes. Otherwise, we prefer |
349 | /// objc_getRealizedClassList_trylock and objc_copyRealizedClassList |
350 | /// respectively, depending on availability. |
351 | Helper (ExecutionContext &exe_ctx) const; |
352 | |
353 | UtilityFunction *(ExecutionContext &exe_ctx, |
354 | Helper helper); |
355 | lldb::addr_t &(Helper helper); |
356 | |
357 | std::unique_ptr<UtilityFunction> |
358 | (ExecutionContext &exe_ctx, Helper helper, |
359 | std::string code, std::string name); |
360 | |
361 | struct { |
362 | std::unique_ptr<UtilityFunction> ; |
363 | lldb::addr_t = LLDB_INVALID_ADDRESS; |
364 | }; |
365 | |
366 | UtilityFunctionHelper ; |
367 | UtilityFunctionHelper ; |
368 | UtilityFunctionHelper ; |
369 | }; |
370 | |
371 | /// Abstraction to read the Objective-C class info from the shared cache. |
372 | class : public ClassInfoExtractor { |
373 | public: |
374 | (AppleObjCRuntimeV2 &runtime) |
375 | : ClassInfoExtractor(runtime) {} |
376 | |
377 | DescriptorMapUpdateResult (); |
378 | |
379 | private: |
380 | UtilityFunction *(ExecutionContext &exe_ctx); |
381 | |
382 | std::unique_ptr<UtilityFunction> |
383 | (ExecutionContext &exe_ctx); |
384 | |
385 | std::unique_ptr<UtilityFunction> ; |
386 | lldb::addr_t = LLDB_INVALID_ADDRESS; |
387 | }; |
388 | |
389 | class { |
390 | public: |
391 | static std::unique_ptr<SharedCacheImageHeaders> |
392 | (AppleObjCRuntimeV2 &runtime); |
393 | |
394 | void () { m_needs_update = true; } |
395 | |
396 | bool (uint16_t image_index); |
397 | |
398 | uint64_t (); |
399 | |
400 | private: |
401 | (AppleObjCRuntimeV2 &runtime, |
402 | lldb::addr_t , uint32_t count, |
403 | uint32_t entsize) |
404 | : m_runtime(runtime), m_headerInfoRWs_ptr(headerInfoRWs_ptr), |
405 | m_loaded_images(count, false), m_version(0), m_count(count), |
406 | m_entsize(entsize), m_needs_update(true) {} |
407 | llvm::Error (); |
408 | |
409 | AppleObjCRuntimeV2 &; |
410 | lldb::addr_t ; |
411 | llvm::BitVector ; |
412 | uint64_t ; |
413 | uint32_t ; |
414 | uint32_t ; |
415 | bool ; |
416 | }; |
417 | |
418 | AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp); |
419 | |
420 | ObjCISA GetPointerISA(ObjCISA isa); |
421 | |
422 | lldb::addr_t GetISAHashTablePointer(); |
423 | |
424 | /// Update the generation count of realized classes. This is not an exact |
425 | /// count but rather a value that is incremented when new classes are realized |
426 | /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will |
427 | /// change when lazily named classes get realized. |
428 | bool RealizedClassGenerationCountChanged(); |
429 | |
430 | uint32_t (const lldb_private::DataExtractor &data, |
431 | uint32_t num_class_infos); |
432 | |
433 | enum class SharedCacheWarningReason { |
434 | eExpressionUnableToRun, |
435 | eExpressionExecutionFailure, |
436 | eNotEnoughClassesRead |
437 | }; |
438 | |
439 | void WarnIfNoClassesCached(SharedCacheWarningReason reason); |
440 | void WarnIfNoExpandedSharedCache(); |
441 | |
442 | lldb::addr_t GetSharedCacheReadOnlyAddress(); |
443 | lldb::addr_t GetSharedCacheBaseAddress(); |
444 | |
445 | bool GetCFBooleanValuesIfNeeded(); |
446 | |
447 | bool HasSymbol(ConstString Name); |
448 | |
449 | NonPointerISACache *GetNonPointerIsaCache() { |
450 | if (!m_non_pointer_isa_cache_up) |
451 | m_non_pointer_isa_cache_up.reset( |
452 | p: NonPointerISACache::CreateInstance(runtime&: *this, objc_module_sp: m_objc_module_sp)); |
453 | return m_non_pointer_isa_cache_up.get(); |
454 | } |
455 | |
456 | friend class ClassDescriptorV2; |
457 | |
458 | lldb::ModuleSP m_objc_module_sp; |
459 | |
460 | DynamicClassInfoExtractor ; |
461 | SharedCacheClassInfoExtractor ; |
462 | |
463 | std::unique_ptr<DeclVendor> m_decl_vendor_up; |
464 | lldb::addr_t m_tagged_pointer_obfuscator; |
465 | lldb::addr_t m_isa_hash_table_ptr; |
466 | lldb::addr_t m_relative_selector_base; |
467 | HashTableSignature m_hash_signature; |
468 | bool m_has_object_getClass; |
469 | bool m_has_objc_copyRealizedClassList; |
470 | bool m_has_objc_getRealizedClassList_trylock; |
471 | bool m_loaded_objc_opt; |
472 | std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up; |
473 | std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up; |
474 | EncodingToTypeSP m_encoding_to_type_sp; |
475 | std::once_flag m_no_classes_cached_warning; |
476 | std::once_flag m_no_expanded_cache_warning; |
477 | std::optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values; |
478 | uint64_t m_realized_class_generation_count; |
479 | std::unique_ptr<SharedCacheImageHeaders> ; |
480 | }; |
481 | |
482 | } // namespace lldb_private |
483 | |
484 | #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H |
485 | |