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