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 { |
227 | public: |
228 | (); |
229 | |
230 | size_t (); |
231 | |
232 | iVarDescriptor &(size_t idx); |
233 | |
234 | void (AppleObjCRuntimeV2 &runtime, ClassDescriptorV2 &descriptor); |
235 | |
236 | private: |
237 | bool = false; |
238 | std::vector<iVarDescriptor> ; |
239 | std::recursive_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 | |