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

source code of lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h