/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
Line | Count | Source (jump to first uncovered line) |
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 | 70.1M | ~ClassDescriptorV2() override = default; |
27 | | |
28 | | ConstString GetClassName() override; |
29 | | |
30 | | ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override; |
31 | | |
32 | | ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override; |
33 | | |
34 | 2.45k | bool IsValid() override { |
35 | 2.45k | return true; // any Objective-C v2 runtime class descriptor we vend is valid |
36 | 2.45k | } |
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 | 24 | uint64_t *payload = nullptr) override { |
44 | 24 | return false; |
45 | 24 | } |
46 | | |
47 | | bool GetTaggedPointerInfoSigned(uint64_t *info_bits = nullptr, |
48 | | int64_t *value_bits = nullptr, |
49 | 104 | uint64_t *payload = nullptr) override { |
50 | 104 | return false; |
51 | 104 | } |
52 | | |
53 | | uint64_t GetInstanceSize() override; |
54 | | |
55 | 0 | 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 | 316 | size_t GetNumIVars() override { |
66 | 316 | GetIVarInformation(); |
67 | 316 | return m_ivars_storage.size(); |
68 | 316 | } |
69 | | |
70 | 125 | iVarDescriptor GetIVarAtIndex(size_t idx) override { |
71 | 125 | if (idx >= GetNumIVars()) |
72 | 0 | return iVarDescriptor(); |
73 | 125 | return m_ivars_storage[idx]; |
74 | 125 | } |
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 | 2.60k | objc_class_t() = default; |
91 | | |
92 | 0 | void Clear() { |
93 | 0 | m_isa = 0; |
94 | 0 | m_superclass = 0; |
95 | 0 | m_cache_ptr = 0; |
96 | 0 | m_vtable_ptr = 0; |
97 | 0 | m_data_ptr = 0; |
98 | 0 | m_flags = 0; |
99 | 0 | } |
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 | 17.8k | static size_t GetSize(Process *process, bool is_small) { |
164 | 17.8k | size_t field_size; |
165 | 17.8k | if (is_small) |
166 | 0 | field_size = 4; // uint32_t relative indirect fields |
167 | 17.8k | else |
168 | 17.8k | field_size = process->GetAddressByteSize(); |
169 | | |
170 | 17.8k | return field_size // SEL name; |
171 | 17.8k | + field_size // const char *types; |
172 | 17.8k | + field_size; // IMP imp; |
173 | 17.8k | } |
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 | 556 | static size_t GetSize(Process *process) { |
199 | 556 | size_t ptr_size = process->GetAddressByteSize(); |
200 | | |
201 | 556 | return ptr_size // uintptr_t *offset; |
202 | 556 | + ptr_size // const char *name; |
203 | 556 | + ptr_size // const char *type; |
204 | 556 | + sizeof(uint32_t) // uint32_t alignment; |
205 | 556 | + sizeof(uint32_t); // uint32_t size; |
206 | 556 | } |
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 | 70.1M | m_ivars_storage(), m_image_to_method_lists(), m_last_version_updated() { |
249 | 70.1M | } |
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 | 0 | ClassDescriptorV2Tagged(ConstString class_name, uint64_t payload) { |
284 | 0 | m_name = class_name; |
285 | 0 | if (!m_name) { |
286 | 0 | m_valid = false; |
287 | 0 | return; |
288 | 0 | } |
289 | 0 | m_valid = true; |
290 | 0 | m_payload = payload; |
291 | 0 | m_info_bits = (m_payload & 0xF0ULL) >> 4; |
292 | 0 | m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8; |
293 | 0 | } |
294 | | |
295 | | ClassDescriptorV2Tagged( |
296 | | ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp, |
297 | 1.11k | uint64_t u_payload, int64_t s_payload) { |
298 | 1.11k | if (!actual_class_sp) { |
299 | 0 | m_valid = false; |
300 | 0 | return; |
301 | 0 | } |
302 | 1.11k | m_name = actual_class_sp->GetClassName(); |
303 | 1.11k | if (!m_name) { |
304 | 0 | m_valid = false; |
305 | 0 | return; |
306 | 0 | } |
307 | 1.11k | m_valid = true; |
308 | 1.11k | m_payload = u_payload; |
309 | 1.11k | m_info_bits = (m_payload & 0x0FULL); |
310 | 1.11k | m_value_bits = (m_payload & ~0x0FULL) >> 4; |
311 | 1.11k | m_value_bits_signed = (s_payload & ~0x0FLL) >> 4; |
312 | 1.11k | } |
313 | | |
314 | 1.11k | ~ClassDescriptorV2Tagged() override = default; |
315 | | |
316 | 888 | ConstString GetClassName() override { return m_name; } |
317 | | |
318 | 24 | 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 | 24 | return ObjCLanguageRuntime::ClassDescriptorSP(); |
325 | 24 | } |
326 | | |
327 | 0 | ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override { |
328 | 0 | return ObjCLanguageRuntime::ClassDescriptorSP(); |
329 | 0 | } |
330 | | |
331 | 1.22k | bool IsValid() override { return m_valid; } |
332 | | |
333 | 358 | bool IsKVO() override { |
334 | 358 | return false; // tagged pointers are not KVO'ed |
335 | 358 | } |
336 | | |
337 | 0 | bool IsCFType() override { |
338 | 0 | return false; // tagged pointers are not CF objects |
339 | 0 | } |
340 | | |
341 | | bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr, |
342 | | uint64_t *value_bits = nullptr, |
343 | 148 | uint64_t *payload = nullptr) override { |
344 | 148 | if (info_bits) |
345 | 128 | *info_bits = GetInfoBits(); |
346 | 148 | if (value_bits) |
347 | 128 | *value_bits = GetValueBits(); |
348 | 148 | if (payload) |
349 | 16 | *payload = GetPayload(); |
350 | 148 | return true; |
351 | 148 | } |
352 | | |
353 | | bool GetTaggedPointerInfoSigned(uint64_t *info_bits = nullptr, |
354 | | int64_t *value_bits = nullptr, |
355 | 274 | uint64_t *payload = nullptr) override { |
356 | 274 | if (info_bits) |
357 | 274 | *info_bits = GetInfoBits(); |
358 | 274 | if (value_bits) |
359 | 274 | *value_bits = GetValueBitsSigned(); |
360 | 274 | if (payload) |
361 | 0 | *payload = GetPayload(); |
362 | 274 | return true; |
363 | 274 | } |
364 | | |
365 | 0 | uint64_t GetInstanceSize() override { |
366 | 0 | return (IsValid() ? m_pointer_size : 0); |
367 | 0 | } |
368 | | |
369 | 0 | ObjCLanguageRuntime::ObjCISA GetISA() override { |
370 | 0 | return 0; // tagged pointers have no ISA |
371 | 0 | } |
372 | | |
373 | | // these calls are not part of any formal tagged pointers specification |
374 | 128 | virtual uint64_t GetValueBits() { return (IsValid() ? m_value_bits : 00 ); } |
375 | | |
376 | 274 | virtual int64_t GetValueBitsSigned() { |
377 | 274 | return (IsValid() ? m_value_bits_signed : 00 ); |
378 | 274 | } |
379 | | |
380 | 402 | virtual uint64_t GetInfoBits() { return (IsValid() ? m_info_bits : 00 ); } |
381 | | |
382 | 16 | virtual uint64_t GetPayload() { return (IsValid() ? m_payload : 00 ); } |
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 |