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 class RemoteNXMapTable;
23 
24 namespace lldb_private {
25 
26 class AppleObjCRuntimeV2 : public AppleObjCRuntime {
27 public:
28   ~AppleObjCRuntimeV2() override = default;
29 
30   static void Initialize();
31 
32   static void Terminate();
33 
34   static lldb_private::LanguageRuntime *
35   CreateInstance(Process *process, lldb::LanguageType language);
36 
37   static llvm::StringRef GetPluginNameStatic() { return "apple-objc-v2"; }
38 
39   static char ID;
40 
41   bool isA(const void *ClassID) const override {
42     return ClassID == &ID || AppleObjCRuntime::isA(ClassID);
43   }
44 
45   static bool classof(const LanguageRuntime *runtime) {
46     return runtime->isA(&ID);
47   }
48 
49   bool GetDynamicTypeAndAddress(ValueObject &in_value,
50                                 lldb::DynamicValueType use_dynamic,
51                                 TypeAndOrName &class_type_or_name,
52                                 Address &address,
53                                 Value::ValueType &value_type) override;
54 
55   llvm::Expected<std::unique_ptr<UtilityFunction>>
56   CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override;
57 
58   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
59 
60   ObjCRuntimeVersions GetRuntimeVersion() const override {
61     return ObjCRuntimeVersions::eAppleObjC_V2;
62   }
63 
64   size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
65                               const char *ivar_name) override;
66 
67   void UpdateISAToDescriptorMapIfNeeded() override;
68 
69   ClassDescriptorSP GetClassDescriptor(ValueObject &in_value) override;
70 
71   ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override;
72 
73   DeclVendor *GetDeclVendor() override;
74 
75   lldb::addr_t LookupRuntimeSymbol(ConstString name) override;
76 
77   EncodingToTypeSP GetEncodingToType() override;
78 
79   bool IsTaggedPointer(lldb::addr_t ptr) override;
80 
81   TaggedPointerVendor *GetTaggedPointerVendor() override {
82     return m_tagged_pointer_vendor_up.get();
83   }
84 
85   lldb::addr_t GetTaggedPointerObfuscator();
86 
87   /// Returns the base address for relative method list selector strings.
88   lldb::addr_t GetRelativeSelectorBaseAddr() {
89     return m_relative_selector_base;
90   }
91 
92   void SetRelativeSelectorBaseAddr(lldb::addr_t relative_selector_base) {
93     m_relative_selector_base = relative_selector_base;
94   }
95 
96   void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
97                                     lldb::addr_t &cf_false) override;
98 
99   // none of these are valid ISAs - we use them to infer the type
100   // of tagged pointers - if we have something meaningful to say
101   // we report an actual type - otherwise, we just say tagged
102   // there is no connection between the values here and the tagged pointers map
103   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA = 1;
104   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSAtom = 2;
105   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSNumber = 3;
106   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDateTS = 4;
107   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSManagedObject =
108       5;
109   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDate = 6;
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 ClassInfoExtractor {
312   public:
313     ClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {}
314     std::mutex &GetMutex() { return m_mutex; }
315 
316   protected:
317     /// The lifetime of this object is tied to that of the runtime.
318     AppleObjCRuntimeV2 &m_runtime;
319     std::mutex m_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 DynamicClassInfoExtractor : public ClassInfoExtractor {
333   public:
334     DynamicClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
335         : ClassInfoExtractor(runtime) {}
336 
337     DescriptorMapUpdateResult
338     UpdateISAToDescriptorMap(RemoteNXMapTable &hash_table);
339 
340   private:
341     enum Helper {
342       gdb_objc_realized_classes,
343       objc_copyRealizedClassList,
344       objc_getRealizedClassList_trylock
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 ComputeHelper(ExecutionContext &exe_ctx) const;
352 
353     UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx,
354                                                  Helper helper);
355     lldb::addr_t &GetClassInfoArgs(Helper helper);
356 
357     std::unique_ptr<UtilityFunction>
358     GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, Helper helper,
359                                     std::string code, std::string name);
360 
361     struct UtilityFunctionHelper {
362       std::unique_ptr<UtilityFunction> utility_function;
363       lldb::addr_t args = LLDB_INVALID_ADDRESS;
364     };
365 
366     UtilityFunctionHelper m_gdb_objc_realized_classes_helper;
367     UtilityFunctionHelper m_objc_copyRealizedClassList_helper;
368     UtilityFunctionHelper m_objc_getRealizedClassList_trylock_helper;
369   };
370 
371   /// Abstraction to read the Objective-C class info from the shared cache.
372   class SharedCacheClassInfoExtractor : public ClassInfoExtractor {
373   public:
374     SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
375         : ClassInfoExtractor(runtime) {}
376 
377     DescriptorMapUpdateResult UpdateISAToDescriptorMap();
378 
379   private:
380     UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx);
381 
382     std::unique_ptr<UtilityFunction>
383     GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx);
384 
385     std::unique_ptr<UtilityFunction> m_utility_function;
386     lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
387   };
388 
389   AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
390 
391   ObjCISA GetPointerISA(ObjCISA isa);
392 
393   lldb::addr_t GetISAHashTablePointer();
394 
395   /// Update the generation count of realized classes. This is not an exact
396   /// count but rather a value that is incremented when new classes are realized
397   /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will
398   /// change when lazily named classes get realized.
399   bool RealizedClassGenerationCountChanged();
400 
401   uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
402                                uint32_t num_class_infos);
403 
404   enum class SharedCacheWarningReason {
405     eExpressionUnableToRun,
406     eExpressionExecutionFailure,
407     eNotEnoughClassesRead
408   };
409 
410   void WarnIfNoClassesCached(SharedCacheWarningReason reason);
411   void WarnIfNoExpandedSharedCache();
412 
413   lldb::addr_t GetSharedCacheReadOnlyAddress();
414   lldb::addr_t GetSharedCacheBaseAddress();
415 
416   bool GetCFBooleanValuesIfNeeded();
417 
418   bool HasSymbol(ConstString Name);
419 
420   NonPointerISACache *GetNonPointerIsaCache() {
421     if (!m_non_pointer_isa_cache_up)
422       m_non_pointer_isa_cache_up.reset(
423           NonPointerISACache::CreateInstance(*this, m_objc_module_sp));
424     return m_non_pointer_isa_cache_up.get();
425   }
426 
427   friend class ClassDescriptorV2;
428 
429   lldb::ModuleSP m_objc_module_sp;
430 
431   DynamicClassInfoExtractor m_dynamic_class_info_extractor;
432   SharedCacheClassInfoExtractor m_shared_cache_class_info_extractor;
433 
434   std::unique_ptr<DeclVendor> m_decl_vendor_up;
435   lldb::addr_t m_tagged_pointer_obfuscator;
436   lldb::addr_t m_isa_hash_table_ptr;
437   lldb::addr_t m_relative_selector_base;
438   HashTableSignature m_hash_signature;
439   bool m_has_object_getClass;
440   bool m_has_objc_copyRealizedClassList;
441   bool m_has_objc_getRealizedClassList_trylock;
442   bool m_loaded_objc_opt;
443   std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
444   std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
445   EncodingToTypeSP m_encoding_to_type_sp;
446   std::once_flag m_no_classes_cached_warning;
447   std::once_flag m_no_expanded_cache_warning;
448   std::optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
449   uint64_t m_realized_class_generation_count;
450 };
451 
452 } // namespace lldb_private
453 
454 #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H
455