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 
25 class RemoteNXMapTable;
26 
27 namespace lldb_private {
28 
29 class AppleObjCRuntimeV2 : public AppleObjCRuntime {
30 public:
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 
GetPluginNameStatic()40   static llvm::StringRef GetPluginNameStatic() { return "apple-objc-v2"; }
41 
42   LanguageRuntime *GetPreferredLanguageRuntime(ValueObject &in_value) override;
43 
44   static char ID;
45 
isA(const void * ClassID)46   bool isA(const void *ClassID) const override {
47     return ClassID == &ID || AppleObjCRuntime::isA(ClassID);
48   }
49 
classof(const LanguageRuntime * runtime)50   static bool classof(const LanguageRuntime *runtime) {
51     return runtime->isA(&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 
GetPluginName()63   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
64 
GetRuntimeVersion()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 
GetTaggedPointerVendor()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.
GetRelativeSelectorBaseAddr()93   lldb::addr_t GetRelativeSelectorBaseAddr() {
94     return m_relative_selector_base;
95   }
96 
SetRelativeSelectorBaseAddr(lldb::addr_t relative_selector_base)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 
112 protected:
113   lldb::BreakpointResolverSP
114   CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
115                           bool throw_bp) override;
116 
117 private:
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 
TaggedPointerVendorV2(AppleObjCRuntimeV2 & runtime)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:
TaggedPointerVendorLegacy(AppleObjCRuntimeV2 & runtime)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 
DescriptorMapUpdateResultDescriptorMapUpdateResult294     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 
FailDescriptorMapUpdateResult302     static DescriptorMapUpdateResult Fail() { return {false, false, 0}; }
303 
SuccessDescriptorMapUpdateResult304     static DescriptorMapUpdateResult Success(uint32_t found) {
305       return {true, false, found};
306     }
307 
RetryDescriptorMapUpdateResult308     static DescriptorMapUpdateResult Retry() { return {false, true, 0}; }
309   };
310 
311   /// Abstraction to read the Objective-C class info.
312   class ClassInfoExtractor {
313   public:
ClassInfoExtractor(AppleObjCRuntimeV2 & runtime)314     ClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {}
GetMutex()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:
DynamicClassInfoExtractor(AppleObjCRuntimeV2 & runtime)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:
SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 & runtime)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 
SetNeedsUpdate()395     void SetNeedsUpdate() { m_needs_update = true; }
396 
397     bool IsImageLoaded(uint16_t image_index);
398 
399     uint64_t GetVersion();
400 
401   private:
SharedCacheImageHeaders(AppleObjCRuntimeV2 & runtime,lldb::addr_t headerInfoRWs_ptr,uint32_t count,uint32_t entsize)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 
GetNonPointerIsaCache()454   NonPointerISACache *GetNonPointerIsaCache() {
455     if (!m_non_pointer_isa_cache_up)
456       m_non_pointer_isa_cache_up.reset(
457           NonPointerISACache::CreateInstance(*this, 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