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 lldb_private::ConstString GetPluginNameStatic();
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   ConstString GetPluginName() override;
58 
59   uint32_t GetPluginVersion() override;
60 
61   ObjCRuntimeVersions GetRuntimeVersion() const override {
62     return ObjCRuntimeVersions::eAppleObjC_V2;
63   }
64 
65   size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
66                               const char *ivar_name) override;
67 
68   void UpdateISAToDescriptorMapIfNeeded() override;
69 
70   ClassDescriptorSP GetClassDescriptor(ValueObject &in_value) override;
71 
72   ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override;
73 
74   DeclVendor *GetDeclVendor() override;
75 
76   lldb::addr_t LookupRuntimeSymbol(ConstString name) override;
77 
78   EncodingToTypeSP GetEncodingToType() override;
79 
80   bool IsTaggedPointer(lldb::addr_t ptr) override;
81 
82   TaggedPointerVendor *GetTaggedPointerVendor() override {
83     return m_tagged_pointer_vendor_up.get();
84   }
85 
86   lldb::addr_t GetTaggedPointerObfuscator();
87 
88   /// Returns the base address for relative method list selector strings.
89   lldb::addr_t GetRelativeSelectorBaseAddr() {
90     return m_relative_selector_base;
91   }
92 
93   void SetRelativeSelectorBaseAddr(lldb::addr_t relative_selector_base) {
94     m_relative_selector_base = relative_selector_base;
95   }
96 
97   void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
98                                     lldb::addr_t &cf_false) override;
99 
100   // none of these are valid ISAs - we use them to infer the type
101   // of tagged pointers - if we have something meaningful to say
102   // we report an actual type - otherwise, we just say tagged
103   // there is no connection between the values here and the tagged pointers map
104   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA = 1;
105   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSAtom = 2;
106   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSNumber = 3;
107   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDateTS = 4;
108   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSManagedObject =
109       5;
110   static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDate = 6;
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 
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:
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     uint32_t m_num_found;
292 
293     DescriptorMapUpdateResult(bool ran, uint32_t found) {
294       m_update_ran = ran;
295       m_num_found = found;
296     }
297 
298     static DescriptorMapUpdateResult Fail() { return {false, 0}; }
299 
300     static DescriptorMapUpdateResult Success(uint32_t found) {
301       return {true, found};
302     }
303   };
304 
305   /// Abstraction to read the Objective-C class info.
306   class ClassInfoExtractor {
307   public:
308     ClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {}
309     std::mutex &GetMutex() { return m_mutex; }
310 
311   protected:
312     /// The lifetime of this object is tied to that of the runtime.
313     AppleObjCRuntimeV2 &m_runtime;
314     std::mutex m_mutex;
315   };
316 
317   /// We can read the class info from the Objective-C runtime using
318   /// gdb_objc_realized_classes or objc_copyRealizedClassList. The latter is
319   /// preferred because it includes lazily named classes, but it's not always
320   /// available or safe to call.
321   ///
322   /// We potentially need both for the same process, because we may need to use
323   /// gdb_objc_realized_classes until dyld is initialized and then switch over
324   /// to objc_copyRealizedClassList 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 { gdb_objc_realized_classes, objc_copyRealizedClassList };
335 
336     /// Compute which helper to use. Prefer objc_copyRealizedClassList if it's
337     /// available and it's safe to call (i.e. dyld is fully initialized). Use
338     /// gdb_objc_realized_classes otherwise.
339     Helper ComputeHelper() const;
340 
341     UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx,
342                                                  Helper helper);
343     lldb::addr_t &GetClassInfoArgs(Helper helper);
344 
345     std::unique_ptr<UtilityFunction>
346     GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, std::string code,
347                                     std::string name);
348 
349     /// Helper to read class info using the gdb_objc_realized_classes.
350     struct gdb_objc_realized_classes_helper {
351       std::unique_ptr<UtilityFunction> utility_function;
352       lldb::addr_t args = LLDB_INVALID_ADDRESS;
353     };
354 
355     /// Helper to read class info using objc_copyRealizedClassList.
356     struct objc_copyRealizedClassList_helper {
357       std::unique_ptr<UtilityFunction> utility_function;
358       lldb::addr_t args = LLDB_INVALID_ADDRESS;
359     };
360 
361     gdb_objc_realized_classes_helper m_gdb_objc_realized_classes_helper;
362     objc_copyRealizedClassList_helper m_objc_copyRealizedClassList_helper;
363   };
364 
365   /// Abstraction to read the Objective-C class info from the shared cache.
366   class SharedCacheClassInfoExtractor : public ClassInfoExtractor {
367   public:
368     SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
369         : ClassInfoExtractor(runtime) {}
370 
371     DescriptorMapUpdateResult UpdateISAToDescriptorMap();
372 
373   private:
374     UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx);
375 
376     std::unique_ptr<UtilityFunction>
377     GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx);
378 
379     std::unique_ptr<UtilityFunction> m_utility_function;
380     lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
381   };
382 
383   AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
384 
385   ObjCISA GetPointerISA(ObjCISA isa);
386 
387   lldb::addr_t GetISAHashTablePointer();
388 
389   /// Update the generation count of realized classes. This is not an exact
390   /// count but rather a value that is incremented when new classes are realized
391   /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will
392   /// change when lazily named classes get realized.
393   bool RealizedClassGenerationCountChanged();
394 
395   uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
396                                uint32_t num_class_infos);
397 
398   enum class SharedCacheWarningReason {
399     eExpressionExecutionFailure,
400     eNotEnoughClassesRead
401   };
402 
403   void WarnIfNoClassesCached(SharedCacheWarningReason reason);
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_loaded_objc_opt;
434   std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
435   std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
436   EncodingToTypeSP m_encoding_to_type_sp;
437   bool m_noclasses_warning_emitted;
438   llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
439   uint64_t m_realized_class_generation_count;
440 };
441 
442 } // namespace lldb_private
443 
444 #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H
445