1 //===-- AppleObjCClassDescriptorV2.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_APPLEOBJCCLASSDESCRIPTORV2_H
10 #define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCCLASSDESCRIPTORV2_H
11 
12 #include <mutex>
13 
14 #include "AppleObjCRuntimeV2.h"
15 #include "lldb/lldb-enumerations.h"
16 #include "lldb/lldb-private.h"
17 
18 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
19 
20 namespace lldb_private {
21 
22 class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
23 public:
24   friend class lldb_private::AppleObjCRuntimeV2;
25 
26   ~ClassDescriptorV2() override = default;
27 
28   ConstString GetClassName() override;
29 
30   ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override;
31 
32   ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override;
33 
34   bool IsValid() override {
35     return true; // any Objective-C v2 runtime class descriptor we vend is valid
36   }
37 
38   lldb::LanguageType GetImplementationLanguage() const override;
39 
40   // a custom descriptor is used for tagged pointers
41   bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
42                             uint64_t *value_bits = nullptr,
43                             uint64_t *payload = nullptr) override {
44     return false;
45   }
46 
47   bool GetTaggedPointerInfoSigned(uint64_t *info_bits = nullptr,
48                                   int64_t *value_bits = nullptr,
49                                   uint64_t *payload = nullptr) override {
50     return false;
51   }
52 
53   uint64_t GetInstanceSize() override;
54 
55   ObjCLanguageRuntime::ObjCISA GetISA() override { return m_objc_class_ptr; }
56 
57   bool Describe(
58       std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
59       std::function<bool(const char *, const char *)> const
60           &instance_method_func,
61       std::function<bool(const char *, const char *)> const &class_method_func,
62       std::function<bool(const char *, const char *, lldb::addr_t,
63                          uint64_t)> const &ivar_func) const override;
64 
65   size_t GetNumIVars() override {
66     GetIVarInformation();
67     return m_ivars_storage.size();
68   }
69 
70   iVarDescriptor GetIVarAtIndex(size_t idx) override {
71     if (idx >= GetNumIVars())
72       return iVarDescriptor();
73     return m_ivars_storage[idx];
74   }
75 
76 protected:
77   void GetIVarInformation();
78 
79 private:
80   static const uint32_t RW_REALIZED = (1u << 31);
81 
82   struct objc_class_t {
83     ObjCLanguageRuntime::ObjCISA m_isa = 0; // The class's metaclass.
84     ObjCLanguageRuntime::ObjCISA m_superclass = 0;
85     lldb::addr_t m_cache_ptr = 0;
86     lldb::addr_t m_vtable_ptr = 0;
87     lldb::addr_t m_data_ptr = 0;
88     uint8_t m_flags = 0;
89 
90     objc_class_t() = default;
91 
92     void Clear() {
93       m_isa = 0;
94       m_superclass = 0;
95       m_cache_ptr = 0;
96       m_vtable_ptr = 0;
97       m_data_ptr = 0;
98       m_flags = 0;
99     }
100 
101     bool Read(Process *process, lldb::addr_t addr);
102   };
103 
104   struct class_ro_t {
105     uint32_t m_flags;
106     uint32_t m_instanceStart;
107     uint32_t m_instanceSize;
108     uint32_t m_reserved;
109 
110     lldb::addr_t m_ivarLayout_ptr;
111     lldb::addr_t m_name_ptr;
112     lldb::addr_t m_baseMethods_ptr;
113     lldb::addr_t m_baseProtocols_ptr;
114     lldb::addr_t m_ivars_ptr;
115 
116     lldb::addr_t m_weakIvarLayout_ptr;
117     lldb::addr_t m_baseProperties_ptr;
118 
119     std::string m_name;
120 
121     bool Read(Process *process, lldb::addr_t addr);
122   };
123 
124   struct class_rw_t {
125     uint32_t m_flags;
126     uint32_t m_version;
127 
128     lldb::addr_t m_ro_ptr;
129     union {
130       lldb::addr_t m_method_list_ptr;
131       lldb::addr_t m_method_lists_ptr;
132     };
133     lldb::addr_t m_properties_ptr;
134     lldb::addr_t m_protocols_ptr;
135 
136     ObjCLanguageRuntime::ObjCISA m_firstSubclass;
137     ObjCLanguageRuntime::ObjCISA m_nextSiblingClass;
138 
139     bool Read(Process *process, lldb::addr_t addr);
140   };
141 
142   struct method_list_t {
143     uint16_t m_entsize;
144     bool m_is_small;
145     bool m_has_direct_selector;
146     uint32_t m_count;
147     lldb::addr_t m_first_ptr;
148 
149     bool Read(Process *process, lldb::addr_t addr);
150   };
151 
152   std::optional<method_list_t>
153   GetMethodList(Process *process, lldb::addr_t method_list_ptr) const;
154 
155   struct method_t {
156     lldb::addr_t m_name_ptr;
157     lldb::addr_t m_types_ptr;
158     lldb::addr_t m_imp_ptr;
159 
160     std::string m_name;
161     std::string m_types;
162 
163     static size_t GetSize(Process *process, bool is_small) {
164       size_t field_size;
165       if (is_small)
166         field_size = 4; // uint32_t relative indirect fields
167       else
168         field_size = process->GetAddressByteSize();
169 
170       return field_size    // SEL name;
171              + field_size  // const char *types;
172              + field_size; // IMP imp;
173     }
174 
175     bool Read(Process *process, lldb::addr_t addr,
176               lldb::addr_t relative_selector_base_addr, bool is_small,
177               bool has_direct_sel);
178   };
179 
180   struct ivar_list_t {
181     uint32_t m_entsize;
182     uint32_t m_count;
183     lldb::addr_t m_first_ptr;
184 
185     bool Read(Process *process, lldb::addr_t addr);
186   };
187 
188   struct ivar_t {
189     lldb::addr_t m_offset_ptr;
190     lldb::addr_t m_name_ptr;
191     lldb::addr_t m_type_ptr;
192     uint32_t m_alignment;
193     uint32_t m_size;
194 
195     std::string m_name;
196     std::string m_type;
197 
198     static size_t GetSize(Process *process) {
199       size_t ptr_size = process->GetAddressByteSize();
200 
201       return ptr_size            // uintptr_t *offset;
202              + ptr_size          // const char *name;
203              + ptr_size          // const char *type;
204              + sizeof(uint32_t)  // uint32_t alignment;
205              + sizeof(uint32_t); // uint32_t size;
206     }
207 
208     bool Read(Process *process, lldb::addr_t addr);
209   };
210 
211   struct relative_list_entry_t {
212     uint16_t m_image_index;
213     int64_t m_list_offset;
214 
215     bool Read(Process *process, lldb::addr_t addr);
216   };
217 
218   struct relative_list_list_t {
219     uint32_t m_entsize;
220     uint32_t m_count;
221     lldb::addr_t m_first_ptr;
222 
223     bool Read(Process *process, lldb::addr_t addr);
224   };
225 
226   class iVarsStorage {
227   public:
228     iVarsStorage();
229 
230     size_t size();
231 
232     iVarDescriptor &operator[](size_t idx);
233 
234     void fill(AppleObjCRuntimeV2 &runtime, ClassDescriptorV2 &descriptor);
235 
236   private:
237     bool m_filled = false;
238     std::vector<iVarDescriptor> m_ivars;
239     std::recursive_mutex m_mutex;
240   };
241 
242   // The constructor should only be invoked by the runtime as it builds its
243   // caches
244   // or populates them.  A ClassDescriptorV2 should only ever exist in a cache.
245   ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
246                     ObjCLanguageRuntime::ObjCISA isa, const char *name)
247       : m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
248         m_ivars_storage(), m_image_to_method_lists(), m_last_version_updated() {
249   }
250 
251   bool Read_objc_class(Process *process,
252                        std::unique_ptr<objc_class_t> &objc_class) const;
253 
254   bool Read_class_row(Process *process, const objc_class_t &objc_class,
255                       std::unique_ptr<class_ro_t> &class_ro,
256                       std::unique_ptr<class_rw_t> &class_rw) const;
257 
258   bool ProcessMethodList(std::function<bool(const char *, const char *)> const
259                              &instance_method_func,
260                          method_list_t &method_list) const;
261 
262   bool ProcessRelativeMethodLists(
263       std::function<bool(const char *, const char *)> const
264           &instance_method_func,
265       lldb::addr_t relative_method_list_ptr) const;
266 
267   AppleObjCRuntimeV2
268       &m_runtime; // The runtime, so we can read information lazily.
269   lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t.  (I.e.,
270                                  // objects of this class type have this as
271                                  // their ISA)
272   ConstString m_name;            // May be NULL
273   iVarsStorage m_ivars_storage;
274 
275   mutable std::map<uint16_t, std::vector<method_list_t>>
276       m_image_to_method_lists;
277   mutable std::optional<uint64_t> m_last_version_updated;
278 };
279 
280 // tagged pointer descriptor
281 class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor {
282 public:
283   ClassDescriptorV2Tagged(ConstString class_name, uint64_t payload) {
284     m_name = class_name;
285     if (!m_name) {
286       m_valid = false;
287       return;
288     }
289     m_valid = true;
290     m_payload = payload;
291     m_info_bits = (m_payload & 0xF0ULL) >> 4;
292     m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8;
293   }
294 
295   ClassDescriptorV2Tagged(
296       ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp,
297       uint64_t u_payload, int64_t s_payload) {
298     if (!actual_class_sp) {
299       m_valid = false;
300       return;
301     }
302     m_name = actual_class_sp->GetClassName();
303     if (!m_name) {
304       m_valid = false;
305       return;
306     }
307     m_valid = true;
308     m_payload = u_payload;
309     m_info_bits = (m_payload & 0x0FULL);
310     m_value_bits = (m_payload & ~0x0FULL) >> 4;
311     m_value_bits_signed = (s_payload & ~0x0FLL) >> 4;
312   }
313 
314   ~ClassDescriptorV2Tagged() override = default;
315 
316   ConstString GetClassName() override { return m_name; }
317 
318   ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override {
319     // tagged pointers can represent a class that has a superclass, but since
320     // that information is not
321     // stored in the object itself, we would have to query the runtime to
322     // discover the hierarchy
323     // for the time being, we skip this step in the interest of static discovery
324     return ObjCLanguageRuntime::ClassDescriptorSP();
325   }
326 
327   ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override {
328     return ObjCLanguageRuntime::ClassDescriptorSP();
329   }
330 
331   bool IsValid() override { return m_valid; }
332 
333   bool IsKVO() override {
334     return false; // tagged pointers are not KVO'ed
335   }
336 
337   bool IsCFType() override {
338     return false; // tagged pointers are not CF objects
339   }
340 
341   bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
342                             uint64_t *value_bits = nullptr,
343                             uint64_t *payload = nullptr) override {
344     if (info_bits)
345       *info_bits = GetInfoBits();
346     if (value_bits)
347       *value_bits = GetValueBits();
348     if (payload)
349       *payload = GetPayload();
350     return true;
351   }
352 
353   bool GetTaggedPointerInfoSigned(uint64_t *info_bits = nullptr,
354                                   int64_t *value_bits = nullptr,
355                                   uint64_t *payload = nullptr) override {
356     if (info_bits)
357       *info_bits = GetInfoBits();
358     if (value_bits)
359       *value_bits = GetValueBitsSigned();
360     if (payload)
361       *payload = GetPayload();
362     return true;
363   }
364 
365   uint64_t GetInstanceSize() override {
366     return (IsValid() ? m_pointer_size : 0);
367   }
368 
369   ObjCLanguageRuntime::ObjCISA GetISA() override {
370     return 0; // tagged pointers have no ISA
371   }
372 
373   // these calls are not part of any formal tagged pointers specification
374   virtual uint64_t GetValueBits() { return (IsValid() ? m_value_bits : 0); }
375 
376   virtual int64_t GetValueBitsSigned() {
377     return (IsValid() ? m_value_bits_signed : 0);
378   }
379 
380   virtual uint64_t GetInfoBits() { return (IsValid() ? m_info_bits : 0); }
381 
382   virtual uint64_t GetPayload() { return (IsValid() ? m_payload : 0); }
383 
384 private:
385   ConstString m_name;
386   uint8_t m_pointer_size = 0;
387   bool m_valid = false;
388   uint64_t m_info_bits = 0;
389   uint64_t m_value_bits = 0;
390   int64_t m_value_bits_signed = 0;
391   uint64_t m_payload = 0;
392 };
393 
394 } // namespace lldb_private
395 
396 #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCCLASSDESCRIPTORV2_H
397