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
24class RemoteNXMapTable;
25
26namespace lldb_private {
27
28class AppleObjCRuntimeV2 : public AppleObjCRuntime {
29public:
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(&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> GetSharedCacheImageHeaderVersion();
108
109protected:
110  lldb::BreakpointResolverSP
111  CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
112                          bool throw_bp) override;
113
114private:
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 ClassInfoExtractor {
310  public:
311    ClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {}
312    std::mutex &GetMutex() { return m_mutex; }
313
314  protected:
315    /// The lifetime of this object is tied to that of the runtime.
316    AppleObjCRuntimeV2 &m_runtime;
317    std::mutex m_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 DynamicClassInfoExtractor : public ClassInfoExtractor {
331  public:
332    DynamicClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
333        : ClassInfoExtractor(runtime) {}
334
335    DescriptorMapUpdateResult
336    UpdateISAToDescriptorMap(RemoteNXMapTable &hash_table);
337
338  private:
339    enum Helper {
340      gdb_objc_realized_classes,
341      objc_copyRealizedClassList,
342      objc_getRealizedClassList_trylock
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 ComputeHelper(ExecutionContext &exe_ctx) const;
350
351    UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx,
352                                                 Helper helper);
353    lldb::addr_t &GetClassInfoArgs(Helper helper);
354
355    std::unique_ptr<UtilityFunction>
356    GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, Helper helper,
357                                    std::string code, std::string name);
358
359    struct UtilityFunctionHelper {
360      std::unique_ptr<UtilityFunction> utility_function;
361      lldb::addr_t args = LLDB_INVALID_ADDRESS;
362    };
363
364    UtilityFunctionHelper m_gdb_objc_realized_classes_helper;
365    UtilityFunctionHelper m_objc_copyRealizedClassList_helper;
366    UtilityFunctionHelper m_objc_getRealizedClassList_trylock_helper;
367  };
368
369  /// Abstraction to read the Objective-C class info from the shared cache.
370  class SharedCacheClassInfoExtractor : public ClassInfoExtractor {
371  public:
372    SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
373        : ClassInfoExtractor(runtime) {}
374
375    DescriptorMapUpdateResult UpdateISAToDescriptorMap();
376
377  private:
378    UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx);
379
380    std::unique_ptr<UtilityFunction>
381    GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx);
382
383    std::unique_ptr<UtilityFunction> m_utility_function;
384    lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
385  };
386
387  class SharedCacheImageHeaders {
388  public:
389    static std::unique_ptr<SharedCacheImageHeaders>
390    CreateSharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime);
391
392    void SetNeedsUpdate() { m_needs_update = true; }
393
394    bool IsImageLoaded(uint16_t image_index);
395
396    uint64_t GetVersion();
397
398  private:
399    SharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime,
400                            lldb::addr_t headerInfoRWs_ptr, 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 UpdateIfNeeded();
406
407    AppleObjCRuntimeV2 &m_runtime;
408    lldb::addr_t m_headerInfoRWs_ptr;
409    llvm::BitVector m_loaded_images;
410    uint64_t m_version;
411    uint32_t m_count;
412    uint32_t m_entsize;
413    bool m_needs_update;
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 ParseClassInfoArray(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          NonPointerISACache::CreateInstance(*this, 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 m_dynamic_class_info_extractor;
459  SharedCacheClassInfoExtractor m_shared_cache_class_info_extractor;
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> m_shared_cache_image_headers_up;
478};
479
480} // namespace lldb_private
481
482#endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H
483