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 or objc_copyRealizedClassList. The latter is
317   /// preferred because it includes lazily named classes, but it's not always
318   /// available or safe to call.
319   ///
320   /// We potentially need both for the same process, because we may need to use
321   /// gdb_objc_realized_classes until dyld is initialized and then switch over
322   /// to objc_copyRealizedClassList for lazily named classes.
323   class DynamicClassInfoExtractor : public ClassInfoExtractor {
324   public:
325     DynamicClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
326         : ClassInfoExtractor(runtime) {}
327 
328     DescriptorMapUpdateResult
329     UpdateISAToDescriptorMap(RemoteNXMapTable &hash_table);
330 
331   private:
332     enum Helper { gdb_objc_realized_classes, objc_copyRealizedClassList };
333 
334     /// Compute which helper to use. Prefer objc_copyRealizedClassList if it's
335     /// available and it's safe to call (i.e. dyld is fully initialized). Use
336     /// gdb_objc_realized_classes otherwise.
337     Helper ComputeHelper() const;
338 
339     UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx,
340                                                  Helper helper);
341     lldb::addr_t &GetClassInfoArgs(Helper helper);
342 
343     std::unique_ptr<UtilityFunction>
344     GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, std::string code,
345                                     std::string name);
346 
347     /// Helper to read class info using the gdb_objc_realized_classes.
348     struct gdb_objc_realized_classes_helper {
349       std::unique_ptr<UtilityFunction> utility_function;
350       lldb::addr_t args = LLDB_INVALID_ADDRESS;
351     };
352 
353     /// Helper to read class info using objc_copyRealizedClassList.
354     struct objc_copyRealizedClassList_helper {
355       std::unique_ptr<UtilityFunction> utility_function;
356       lldb::addr_t args = LLDB_INVALID_ADDRESS;
357     };
358 
359     gdb_objc_realized_classes_helper m_gdb_objc_realized_classes_helper;
360     objc_copyRealizedClassList_helper m_objc_copyRealizedClassList_helper;
361   };
362 
363   /// Abstraction to read the Objective-C class info from the shared cache.
364   class SharedCacheClassInfoExtractor : public ClassInfoExtractor {
365   public:
366     SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
367         : ClassInfoExtractor(runtime) {}
368 
369     DescriptorMapUpdateResult UpdateISAToDescriptorMap();
370 
371   private:
372     UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx);
373 
374     std::unique_ptr<UtilityFunction>
375     GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx);
376 
377     std::unique_ptr<UtilityFunction> m_utility_function;
378     lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
379   };
380 
381   AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
382 
383   ObjCISA GetPointerISA(ObjCISA isa);
384 
385   lldb::addr_t GetISAHashTablePointer();
386 
387   /// Update the generation count of realized classes. This is not an exact
388   /// count but rather a value that is incremented when new classes are realized
389   /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will
390   /// change when lazily named classes get realized.
391   bool RealizedClassGenerationCountChanged();
392 
393   uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
394                                uint32_t num_class_infos);
395 
396   enum class SharedCacheWarningReason {
397     eExpressionExecutionFailure,
398     eNotEnoughClassesRead
399   };
400 
401   void WarnIfNoClassesCached(SharedCacheWarningReason reason);
402   void WarnIfNoExpandedSharedCache();
403 
404   lldb::addr_t GetSharedCacheReadOnlyAddress();
405   lldb::addr_t GetSharedCacheBaseAddress();
406 
407   bool GetCFBooleanValuesIfNeeded();
408 
409   bool HasSymbol(ConstString Name);
410 
411   NonPointerISACache *GetNonPointerIsaCache() {
412     if (!m_non_pointer_isa_cache_up)
413       m_non_pointer_isa_cache_up.reset(
414           NonPointerISACache::CreateInstance(*this, m_objc_module_sp));
415     return m_non_pointer_isa_cache_up.get();
416   }
417 
418   friend class ClassDescriptorV2;
419 
420   lldb::ModuleSP m_objc_module_sp;
421 
422   DynamicClassInfoExtractor m_dynamic_class_info_extractor;
423   SharedCacheClassInfoExtractor m_shared_cache_class_info_extractor;
424 
425   std::unique_ptr<DeclVendor> m_decl_vendor_up;
426   lldb::addr_t m_tagged_pointer_obfuscator;
427   lldb::addr_t m_isa_hash_table_ptr;
428   lldb::addr_t m_relative_selector_base;
429   HashTableSignature m_hash_signature;
430   bool m_has_object_getClass;
431   bool m_has_objc_copyRealizedClassList;
432   bool m_loaded_objc_opt;
433   std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
434   std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
435   EncodingToTypeSP m_encoding_to_type_sp;
436   bool m_noclasses_warning_emitted;
437   llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
438   uint64_t m_realized_class_generation_count;
439 };
440 
441 } // namespace lldb_private
442 
443 #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H
444