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
20namespace lldb_private {
21
22class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
23public:
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
76protected:
77 void GetIVarInformation();
78
79private:
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
281class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor {
282public:
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
384private:
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

source code of lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h