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
18using namespace lldb;
19using namespace lldb_private;
20
21bool 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
33static 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
46bool 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 extractor(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
88bool 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 extractor(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 extractor(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
141bool 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 extractor(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
199bool 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
239bool 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 extractor(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
269bool 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 extractor(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
320bool 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 extractor(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
344bool 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 extractor(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
375bool 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 extractor(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
401bool 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 extractor(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
427std::optional<ClassDescriptorV2::method_list_t>
428ClassDescriptorV2::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
447bool 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
477bool 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
524bool 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
602ConstString 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
622ObjCLanguageRuntime::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
637ObjCLanguageRuntime::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
654uint64_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.
674static uint8_t IS_SWIFT_STABLE = 1U << 1;
675
676LanguageType 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
686ClassDescriptorV2::iVarsStorage::iVarsStorage() : m_ivars(), m_mutex() {}
687
688size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); }
689
690ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage::
691operator[](size_t idx) {
692 return m_ivars[idx];
693}
694
695void ClassDescriptorV2::iVarsStorage::fill(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
745void ClassDescriptorV2::GetIVarInformation() {
746 m_ivars_storage.fill(runtime&: m_runtime, descriptor&: *this);
747}
748

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