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(&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_qual_type,
69                               const char *ivar_name) override;
70 
71   void UpdateISAToDescriptorMapIfNeeded() override;
72 
73   ClassDescriptorSP GetClassDescriptor(ValueObject &in_value) 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 
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 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