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, |
57 | Value::ValueType &value_type) 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 | protected: |
110 | lldb::BreakpointResolverSP |
111 | CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp, |
112 | bool throw_bp) override; |
113 | |
114 | private: |
115 | class HashTableSignature { |
116 | public: |
117 | HashTableSignature(); |
118 | |
119 | bool NeedsUpdate(Process *process, AppleObjCRuntimeV2 *runtime, |
120 | RemoteNXMapTable &hash_table); |
121 | |
122 | void UpdateSignature(const RemoteNXMapTable &hash_table); |
123 | |
124 | protected: |
125 | uint32_t m_count = 0; |
126 | uint32_t m_num_buckets = 0; |
127 | lldb::addr_t m_buckets_ptr = 0; |
128 | }; |
129 | |
130 | class NonPointerISACache { |
131 | public: |
132 | static NonPointerISACache * |
133 | CreateInstance(AppleObjCRuntimeV2 &runtime, |
134 | const lldb::ModuleSP &objc_module_sp); |
135 | |
136 | ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(ObjCISA isa); |
137 | |
138 | private: |
139 | NonPointerISACache(AppleObjCRuntimeV2 &runtime, |
140 | const lldb::ModuleSP &objc_module_sp, |
141 | uint64_t objc_debug_isa_class_mask, |
142 | uint64_t objc_debug_isa_magic_mask, |
143 | uint64_t objc_debug_isa_magic_value, |
144 | uint64_t objc_debug_indexed_isa_magic_mask, |
145 | uint64_t objc_debug_indexed_isa_magic_value, |
146 | uint64_t objc_debug_indexed_isa_index_mask, |
147 | uint64_t objc_debug_indexed_isa_index_shift, |
148 | lldb::addr_t objc_indexed_classes); |
149 | |
150 | bool EvaluateNonPointerISA(ObjCISA isa, ObjCISA &ret_isa); |
151 | |
152 | AppleObjCRuntimeV2 &m_runtime; |
153 | std::map<ObjCISA, ObjCLanguageRuntime::ClassDescriptorSP> m_cache; |
154 | lldb::ModuleWP m_objc_module_wp; |
155 | uint64_t m_objc_debug_isa_class_mask; |
156 | uint64_t m_objc_debug_isa_magic_mask; |
157 | uint64_t m_objc_debug_isa_magic_value; |
158 | |
159 | uint64_t m_objc_debug_indexed_isa_magic_mask; |
160 | uint64_t m_objc_debug_indexed_isa_magic_value; |
161 | uint64_t m_objc_debug_indexed_isa_index_mask; |
162 | uint64_t m_objc_debug_indexed_isa_index_shift; |
163 | lldb::addr_t m_objc_indexed_classes; |
164 | |
165 | std::vector<lldb::addr_t> m_indexed_isa_cache; |
166 | |
167 | friend class AppleObjCRuntimeV2; |
168 | |
169 | NonPointerISACache(const NonPointerISACache &) = delete; |
170 | const NonPointerISACache &operator=(const NonPointerISACache &) = delete; |
171 | }; |
172 | |
173 | class TaggedPointerVendorV2 |
174 | : public ObjCLanguageRuntime::TaggedPointerVendor { |
175 | public: |
176 | ~TaggedPointerVendorV2() override = default; |
177 | |
178 | static TaggedPointerVendorV2 * |
179 | CreateInstance(AppleObjCRuntimeV2 &runtime, |
180 | const lldb::ModuleSP &objc_module_sp); |
181 | |
182 | protected: |
183 | AppleObjCRuntimeV2 &m_runtime; |
184 | |
185 | TaggedPointerVendorV2(AppleObjCRuntimeV2 &runtime) |
186 | : TaggedPointerVendor(), m_runtime(runtime) {} |
187 | |
188 | private: |
189 | TaggedPointerVendorV2(const TaggedPointerVendorV2 &) = delete; |
190 | const TaggedPointerVendorV2 & |
191 | operator=(const TaggedPointerVendorV2 &) = delete; |
192 | }; |
193 | |
194 | class TaggedPointerVendorRuntimeAssisted : public TaggedPointerVendorV2 { |
195 | public: |
196 | bool IsPossibleTaggedPointer(lldb::addr_t ptr) override; |
197 | |
198 | ObjCLanguageRuntime::ClassDescriptorSP |
199 | GetClassDescriptor(lldb::addr_t ptr) override; |
200 | |
201 | protected: |
202 | TaggedPointerVendorRuntimeAssisted( |
203 | AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, |
204 | uint32_t objc_debug_taggedpointer_slot_shift, |
205 | uint32_t objc_debug_taggedpointer_slot_mask, |
206 | uint32_t objc_debug_taggedpointer_payload_lshift, |
207 | uint32_t objc_debug_taggedpointer_payload_rshift, |
208 | lldb::addr_t objc_debug_taggedpointer_classes); |
209 | |
210 | typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache; |
211 | typedef Cache::iterator CacheIterator; |
212 | Cache m_cache; |
213 | uint64_t m_objc_debug_taggedpointer_mask; |
214 | uint32_t m_objc_debug_taggedpointer_slot_shift; |
215 | uint32_t m_objc_debug_taggedpointer_slot_mask; |
216 | uint32_t m_objc_debug_taggedpointer_payload_lshift; |
217 | uint32_t m_objc_debug_taggedpointer_payload_rshift; |
218 | lldb::addr_t m_objc_debug_taggedpointer_classes; |
219 | |
220 | friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; |
221 | |
222 | TaggedPointerVendorRuntimeAssisted( |
223 | const TaggedPointerVendorRuntimeAssisted &) = delete; |
224 | const TaggedPointerVendorRuntimeAssisted & |
225 | operator=(const TaggedPointerVendorRuntimeAssisted &) = delete; |
226 | }; |
227 | |
228 | class TaggedPointerVendorExtended |
229 | : public TaggedPointerVendorRuntimeAssisted { |
230 | public: |
231 | ObjCLanguageRuntime::ClassDescriptorSP |
232 | GetClassDescriptor(lldb::addr_t ptr) override; |
233 | |
234 | protected: |
235 | TaggedPointerVendorExtended( |
236 | AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, |
237 | uint64_t objc_debug_taggedpointer_ext_mask, |
238 | uint32_t objc_debug_taggedpointer_slot_shift, |
239 | uint32_t objc_debug_taggedpointer_ext_slot_shift, |
240 | uint32_t objc_debug_taggedpointer_slot_mask, |
241 | uint32_t objc_debug_taggedpointer_ext_slot_mask, |
242 | uint32_t objc_debug_taggedpointer_payload_lshift, |
243 | uint32_t objc_debug_taggedpointer_payload_rshift, |
244 | uint32_t objc_debug_taggedpointer_ext_payload_lshift, |
245 | uint32_t objc_debug_taggedpointer_ext_payload_rshift, |
246 | lldb::addr_t objc_debug_taggedpointer_classes, |
247 | lldb::addr_t objc_debug_taggedpointer_ext_classes); |
248 | |
249 | bool IsPossibleExtendedTaggedPointer(lldb::addr_t ptr); |
250 | |
251 | typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache; |
252 | typedef Cache::iterator CacheIterator; |
253 | Cache m_ext_cache; |
254 | uint64_t m_objc_debug_taggedpointer_ext_mask; |
255 | uint32_t m_objc_debug_taggedpointer_ext_slot_shift; |
256 | uint32_t m_objc_debug_taggedpointer_ext_slot_mask; |
257 | uint32_t m_objc_debug_taggedpointer_ext_payload_lshift; |
258 | uint32_t m_objc_debug_taggedpointer_ext_payload_rshift; |
259 | lldb::addr_t m_objc_debug_taggedpointer_ext_classes; |
260 | |
261 | friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; |
262 | |
263 | TaggedPointerVendorExtended(const TaggedPointerVendorExtended &) = delete; |
264 | const TaggedPointerVendorExtended & |
265 | operator=(const TaggedPointerVendorExtended &) = delete; |
266 | }; |
267 | |
268 | class TaggedPointerVendorLegacy : public TaggedPointerVendorV2 { |
269 | public: |
270 | bool IsPossibleTaggedPointer(lldb::addr_t ptr) override; |
271 | |
272 | ObjCLanguageRuntime::ClassDescriptorSP |
273 | GetClassDescriptor(lldb::addr_t ptr) override; |
274 | |
275 | protected: |
276 | TaggedPointerVendorLegacy(AppleObjCRuntimeV2 &runtime) |
277 | : TaggedPointerVendorV2(runtime) {} |
278 | |
279 | friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; |
280 | |
281 | TaggedPointerVendorLegacy(const TaggedPointerVendorLegacy &) = delete; |
282 | const TaggedPointerVendorLegacy & |
283 | operator=(const TaggedPointerVendorLegacy &) = delete; |
284 | }; |
285 | |
286 | struct DescriptorMapUpdateResult { |
287 | bool m_update_ran; |
288 | bool m_retry_update; |
289 | uint32_t m_num_found; |
290 | |
291 | DescriptorMapUpdateResult(bool ran, bool retry, uint32_t found) { |
292 | m_update_ran = ran; |
293 | |
294 | m_retry_update = retry; |
295 | |
296 | m_num_found = found; |
297 | } |
298 | |
299 | static DescriptorMapUpdateResult Fail() { return {false, false, 0}; } |
300 | |
301 | static DescriptorMapUpdateResult Success(uint32_t found) { |
302 | return {true, false, found}; |
303 | } |
304 | |
305 | static DescriptorMapUpdateResult Retry() { return {false, true, 0}; } |
306 | }; |
307 | |
308 | /// Abstraction to read the Objective-C class info. |
309 | class { |
310 | public: |
311 | (AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {} |
312 | std::mutex &() { return m_mutex; } |
313 | |
314 | protected: |
315 | /// The lifetime of this object is tied to that of the runtime. |
316 | AppleObjCRuntimeV2 &; |
317 | std::mutex ; |
318 | }; |
319 | |
320 | /// We can read the class info from the Objective-C runtime using |
321 | /// gdb_objc_realized_classes, objc_copyRealizedClassList or |
322 | /// objc_getRealizedClassList_trylock. The RealizedClassList variants are |
323 | /// preferred because they include lazily named classes, but they are not |
324 | /// always available or safe to call. |
325 | /// |
326 | /// We potentially need more than one helper for the same process, because we |
327 | /// may need to use gdb_objc_realized_classes until dyld is initialized and |
328 | /// then switch over to objc_copyRealizedClassList or |
329 | /// objc_getRealizedClassList_trylock for lazily named classes. |
330 | class : public ClassInfoExtractor { |
331 | public: |
332 | (AppleObjCRuntimeV2 &runtime) |
333 | : ClassInfoExtractor(runtime) {} |
334 | |
335 | DescriptorMapUpdateResult |
336 | (RemoteNXMapTable &hash_table); |
337 | |
338 | private: |
339 | enum { |
340 | , |
341 | , |
342 | |
343 | }; |
344 | |
345 | /// Compute which helper to use. If dyld is not yet fully initialized we |
346 | /// must use gdb_objc_realized_classes. Otherwise, we prefer |
347 | /// objc_getRealizedClassList_trylock and objc_copyRealizedClassList |
348 | /// respectively, depending on availability. |
349 | Helper (ExecutionContext &exe_ctx) const; |
350 | |
351 | UtilityFunction *(ExecutionContext &exe_ctx, |
352 | Helper helper); |
353 | lldb::addr_t &(Helper helper); |
354 | |
355 | std::unique_ptr<UtilityFunction> |
356 | (ExecutionContext &exe_ctx, Helper helper, |
357 | std::string code, std::string name); |
358 | |
359 | struct { |
360 | std::unique_ptr<UtilityFunction> ; |
361 | lldb::addr_t = LLDB_INVALID_ADDRESS; |
362 | }; |
363 | |
364 | UtilityFunctionHelper ; |
365 | UtilityFunctionHelper ; |
366 | UtilityFunctionHelper ; |
367 | }; |
368 | |
369 | /// Abstraction to read the Objective-C class info from the shared cache. |
370 | class : public ClassInfoExtractor { |
371 | public: |
372 | (AppleObjCRuntimeV2 &runtime) |
373 | : ClassInfoExtractor(runtime) {} |
374 | |
375 | DescriptorMapUpdateResult (); |
376 | |
377 | private: |
378 | UtilityFunction *(ExecutionContext &exe_ctx); |
379 | |
380 | std::unique_ptr<UtilityFunction> |
381 | (ExecutionContext &exe_ctx); |
382 | |
383 | std::unique_ptr<UtilityFunction> ; |
384 | lldb::addr_t = LLDB_INVALID_ADDRESS; |
385 | }; |
386 | |
387 | class { |
388 | public: |
389 | static std::unique_ptr<SharedCacheImageHeaders> |
390 | (AppleObjCRuntimeV2 &runtime); |
391 | |
392 | void () { m_needs_update = true; } |
393 | |
394 | bool (uint16_t image_index); |
395 | |
396 | uint64_t (); |
397 | |
398 | private: |
399 | (AppleObjCRuntimeV2 &runtime, |
400 | lldb::addr_t , uint32_t count, |
401 | uint32_t entsize) |
402 | : m_runtime(runtime), m_headerInfoRWs_ptr(headerInfoRWs_ptr), |
403 | m_loaded_images(count, false), m_version(0), m_count(count), |
404 | m_entsize(entsize), m_needs_update(true) {} |
405 | llvm::Error (); |
406 | |
407 | AppleObjCRuntimeV2 &; |
408 | lldb::addr_t ; |
409 | llvm::BitVector ; |
410 | uint64_t ; |
411 | uint32_t ; |
412 | uint32_t ; |
413 | bool ; |
414 | }; |
415 | |
416 | AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp); |
417 | |
418 | ObjCISA GetPointerISA(ObjCISA isa); |
419 | |
420 | lldb::addr_t GetISAHashTablePointer(); |
421 | |
422 | /// Update the generation count of realized classes. This is not an exact |
423 | /// count but rather a value that is incremented when new classes are realized |
424 | /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will |
425 | /// change when lazily named classes get realized. |
426 | bool RealizedClassGenerationCountChanged(); |
427 | |
428 | uint32_t (const lldb_private::DataExtractor &data, |
429 | uint32_t num_class_infos); |
430 | |
431 | enum class SharedCacheWarningReason { |
432 | eExpressionUnableToRun, |
433 | eExpressionExecutionFailure, |
434 | eNotEnoughClassesRead |
435 | }; |
436 | |
437 | void WarnIfNoClassesCached(SharedCacheWarningReason reason); |
438 | void WarnIfNoExpandedSharedCache(); |
439 | |
440 | lldb::addr_t GetSharedCacheReadOnlyAddress(); |
441 | lldb::addr_t GetSharedCacheBaseAddress(); |
442 | |
443 | bool GetCFBooleanValuesIfNeeded(); |
444 | |
445 | bool HasSymbol(ConstString Name); |
446 | |
447 | NonPointerISACache *GetNonPointerIsaCache() { |
448 | if (!m_non_pointer_isa_cache_up) |
449 | m_non_pointer_isa_cache_up.reset( |
450 | p: NonPointerISACache::CreateInstance(runtime&: *this, objc_module_sp: m_objc_module_sp)); |
451 | return m_non_pointer_isa_cache_up.get(); |
452 | } |
453 | |
454 | friend class ClassDescriptorV2; |
455 | |
456 | lldb::ModuleSP m_objc_module_sp; |
457 | |
458 | DynamicClassInfoExtractor ; |
459 | SharedCacheClassInfoExtractor ; |
460 | |
461 | std::unique_ptr<DeclVendor> m_decl_vendor_up; |
462 | lldb::addr_t m_tagged_pointer_obfuscator; |
463 | lldb::addr_t m_isa_hash_table_ptr; |
464 | lldb::addr_t m_relative_selector_base; |
465 | HashTableSignature m_hash_signature; |
466 | bool m_has_object_getClass; |
467 | bool m_has_objc_copyRealizedClassList; |
468 | bool m_has_objc_getRealizedClassList_trylock; |
469 | bool m_loaded_objc_opt; |
470 | std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up; |
471 | std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up; |
472 | EncodingToTypeSP m_encoding_to_type_sp; |
473 | std::once_flag m_no_classes_cached_warning; |
474 | std::once_flag m_no_expanded_cache_warning; |
475 | std::optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values; |
476 | uint64_t m_realized_class_generation_count; |
477 | std::unique_ptr<SharedCacheImageHeaders> ; |
478 | }; |
479 | |
480 | } // namespace lldb_private |
481 | |
482 | #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H |
483 | |