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