/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- AppleObjCClassDescriptorV2.cpp ------------------------------------===// |
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 | | #include "AppleObjCClassDescriptorV2.h" |
10 | | |
11 | | #include "lldb/Expression/FunctionCaller.h" |
12 | | #include "lldb/Target/ABI.h" |
13 | | #include "lldb/Target/Language.h" |
14 | | #include "lldb/Utility/LLDBLog.h" |
15 | | #include "lldb/Utility/Log.h" |
16 | | #include "lldb/lldb-enumerations.h" |
17 | | |
18 | | using namespace lldb; |
19 | | using namespace lldb_private; |
20 | | |
21 | | bool ClassDescriptorV2::Read_objc_class( |
22 | 2.99k | Process *process, std::unique_ptr<objc_class_t> &objc_class) const { |
23 | 2.99k | objc_class = std::make_unique<objc_class_t>(); |
24 | | |
25 | 2.99k | bool ret = objc_class->Read(process, m_objc_class_ptr); |
26 | | |
27 | 2.99k | if (!ret) |
28 | 0 | objc_class.reset(); |
29 | | |
30 | 2.99k | return ret; |
31 | 2.99k | } |
32 | | |
33 | 2.99k | static lldb::addr_t GetClassDataMask(Process *process) { |
34 | 2.99k | switch (process->GetAddressByteSize()) { |
35 | 0 | case 4: |
36 | 0 | return 0xfffffffcUL; |
37 | 2.99k | case 8: |
38 | 2.99k | return 0x00007ffffffffff8UL; |
39 | 0 | default: |
40 | 0 | break; |
41 | 2.99k | } |
42 | | |
43 | 0 | return LLDB_INVALID_ADDRESS; |
44 | 2.99k | } |
45 | | |
46 | | bool ClassDescriptorV2::objc_class_t::Read(Process *process, |
47 | 2.99k | lldb::addr_t addr) { |
48 | 2.99k | size_t ptr_size = process->GetAddressByteSize(); |
49 | | |
50 | 2.99k | size_t objc_class_size = ptr_size // uintptr_t isa; |
51 | 2.99k | + ptr_size // Class superclass; |
52 | 2.99k | + ptr_size // void *cache; |
53 | 2.99k | + ptr_size // IMP *vtable; |
54 | 2.99k | + ptr_size; // uintptr_t data_NEVER_USE; |
55 | | |
56 | 2.99k | DataBufferHeap objc_class_buf(objc_class_size, '\0'); |
57 | 2.99k | Status error; |
58 | | |
59 | 2.99k | process->ReadMemory(addr, objc_class_buf.GetBytes(), objc_class_size, error); |
60 | 2.99k | if (error.Fail()) { |
61 | 0 | return false; |
62 | 0 | } |
63 | | |
64 | 2.99k | DataExtractor extractor(objc_class_buf.GetBytes(), objc_class_size, |
65 | 2.99k | process->GetByteOrder(), |
66 | 2.99k | process->GetAddressByteSize()); |
67 | | |
68 | 2.99k | lldb::offset_t cursor = 0; |
69 | | |
70 | 2.99k | m_isa = extractor.GetAddress_unchecked(&cursor); // uintptr_t isa; |
71 | 2.99k | m_superclass = extractor.GetAddress_unchecked(&cursor); // Class superclass; |
72 | 2.99k | m_cache_ptr = extractor.GetAddress_unchecked(&cursor); // void *cache; |
73 | 2.99k | m_vtable_ptr = extractor.GetAddress_unchecked(&cursor); // IMP *vtable; |
74 | 2.99k | lldb::addr_t data_NEVER_USE = |
75 | 2.99k | extractor.GetAddress_unchecked(&cursor); // uintptr_t data_NEVER_USE; |
76 | | |
77 | 2.99k | m_flags = (uint8_t)(data_NEVER_USE & (lldb::addr_t)3); |
78 | 2.99k | m_data_ptr = data_NEVER_USE & GetClassDataMask(process); |
79 | | |
80 | 2.99k | if (ABISP abi_sp = process->GetABI()) { |
81 | 2.99k | m_isa = abi_sp->FixCodeAddress(m_isa); |
82 | 2.99k | m_superclass = abi_sp->FixCodeAddress(m_superclass); |
83 | 2.99k | m_data_ptr = abi_sp->FixCodeAddress(m_data_ptr); |
84 | 2.99k | } |
85 | 2.99k | return true; |
86 | 2.99k | } |
87 | | |
88 | 1.51k | bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) { |
89 | 1.51k | size_t ptr_size = process->GetAddressByteSize(); |
90 | | |
91 | 1.51k | size_t size = sizeof(uint32_t) // uint32_t flags; |
92 | 1.51k | + sizeof(uint32_t) // uint32_t version; |
93 | 1.51k | + ptr_size // const class_ro_t *ro; |
94 | 1.51k | + ptr_size // union { method_list_t **method_lists; |
95 | | // method_list_t *method_list; }; |
96 | 1.51k | + ptr_size // struct chained_property_list *properties; |
97 | 1.51k | + ptr_size // const protocol_list_t **protocols; |
98 | 1.51k | + ptr_size // Class firstSubclass; |
99 | 1.51k | + ptr_size; // Class nextSiblingClass; |
100 | | |
101 | 1.51k | DataBufferHeap buffer(size, '\0'); |
102 | 1.51k | Status error; |
103 | | |
104 | 1.51k | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
105 | 1.51k | if (error.Fail()) { |
106 | 0 | return false; |
107 | 0 | } |
108 | | |
109 | 1.51k | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
110 | 1.51k | process->GetAddressByteSize()); |
111 | | |
112 | 1.51k | lldb::offset_t cursor = 0; |
113 | | |
114 | 1.51k | m_flags = extractor.GetU32_unchecked(&cursor); |
115 | 1.51k | m_version = extractor.GetU32_unchecked(&cursor); |
116 | 1.51k | m_ro_ptr = extractor.GetAddress_unchecked(&cursor); |
117 | 1.51k | if (ABISP abi_sp = process->GetABI()) |
118 | 1.51k | m_ro_ptr = abi_sp->FixCodeAddress(m_ro_ptr); |
119 | 1.51k | m_method_list_ptr = extractor.GetAddress_unchecked(&cursor); |
120 | 1.51k | m_properties_ptr = extractor.GetAddress_unchecked(&cursor); |
121 | 1.51k | m_firstSubclass = extractor.GetAddress_unchecked(&cursor); |
122 | 1.51k | m_nextSiblingClass = extractor.GetAddress_unchecked(&cursor); |
123 | | |
124 | 1.51k | if (m_ro_ptr & 1) { |
125 | 538 | DataBufferHeap buffer(ptr_size, '\0'); |
126 | 538 | process->ReadMemory(m_ro_ptr ^ 1, buffer.GetBytes(), ptr_size, error); |
127 | 538 | if (error.Fail()) |
128 | 0 | return false; |
129 | 538 | cursor = 0; |
130 | 538 | DataExtractor extractor(buffer.GetBytes(), ptr_size, |
131 | 538 | process->GetByteOrder(), |
132 | 538 | process->GetAddressByteSize()); |
133 | 538 | m_ro_ptr = extractor.GetAddress_unchecked(&cursor); |
134 | 538 | if (ABISP abi_sp = process->GetABI()) |
135 | 538 | m_ro_ptr = abi_sp->FixCodeAddress(m_ro_ptr); |
136 | 538 | } |
137 | | |
138 | 1.51k | return true; |
139 | 1.51k | } |
140 | | |
141 | 1.53k | bool ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) { |
142 | 1.53k | size_t ptr_size = process->GetAddressByteSize(); |
143 | | |
144 | 1.53k | size_t size = sizeof(uint32_t) // uint32_t flags; |
145 | 1.53k | + sizeof(uint32_t) // uint32_t instanceStart; |
146 | 1.53k | + sizeof(uint32_t) // uint32_t instanceSize; |
147 | 1.53k | + (ptr_size == 8 ? sizeof(uint32_t) |
148 | 1.53k | : 00 ) // uint32_t reserved; // __LP64__ only |
149 | 1.53k | + ptr_size // const uint8_t *ivarLayout; |
150 | 1.53k | + ptr_size // const char *name; |
151 | 1.53k | + ptr_size // const method_list_t *baseMethods; |
152 | 1.53k | + ptr_size // const protocol_list_t *baseProtocols; |
153 | 1.53k | + ptr_size // const ivar_list_t *ivars; |
154 | 1.53k | + ptr_size // const uint8_t *weakIvarLayout; |
155 | 1.53k | + ptr_size; // const property_list_t *baseProperties; |
156 | | |
157 | 1.53k | DataBufferHeap buffer(size, '\0'); |
158 | 1.53k | Status error; |
159 | | |
160 | 1.53k | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
161 | 1.53k | if (error.Fail()) { |
162 | 0 | return false; |
163 | 0 | } |
164 | | |
165 | 1.53k | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
166 | 1.53k | process->GetAddressByteSize()); |
167 | | |
168 | 1.53k | lldb::offset_t cursor = 0; |
169 | | |
170 | 1.53k | m_flags = extractor.GetU32_unchecked(&cursor); |
171 | 1.53k | m_instanceStart = extractor.GetU32_unchecked(&cursor); |
172 | 1.53k | m_instanceSize = extractor.GetU32_unchecked(&cursor); |
173 | 1.53k | if (ptr_size == 8) |
174 | 1.53k | m_reserved = extractor.GetU32_unchecked(&cursor); |
175 | 0 | else |
176 | 0 | m_reserved = 0; |
177 | 1.53k | m_ivarLayout_ptr = extractor.GetAddress_unchecked(&cursor); |
178 | 1.53k | m_name_ptr = extractor.GetAddress_unchecked(&cursor); |
179 | 1.53k | m_baseMethods_ptr = extractor.GetAddress_unchecked(&cursor); |
180 | 1.53k | m_baseProtocols_ptr = extractor.GetAddress_unchecked(&cursor); |
181 | 1.53k | m_ivars_ptr = extractor.GetAddress_unchecked(&cursor); |
182 | 1.53k | m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor); |
183 | 1.53k | m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor); |
184 | | |
185 | 1.53k | DataBufferHeap name_buf(1024, '\0'); |
186 | | |
187 | 1.53k | process->ReadCStringFromMemory(m_name_ptr, (char *)name_buf.GetBytes(), |
188 | 1.53k | name_buf.GetByteSize(), error); |
189 | | |
190 | 1.53k | if (error.Fail()) { |
191 | 0 | return false; |
192 | 0 | } |
193 | | |
194 | 1.53k | m_name.assign((char *)name_buf.GetBytes()); |
195 | | |
196 | 1.53k | return true; |
197 | 1.53k | } |
198 | | |
199 | | bool ClassDescriptorV2::Read_class_row( |
200 | | Process *process, const objc_class_t &objc_class, |
201 | | std::unique_ptr<class_ro_t> &class_ro, |
202 | 1.53k | std::unique_ptr<class_rw_t> &class_rw) const { |
203 | 1.53k | class_ro.reset(); |
204 | 1.53k | class_rw.reset(); |
205 | | |
206 | 1.53k | Status error; |
207 | 1.53k | uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory( |
208 | 1.53k | objc_class.m_data_ptr, sizeof(uint32_t), 0, error); |
209 | 1.53k | if (!error.Success()) |
210 | 0 | return false; |
211 | | |
212 | 1.53k | if (class_row_t_flags & RW_REALIZED) { |
213 | 1.51k | class_rw = std::make_unique<class_rw_t>(); |
214 | | |
215 | 1.51k | if (!class_rw->Read(process, objc_class.m_data_ptr)) { |
216 | 0 | class_rw.reset(); |
217 | 0 | return false; |
218 | 0 | } |
219 | | |
220 | 1.51k | class_ro = std::make_unique<class_ro_t>(); |
221 | | |
222 | 1.51k | if (!class_ro->Read(process, class_rw->m_ro_ptr)) { |
223 | 0 | class_rw.reset(); |
224 | 0 | class_ro.reset(); |
225 | 0 | return false; |
226 | 0 | } |
227 | 1.51k | } else { |
228 | 26 | class_ro = std::make_unique<class_ro_t>(); |
229 | | |
230 | 26 | if (!class_ro->Read(process, objc_class.m_data_ptr)) { |
231 | 0 | class_ro.reset(); |
232 | 0 | return false; |
233 | 0 | } |
234 | 26 | } |
235 | | |
236 | 1.53k | return true; |
237 | 1.53k | } |
238 | | |
239 | | bool ClassDescriptorV2::method_list_t::Read(Process *process, |
240 | 636 | lldb::addr_t addr) { |
241 | 636 | size_t size = sizeof(uint32_t) // uint32_t entsize_NEVER_USE; |
242 | 636 | + sizeof(uint32_t); // uint32_t count; |
243 | | |
244 | 636 | DataBufferHeap buffer(size, '\0'); |
245 | 636 | Status error; |
246 | | |
247 | 636 | if (ABISP abi_sp = process->GetABI()) |
248 | 636 | addr = abi_sp->FixCodeAddress(addr); |
249 | 636 | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
250 | 636 | if (error.Fail()) { |
251 | 52 | return false; |
252 | 52 | } |
253 | | |
254 | 584 | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
255 | 584 | process->GetAddressByteSize()); |
256 | | |
257 | 584 | lldb::offset_t cursor = 0; |
258 | | |
259 | 584 | uint32_t entsize = extractor.GetU32_unchecked(&cursor); |
260 | 584 | m_is_small = (entsize & 0x80000000) != 0; |
261 | 584 | m_has_direct_selector = (entsize & 0x40000000) != 0; |
262 | 584 | m_entsize = entsize & 0xfffc; |
263 | 584 | m_count = extractor.GetU32_unchecked(&cursor); |
264 | 584 | m_first_ptr = addr + cursor; |
265 | | |
266 | 584 | return true; |
267 | 636 | } |
268 | | |
269 | | bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr, |
270 | | lldb::addr_t relative_selector_base_addr, |
271 | 20.4k | bool is_small, bool has_direct_sel) { |
272 | 20.4k | size_t ptr_size = process->GetAddressByteSize(); |
273 | 20.4k | size_t size = GetSize(process, is_small); |
274 | | |
275 | 20.4k | DataBufferHeap buffer(size, '\0'); |
276 | 20.4k | Status error; |
277 | | |
278 | 20.4k | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
279 | 20.4k | if (error.Fail()) { |
280 | 0 | return false; |
281 | 0 | } |
282 | | |
283 | 20.4k | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
284 | 20.4k | ptr_size); |
285 | 20.4k | lldb::offset_t cursor = 0; |
286 | | |
287 | 20.4k | if (is_small) { |
288 | 0 | uint32_t nameref_offset = extractor.GetU32_unchecked(&cursor); |
289 | 0 | uint32_t types_offset = extractor.GetU32_unchecked(&cursor); |
290 | 0 | uint32_t imp_offset = extractor.GetU32_unchecked(&cursor); |
291 | |
|
292 | 0 | m_name_ptr = addr + nameref_offset; |
293 | |
|
294 | 0 | if (!has_direct_sel) { |
295 | | // The SEL offset points to a SELRef. We need to dereference twice. |
296 | 0 | m_name_ptr = process->ReadUnsignedIntegerFromMemory(m_name_ptr, ptr_size, |
297 | 0 | 0, error); |
298 | 0 | if (!error.Success()) |
299 | 0 | return false; |
300 | 0 | } else if (relative_selector_base_addr != LLDB_INVALID_ADDRESS) { |
301 | 0 | m_name_ptr = relative_selector_base_addr + nameref_offset; |
302 | 0 | } |
303 | 0 | m_types_ptr = addr + 4 + types_offset; |
304 | 0 | m_imp_ptr = addr + 8 + imp_offset; |
305 | 20.4k | } else { |
306 | 20.4k | m_name_ptr = extractor.GetAddress_unchecked(&cursor); |
307 | 20.4k | m_types_ptr = extractor.GetAddress_unchecked(&cursor); |
308 | 20.4k | m_imp_ptr = extractor.GetAddress_unchecked(&cursor); |
309 | 20.4k | } |
310 | | |
311 | 20.4k | process->ReadCStringFromMemory(m_name_ptr, m_name, error); |
312 | 20.4k | if (error.Fail()) { |
313 | 0 | return false; |
314 | 0 | } |
315 | | |
316 | 20.4k | process->ReadCStringFromMemory(m_types_ptr, m_types, error); |
317 | 20.4k | return !error.Fail(); |
318 | 20.4k | } |
319 | | |
320 | 241 | bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) { |
321 | 241 | size_t size = sizeof(uint32_t) // uint32_t entsize; |
322 | 241 | + sizeof(uint32_t); // uint32_t count; |
323 | | |
324 | 241 | DataBufferHeap buffer(size, '\0'); |
325 | 241 | Status error; |
326 | | |
327 | 241 | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
328 | 241 | if (error.Fail()) { |
329 | 0 | return false; |
330 | 0 | } |
331 | | |
332 | 241 | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
333 | 241 | process->GetAddressByteSize()); |
334 | | |
335 | 241 | lldb::offset_t cursor = 0; |
336 | | |
337 | 241 | m_entsize = extractor.GetU32_unchecked(&cursor); |
338 | 241 | m_count = extractor.GetU32_unchecked(&cursor); |
339 | 241 | m_first_ptr = addr + cursor; |
340 | | |
341 | 241 | return true; |
342 | 241 | } |
343 | | |
344 | 515 | bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) { |
345 | 515 | size_t size = GetSize(process); |
346 | | |
347 | 515 | DataBufferHeap buffer(size, '\0'); |
348 | 515 | Status error; |
349 | | |
350 | 515 | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
351 | 515 | if (error.Fail()) { |
352 | 0 | return false; |
353 | 0 | } |
354 | | |
355 | 515 | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
356 | 515 | process->GetAddressByteSize()); |
357 | | |
358 | 515 | lldb::offset_t cursor = 0; |
359 | | |
360 | 515 | m_offset_ptr = extractor.GetAddress_unchecked(&cursor); |
361 | 515 | m_name_ptr = extractor.GetAddress_unchecked(&cursor); |
362 | 515 | m_type_ptr = extractor.GetAddress_unchecked(&cursor); |
363 | 515 | m_alignment = extractor.GetU32_unchecked(&cursor); |
364 | 515 | m_size = extractor.GetU32_unchecked(&cursor); |
365 | | |
366 | 515 | process->ReadCStringFromMemory(m_name_ptr, m_name, error); |
367 | 515 | if (error.Fail()) { |
368 | 0 | return false; |
369 | 0 | } |
370 | | |
371 | 515 | process->ReadCStringFromMemory(m_type_ptr, m_type, error); |
372 | 515 | return !error.Fail(); |
373 | 515 | } |
374 | | |
375 | | bool ClassDescriptorV2::relative_list_entry_t::Read(Process *process, |
376 | 0 | lldb::addr_t addr) { |
377 | 0 | Log *log = GetLog(LLDBLog::Types); |
378 | 0 | size_t size = sizeof(uint64_t); // m_image_index : 16 |
379 | | // m_list_offset : 48 |
380 | |
|
381 | 0 | DataBufferHeap buffer(size, '\0'); |
382 | 0 | Status error; |
383 | |
|
384 | 0 | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
385 | | // FIXME: Propagate this error up |
386 | 0 | if (error.Fail()) { |
387 | 0 | LLDB_LOG(log, "Failed to read relative_list_entry_t at address {0:x}", |
388 | 0 | addr); |
389 | 0 | return false; |
390 | 0 | } |
391 | | |
392 | 0 | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
393 | 0 | process->GetAddressByteSize()); |
394 | 0 | lldb::offset_t cursor = 0; |
395 | 0 | uint64_t raw_entry = extractor.GetU64_unchecked(&cursor); |
396 | 0 | m_image_index = raw_entry & 0xFFFF; |
397 | 0 | m_list_offset = (int64_t)(raw_entry >> 16); |
398 | 0 | return true; |
399 | 0 | } |
400 | | |
401 | | bool ClassDescriptorV2::relative_list_list_t::Read(Process *process, |
402 | 0 | lldb::addr_t addr) { |
403 | 0 | Log *log = GetLog(LLDBLog::Types); |
404 | 0 | size_t size = sizeof(uint32_t) // m_entsize |
405 | 0 | + sizeof(uint32_t); // m_count |
406 | |
|
407 | 0 | DataBufferHeap buffer(size, '\0'); |
408 | 0 | Status error; |
409 | | |
410 | | // FIXME: Propagate this error up |
411 | 0 | process->ReadMemory(addr, buffer.GetBytes(), size, error); |
412 | 0 | if (error.Fail()) { |
413 | 0 | LLDB_LOG(log, "Failed to read relative_list_list_t at address 0x" PRIx64, |
414 | 0 | addr); |
415 | 0 | return false; |
416 | 0 | } |
417 | | |
418 | 0 | DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), |
419 | 0 | process->GetAddressByteSize()); |
420 | 0 | lldb::offset_t cursor = 0; |
421 | 0 | m_entsize = extractor.GetU32_unchecked(&cursor); |
422 | 0 | m_count = extractor.GetU32_unchecked(&cursor); |
423 | 0 | m_first_ptr = addr + cursor; |
424 | 0 | return true; |
425 | 0 | } |
426 | | |
427 | | std::optional<ClassDescriptorV2::method_list_t> |
428 | | ClassDescriptorV2::GetMethodList(Process *process, |
429 | 636 | lldb::addr_t method_list_ptr) const { |
430 | 636 | Log *log = GetLog(LLDBLog::Types); |
431 | 636 | ClassDescriptorV2::method_list_t method_list; |
432 | 636 | if (!method_list.Read(process, method_list_ptr)) |
433 | 52 | return std::nullopt; |
434 | | |
435 | 584 | const size_t method_size = method_t::GetSize(process, method_list.m_is_small); |
436 | 584 | if (method_list.m_entsize != method_size) { |
437 | 0 | LLDB_LOG(log, |
438 | 0 | "method_list_t at address 0x" PRIx64 " has an entsize of " PRIu16 |
439 | 0 | " but method size should be " PRIu64, |
440 | 0 | method_list_ptr, method_list.m_entsize, method_size); |
441 | 0 | return std::nullopt; |
442 | 0 | } |
443 | | |
444 | 584 | return method_list; |
445 | 584 | } |
446 | | |
447 | | bool ClassDescriptorV2::ProcessMethodList( |
448 | | std::function<bool(const char *, const char *)> const &instance_method_func, |
449 | 584 | ClassDescriptorV2::method_list_t &method_list) const { |
450 | 584 | lldb_private::Process *process = m_runtime.GetProcess(); |
451 | 584 | auto method = std::make_unique<method_t>(); |
452 | 584 | lldb::addr_t relative_selector_base_addr = |
453 | 584 | m_runtime.GetRelativeSelectorBaseAddr(); |
454 | 20.9k | for (uint32_t i = 0, e = method_list.m_count; i < e; ++i20.4k ) { |
455 | 20.4k | method->Read(process, method_list.m_first_ptr + (i * method_list.m_entsize), |
456 | 20.4k | relative_selector_base_addr, method_list.m_is_small, |
457 | 20.4k | method_list.m_has_direct_selector); |
458 | 20.4k | if (instance_method_func(method->m_name.c_str(), method->m_types.c_str())) |
459 | 0 | break; |
460 | 20.4k | } |
461 | 584 | return true; |
462 | 584 | } |
463 | | |
464 | | // The relevant data structures: |
465 | | // - relative_list_list_t |
466 | | // - uint32_t count |
467 | | // - uint32_t entsize |
468 | | // - Followed by <count> number of relative_list_entry_t of size <entsize> |
469 | | // |
470 | | // - relative_list_entry_t |
471 | | // - uint64_t image_index : 16 |
472 | | // - int64_t list_offset : 48 |
473 | | // - Note: The above 2 fit into 8 bytes always |
474 | | // |
475 | | // image_index corresponds to an image in the shared cache |
476 | | // list_offset is used to calculate the address of the method_list_t we want |
477 | | bool ClassDescriptorV2::ProcessRelativeMethodLists( |
478 | | std::function<bool(const char *, const char *)> const &instance_method_func, |
479 | 0 | lldb::addr_t relative_method_list_ptr) const { |
480 | 0 | lldb_private::Process *process = m_runtime.GetProcess(); |
481 | 0 | auto relative_method_lists = std::make_unique<relative_list_list_t>(); |
482 | | |
483 | | // 1. Process the count and entsize of the relative_list_list_t |
484 | 0 | if (!relative_method_lists->Read(process, relative_method_list_ptr)) |
485 | 0 | return false; |
486 | | |
487 | 0 | auto entry = std::make_unique<relative_list_entry_t>(); |
488 | 0 | for (uint32_t i = 0; i < relative_method_lists->m_count; i++) { |
489 | | // 2. Extract the image index and the list offset from the |
490 | | // relative_list_entry_t |
491 | 0 | const lldb::addr_t entry_addr = relative_method_lists->m_first_ptr + |
492 | 0 | (i * relative_method_lists->m_entsize); |
493 | 0 | if (!entry->Read(process, entry_addr)) |
494 | 0 | return false; |
495 | | |
496 | | // 3. Calculate the pointer to the method_list_t from the |
497 | | // relative_list_entry_t |
498 | 0 | const lldb::addr_t method_list_addr = entry_addr + entry->m_list_offset; |
499 | | |
500 | | // 4. Get the method_list_t from the pointer |
501 | 0 | std::optional<method_list_t> method_list = |
502 | 0 | GetMethodList(process, method_list_addr); |
503 | 0 | if (!method_list) |
504 | 0 | return false; |
505 | | |
506 | | // 5. Cache the result so we don't need to reconstruct it later. |
507 | 0 | m_image_to_method_lists[entry->m_image_index].emplace_back(*method_list); |
508 | | |
509 | | // 6. If the relevant image is loaded, add the methods to the Decl |
510 | 0 | if (!m_runtime.IsSharedCacheImageLoaded(entry->m_image_index)) |
511 | 0 | continue; |
512 | | |
513 | 0 | if (!ProcessMethodList(instance_method_func, *method_list)) |
514 | 0 | return false; |
515 | 0 | } |
516 | | |
517 | | // We need to keep track of the last time we updated so we can re-update the |
518 | | // type information in the future |
519 | 0 | m_last_version_updated = m_runtime.GetSharedCacheImageHeaderVersion(); |
520 | |
|
521 | 0 | return true; |
522 | 0 | } |
523 | | |
524 | | bool ClassDescriptorV2::Describe( |
525 | | std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func, |
526 | | std::function<bool(const char *, const char *)> const &instance_method_func, |
527 | | std::function<bool(const char *, const char *)> const &class_method_func, |
528 | | std::function<bool(const char *, const char *, lldb::addr_t, |
529 | 692 | uint64_t)> const &ivar_func) const { |
530 | 692 | lldb_private::Process *process = m_runtime.GetProcess(); |
531 | | |
532 | 692 | std::unique_ptr<objc_class_t> objc_class; |
533 | 692 | std::unique_ptr<class_ro_t> class_ro; |
534 | 692 | std::unique_ptr<class_rw_t> class_rw; |
535 | | |
536 | 692 | if (!Read_objc_class(process, objc_class)) |
537 | 0 | return false; |
538 | 692 | if (!Read_class_row(process, *objc_class, class_ro, class_rw)) |
539 | 0 | return false; |
540 | | |
541 | 692 | static ConstString NSObject_name("NSObject"); |
542 | | |
543 | 692 | if (m_name != NSObject_name && superclass_func584 ) |
544 | 212 | superclass_func(objc_class->m_superclass); |
545 | | |
546 | 692 | if (instance_method_func) { |
547 | | // This is a relative list of lists |
548 | 636 | if (class_ro->m_baseMethods_ptr & 1) { |
549 | 0 | if (!ProcessRelativeMethodLists(instance_method_func, |
550 | 0 | class_ro->m_baseMethods_ptr ^ 1)) |
551 | 0 | return false; |
552 | 636 | } else { |
553 | 636 | std::optional<method_list_t> base_method_list = |
554 | 636 | GetMethodList(process, class_ro->m_baseMethods_ptr); |
555 | 636 | if (!base_method_list) |
556 | 52 | return false; |
557 | 584 | if (!ProcessMethodList(instance_method_func, *base_method_list)) |
558 | 0 | return false; |
559 | 584 | } |
560 | 636 | } |
561 | | |
562 | 640 | if (class_method_func) { |
563 | 316 | AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass()); |
564 | | |
565 | | // We don't care about the metaclass's superclass, or its class methods. |
566 | | // Its instance methods are our class methods. |
567 | | |
568 | 316 | if (metaclass) { |
569 | 316 | metaclass->Describe( |
570 | 316 | std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr), |
571 | 316 | class_method_func, |
572 | 316 | std::function<bool(const char *, const char *)>(nullptr), |
573 | 316 | std::function<bool(const char *, const char *, lldb::addr_t, |
574 | 316 | uint64_t)>(nullptr)); |
575 | 316 | } |
576 | 316 | } |
577 | | |
578 | 640 | if (ivar_func) { |
579 | 372 | if (class_ro->m_ivars_ptr != 0) { |
580 | 241 | ivar_list_t ivar_list; |
581 | 241 | if (!ivar_list.Read(process, class_ro->m_ivars_ptr)) |
582 | 0 | return false; |
583 | | |
584 | 241 | if (ivar_list.m_entsize != ivar_t::GetSize(process)) |
585 | 0 | return false; |
586 | | |
587 | 241 | ivar_t ivar; |
588 | | |
589 | 749 | for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i508 ) { |
590 | 515 | ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize)); |
591 | | |
592 | 515 | if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(), |
593 | 515 | ivar.m_offset_ptr, ivar.m_size)) |
594 | 7 | break; |
595 | 515 | } |
596 | 241 | } |
597 | 372 | } |
598 | | |
599 | 640 | return true; |
600 | 640 | } |
601 | | |
602 | 7.79k | ConstString ClassDescriptorV2::GetClassName() { |
603 | 7.79k | if (!m_name) { |
604 | 847 | lldb_private::Process *process = m_runtime.GetProcess(); |
605 | | |
606 | 847 | if (process) { |
607 | 847 | std::unique_ptr<objc_class_t> objc_class; |
608 | 847 | std::unique_ptr<class_ro_t> class_ro; |
609 | 847 | std::unique_ptr<class_rw_t> class_rw; |
610 | | |
611 | 847 | if (!Read_objc_class(process, objc_class)) |
612 | 0 | return m_name; |
613 | 847 | if (!Read_class_row(process, *objc_class, class_ro, class_rw)) |
614 | 0 | return m_name; |
615 | | |
616 | 847 | m_name = ConstString(class_ro->m_name.c_str()); |
617 | 847 | } |
618 | 847 | } |
619 | 7.79k | return m_name; |
620 | 7.79k | } |
621 | | |
622 | 317 | ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() { |
623 | 317 | lldb_private::Process *process = m_runtime.GetProcess(); |
624 | | |
625 | 317 | if (!process) |
626 | 0 | return ObjCLanguageRuntime::ClassDescriptorSP(); |
627 | | |
628 | 317 | std::unique_ptr<objc_class_t> objc_class; |
629 | | |
630 | 317 | if (!Read_objc_class(process, objc_class)) |
631 | 0 | return ObjCLanguageRuntime::ClassDescriptorSP(); |
632 | | |
633 | 317 | return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA( |
634 | 317 | objc_class->m_superclass); |
635 | 317 | } |
636 | | |
637 | 316 | ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const { |
638 | 316 | lldb_private::Process *process = m_runtime.GetProcess(); |
639 | | |
640 | 316 | if (!process) |
641 | 0 | return ObjCLanguageRuntime::ClassDescriptorSP(); |
642 | | |
643 | 316 | std::unique_ptr<objc_class_t> objc_class; |
644 | | |
645 | 316 | if (!Read_objc_class(process, objc_class)) |
646 | 0 | return ObjCLanguageRuntime::ClassDescriptorSP(); |
647 | | |
648 | 316 | lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa); |
649 | | |
650 | 316 | return ObjCLanguageRuntime::ClassDescriptorSP( |
651 | 316 | new ClassDescriptorV2(m_runtime, candidate_isa, nullptr)); |
652 | 316 | } |
653 | | |
654 | 0 | uint64_t ClassDescriptorV2::GetInstanceSize() { |
655 | 0 | lldb_private::Process *process = m_runtime.GetProcess(); |
656 | |
|
657 | 0 | if (process) { |
658 | 0 | std::unique_ptr<objc_class_t> objc_class; |
659 | 0 | std::unique_ptr<class_ro_t> class_ro; |
660 | 0 | std::unique_ptr<class_rw_t> class_rw; |
661 | |
|
662 | 0 | if (!Read_objc_class(process, objc_class)) |
663 | 0 | return 0; |
664 | 0 | if (!Read_class_row(process, *objc_class, class_ro, class_rw)) |
665 | 0 | return 0; |
666 | | |
667 | 0 | return class_ro->m_instanceSize; |
668 | 0 | } |
669 | | |
670 | 0 | return 0; |
671 | 0 | } |
672 | | |
673 | | // From the ObjC runtime. |
674 | | static uint8_t IS_SWIFT_STABLE = 1U << 1; |
675 | | |
676 | 819 | LanguageType ClassDescriptorV2::GetImplementationLanguage() const { |
677 | 819 | std::unique_ptr<objc_class_t> objc_class; |
678 | 819 | if (auto *process = m_runtime.GetProcess()) |
679 | 819 | if (Read_objc_class(process, objc_class)) |
680 | 819 | if (objc_class->m_flags & IS_SWIFT_STABLE) |
681 | 0 | return lldb::eLanguageTypeSwift; |
682 | | |
683 | 819 | return lldb::eLanguageTypeObjC; |
684 | 819 | } |
685 | | |
686 | 78.4M | ClassDescriptorV2::iVarsStorage::iVarsStorage() : m_ivars(), m_mutex() {} |
687 | | |
688 | 453 | size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); } |
689 | | |
690 | | ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage:: |
691 | 186 | operator[](size_t idx) { |
692 | 186 | return m_ivars[idx]; |
693 | 186 | } |
694 | | |
695 | | void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime, |
696 | 453 | ClassDescriptorV2 &descriptor) { |
697 | 453 | if (m_filled) |
698 | 404 | return; |
699 | 49 | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
700 | 49 | Log *log = GetLog(LLDBLog::Types); |
701 | 49 | LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName()); |
702 | 49 | m_filled = true; |
703 | 49 | ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp( |
704 | 49 | runtime.GetEncodingToType()); |
705 | 49 | Process *process(runtime.GetProcess()); |
706 | 49 | if (!encoding_to_type_sp) |
707 | 0 | return; |
708 | 49 | descriptor.Describe(nullptr, nullptr, nullptr, [this, process, |
709 | 49 | encoding_to_type_sp, |
710 | 49 | log](const char *name, |
711 | 49 | const char *type, |
712 | 49 | lldb::addr_t offset_ptr, |
713 | 143 | uint64_t size) -> bool { |
714 | 143 | const bool for_expression = false; |
715 | 143 | const bool stop_loop = false; |
716 | 143 | LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}", |
717 | 143 | name, type, offset_ptr, size); |
718 | 143 | CompilerType ivar_type = |
719 | 143 | encoding_to_type_sp->RealizeType(type, for_expression); |
720 | 143 | if (ivar_type) { |
721 | 135 | LLDB_LOGV(log, |
722 | 135 | "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = " |
723 | 135 | "{3}, type_size = {4}", |
724 | 135 | name, type, offset_ptr, size, |
725 | 135 | ivar_type.GetByteSize(nullptr).value_or(0)); |
726 | 135 | Scalar offset_scalar; |
727 | 135 | Status error; |
728 | 135 | const int offset_ptr_size = 4; |
729 | 135 | const bool is_signed = false; |
730 | 135 | size_t read = process->ReadScalarIntegerFromMemory( |
731 | 135 | offset_ptr, offset_ptr_size, is_signed, offset_scalar, error); |
732 | 135 | if (error.Success() && 4 == read) { |
733 | 135 | LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr, |
734 | 135 | offset_scalar.SInt()); |
735 | 135 | m_ivars.push_back( |
736 | 135 | {ConstString(name), ivar_type, size, offset_scalar.SInt()}); |
737 | 135 | } else |
738 | 0 | LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}", |
739 | 135 | offset_ptr, read); |
740 | 135 | } |
741 | 143 | return stop_loop; |
742 | 143 | }); |
743 | 49 | } |
744 | | |
745 | 453 | void ClassDescriptorV2::GetIVarInformation() { |
746 | 453 | m_ivars_storage.fill(m_runtime, *this); |
747 | 453 | } |