1 //===-- AppleObjCClassDescriptorV2.cpp -----------------------------*- C++
2 //-*-===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "AppleObjCClassDescriptorV2.h"
11
12 #include "lldb/Expression/FunctionCaller.h"
13 #include "lldb/Utility/Log.h"
14
15 using namespace lldb;
16 using namespace lldb_private;
17
Read_objc_class(Process * process,std::unique_ptr<objc_class_t> & objc_class) const18 bool ClassDescriptorV2::Read_objc_class(
19 Process *process, std::unique_ptr<objc_class_t> &objc_class) const {
20 objc_class.reset(new objc_class_t);
21
22 bool ret = objc_class->Read(process, m_objc_class_ptr);
23
24 if (!ret)
25 objc_class.reset();
26
27 return ret;
28 }
29
GetClassDataMask(Process * process)30 static lldb::addr_t GetClassDataMask(Process *process) {
31 switch (process->GetAddressByteSize()) {
32 case 4:
33 return 0xfffffffcUL;
34 case 8:
35 return 0x00007ffffffffff8UL;
36 default:
37 break;
38 }
39
40 return LLDB_INVALID_ADDRESS;
41 }
42
Read(Process * process,lldb::addr_t addr)43 bool ClassDescriptorV2::objc_class_t::Read(Process *process,
44 lldb::addr_t addr) {
45 size_t ptr_size = process->GetAddressByteSize();
46
47 size_t objc_class_size = ptr_size // uintptr_t isa;
48 + ptr_size // Class superclass;
49 + ptr_size // void *cache;
50 + ptr_size // IMP *vtable;
51 + ptr_size; // uintptr_t data_NEVER_USE;
52
53 DataBufferHeap objc_class_buf(objc_class_size, '\0');
54 Status error;
55
56 process->ReadMemory(addr, objc_class_buf.GetBytes(), objc_class_size, error);
57 if (error.Fail()) {
58 return false;
59 }
60
61 DataExtractor extractor(objc_class_buf.GetBytes(), objc_class_size,
62 process->GetByteOrder(),
63 process->GetAddressByteSize());
64
65 lldb::offset_t cursor = 0;
66
67 m_isa = extractor.GetAddress_unchecked(&cursor); // uintptr_t isa;
68 m_superclass = extractor.GetAddress_unchecked(&cursor); // Class superclass;
69 m_cache_ptr = extractor.GetAddress_unchecked(&cursor); // void *cache;
70 m_vtable_ptr = extractor.GetAddress_unchecked(&cursor); // IMP *vtable;
71 lldb::addr_t data_NEVER_USE =
72 extractor.GetAddress_unchecked(&cursor); // uintptr_t data_NEVER_USE;
73
74 m_flags = (uint8_t)(data_NEVER_USE & (lldb::addr_t)3);
75 m_data_ptr = data_NEVER_USE & GetClassDataMask(process);
76
77 return true;
78 }
79
Read(Process * process,lldb::addr_t addr)80 bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) {
81 size_t ptr_size = process->GetAddressByteSize();
82
83 size_t size = sizeof(uint32_t) // uint32_t flags;
84 + sizeof(uint32_t) // uint32_t version;
85 + ptr_size // const class_ro_t *ro;
86 + ptr_size // union { method_list_t **method_lists;
87 // method_list_t *method_list; };
88 + ptr_size // struct chained_property_list *properties;
89 + ptr_size // const protocol_list_t **protocols;
90 + ptr_size // Class firstSubclass;
91 + ptr_size; // Class nextSiblingClass;
92
93 DataBufferHeap buffer(size, '\0');
94 Status error;
95
96 process->ReadMemory(addr, buffer.GetBytes(), size, error);
97 if (error.Fail()) {
98 return false;
99 }
100
101 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
102 process->GetAddressByteSize());
103
104 lldb::offset_t cursor = 0;
105
106 m_flags = extractor.GetU32_unchecked(&cursor);
107 m_version = extractor.GetU32_unchecked(&cursor);
108 m_ro_ptr = extractor.GetAddress_unchecked(&cursor);
109 m_method_list_ptr = extractor.GetAddress_unchecked(&cursor);
110 m_properties_ptr = extractor.GetAddress_unchecked(&cursor);
111 m_firstSubclass = extractor.GetAddress_unchecked(&cursor);
112 m_nextSiblingClass = extractor.GetAddress_unchecked(&cursor);
113
114 if (m_ro_ptr & 1) {
115 DataBufferHeap buffer(ptr_size, '\0');
116 process->ReadMemory(m_ro_ptr ^ 1, buffer.GetBytes(), ptr_size, error);
117 if (error.Fail())
118 return false;
119 cursor = 0;
120 DataExtractor extractor(buffer.GetBytes(), ptr_size,
121 process->GetByteOrder(),
122 process->GetAddressByteSize());
123 m_ro_ptr = extractor.GetAddress_unchecked(&cursor);
124 }
125
126 return true;
127 }
128
Read(Process * process,lldb::addr_t addr)129 bool ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) {
130 size_t ptr_size = process->GetAddressByteSize();
131
132 size_t size = sizeof(uint32_t) // uint32_t flags;
133 + sizeof(uint32_t) // uint32_t instanceStart;
134 + sizeof(uint32_t) // uint32_t instanceSize;
135 + (ptr_size == 8 ? sizeof(uint32_t)
136 : 0) // uint32_t reserved; // __LP64__ only
137 + ptr_size // const uint8_t *ivarLayout;
138 + ptr_size // const char *name;
139 + ptr_size // const method_list_t *baseMethods;
140 + ptr_size // const protocol_list_t *baseProtocols;
141 + ptr_size // const ivar_list_t *ivars;
142 + ptr_size // const uint8_t *weakIvarLayout;
143 + ptr_size; // const property_list_t *baseProperties;
144
145 DataBufferHeap buffer(size, '\0');
146 Status error;
147
148 process->ReadMemory(addr, buffer.GetBytes(), size, error);
149 if (error.Fail()) {
150 return false;
151 }
152
153 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
154 process->GetAddressByteSize());
155
156 lldb::offset_t cursor = 0;
157
158 m_flags = extractor.GetU32_unchecked(&cursor);
159 m_instanceStart = extractor.GetU32_unchecked(&cursor);
160 m_instanceSize = extractor.GetU32_unchecked(&cursor);
161 if (ptr_size == 8)
162 m_reserved = extractor.GetU32_unchecked(&cursor);
163 else
164 m_reserved = 0;
165 m_ivarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
166 m_name_ptr = extractor.GetAddress_unchecked(&cursor);
167 m_baseMethods_ptr = extractor.GetAddress_unchecked(&cursor);
168 m_baseProtocols_ptr = extractor.GetAddress_unchecked(&cursor);
169 m_ivars_ptr = extractor.GetAddress_unchecked(&cursor);
170 m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
171 m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor);
172
173 DataBufferHeap name_buf(1024, '\0');
174
175 process->ReadCStringFromMemory(m_name_ptr, (char *)name_buf.GetBytes(),
176 name_buf.GetByteSize(), error);
177
178 if (error.Fail()) {
179 return false;
180 }
181
182 m_name.assign((char *)name_buf.GetBytes());
183
184 return true;
185 }
186
Read_class_row(Process * process,const objc_class_t & objc_class,std::unique_ptr<class_ro_t> & class_ro,std::unique_ptr<class_rw_t> & class_rw) const187 bool ClassDescriptorV2::Read_class_row(
188 Process *process, const objc_class_t &objc_class,
189 std::unique_ptr<class_ro_t> &class_ro,
190 std::unique_ptr<class_rw_t> &class_rw) const {
191 class_ro.reset();
192 class_rw.reset();
193
194 Status error;
195 uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory(
196 objc_class.m_data_ptr, sizeof(uint32_t), 0, error);
197 if (!error.Success())
198 return false;
199
200 if (class_row_t_flags & RW_REALIZED) {
201 class_rw.reset(new class_rw_t);
202
203 if (!class_rw->Read(process, objc_class.m_data_ptr)) {
204 class_rw.reset();
205 return false;
206 }
207
208 class_ro.reset(new class_ro_t);
209
210 if (!class_ro->Read(process, class_rw->m_ro_ptr)) {
211 class_rw.reset();
212 class_ro.reset();
213 return false;
214 }
215 } else {
216 class_ro.reset(new class_ro_t);
217
218 if (!class_ro->Read(process, objc_class.m_data_ptr)) {
219 class_ro.reset();
220 return false;
221 }
222 }
223
224 return true;
225 }
226
Read(Process * process,lldb::addr_t addr)227 bool ClassDescriptorV2::method_list_t::Read(Process *process,
228 lldb::addr_t addr) {
229 size_t size = sizeof(uint32_t) // uint32_t entsize_NEVER_USE;
230 + sizeof(uint32_t); // uint32_t count;
231
232 DataBufferHeap buffer(size, '\0');
233 Status error;
234
235 process->ReadMemory(addr, buffer.GetBytes(), size, error);
236 if (error.Fail()) {
237 return false;
238 }
239
240 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
241 process->GetAddressByteSize());
242
243 lldb::offset_t cursor = 0;
244
245 m_entsize = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3;
246 m_count = extractor.GetU32_unchecked(&cursor);
247 m_first_ptr = addr + cursor;
248
249 return true;
250 }
251
Read(Process * process,lldb::addr_t addr)252 bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) {
253 size_t size = GetSize(process);
254
255 DataBufferHeap buffer(size, '\0');
256 Status error;
257
258 process->ReadMemory(addr, buffer.GetBytes(), size, error);
259 if (error.Fail()) {
260 return false;
261 }
262
263 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
264 process->GetAddressByteSize());
265
266 lldb::offset_t cursor = 0;
267
268 m_name_ptr = extractor.GetAddress_unchecked(&cursor);
269 m_types_ptr = extractor.GetAddress_unchecked(&cursor);
270 m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
271
272 process->ReadCStringFromMemory(m_name_ptr, m_name, error);
273 if (error.Fail()) {
274 return false;
275 }
276
277 process->ReadCStringFromMemory(m_types_ptr, m_types, error);
278 return !error.Fail();
279 }
280
Read(Process * process,lldb::addr_t addr)281 bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) {
282 size_t size = sizeof(uint32_t) // uint32_t entsize;
283 + sizeof(uint32_t); // uint32_t count;
284
285 DataBufferHeap buffer(size, '\0');
286 Status error;
287
288 process->ReadMemory(addr, buffer.GetBytes(), size, error);
289 if (error.Fail()) {
290 return false;
291 }
292
293 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
294 process->GetAddressByteSize());
295
296 lldb::offset_t cursor = 0;
297
298 m_entsize = extractor.GetU32_unchecked(&cursor);
299 m_count = extractor.GetU32_unchecked(&cursor);
300 m_first_ptr = addr + cursor;
301
302 return true;
303 }
304
Read(Process * process,lldb::addr_t addr)305 bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) {
306 size_t size = GetSize(process);
307
308 DataBufferHeap buffer(size, '\0');
309 Status error;
310
311 process->ReadMemory(addr, buffer.GetBytes(), size, error);
312 if (error.Fail()) {
313 return false;
314 }
315
316 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
317 process->GetAddressByteSize());
318
319 lldb::offset_t cursor = 0;
320
321 m_offset_ptr = extractor.GetAddress_unchecked(&cursor);
322 m_name_ptr = extractor.GetAddress_unchecked(&cursor);
323 m_type_ptr = extractor.GetAddress_unchecked(&cursor);
324 m_alignment = extractor.GetU32_unchecked(&cursor);
325 m_size = extractor.GetU32_unchecked(&cursor);
326
327 process->ReadCStringFromMemory(m_name_ptr, m_name, error);
328 if (error.Fail()) {
329 return false;
330 }
331
332 process->ReadCStringFromMemory(m_type_ptr, m_type, error);
333 return !error.Fail();
334 }
335
Describe(std::function<void (ObjCLanguageRuntime::ObjCISA)> const & superclass_func,std::function<bool (const char *,const char *)> const & instance_method_func,std::function<bool (const char *,const char *)> const & class_method_func,std::function<bool (const char *,const char *,lldb::addr_t,uint64_t)> const & ivar_func) const336 bool ClassDescriptorV2::Describe(
337 std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
338 std::function<bool(const char *, const char *)> const &instance_method_func,
339 std::function<bool(const char *, const char *)> const &class_method_func,
340 std::function<bool(const char *, const char *, lldb::addr_t,
341 uint64_t)> const &ivar_func) const {
342 lldb_private::Process *process = m_runtime.GetProcess();
343
344 std::unique_ptr<objc_class_t> objc_class;
345 std::unique_ptr<class_ro_t> class_ro;
346 std::unique_ptr<class_rw_t> class_rw;
347
348 if (!Read_objc_class(process, objc_class))
349 return false;
350 if (!Read_class_row(process, *objc_class, class_ro, class_rw))
351 return false;
352
353 static ConstString NSObject_name("NSObject");
354
355 if (m_name != NSObject_name && superclass_func)
356 superclass_func(objc_class->m_superclass);
357
358 if (instance_method_func) {
359 std::unique_ptr<method_list_t> base_method_list;
360
361 base_method_list.reset(new method_list_t);
362 if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
363 return false;
364
365 if (base_method_list->m_entsize != method_t::GetSize(process))
366 return false;
367
368 std::unique_ptr<method_t> method;
369 method.reset(new method_t);
370
371 for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
372 method->Read(process, base_method_list->m_first_ptr +
373 (i * base_method_list->m_entsize));
374
375 if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
376 break;
377 }
378 }
379
380 if (class_method_func) {
381 AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass());
382
383 // We don't care about the metaclass's superclass, or its class methods.
384 // Its instance methods are our class methods.
385
386 if (metaclass) {
387 metaclass->Describe(
388 std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr),
389 class_method_func,
390 std::function<bool(const char *, const char *)>(nullptr),
391 std::function<bool(const char *, const char *, lldb::addr_t,
392 uint64_t)>(nullptr));
393 }
394 }
395
396 if (ivar_func) {
397 if (class_ro->m_ivars_ptr != 0) {
398 ivar_list_t ivar_list;
399 if (!ivar_list.Read(process, class_ro->m_ivars_ptr))
400 return false;
401
402 if (ivar_list.m_entsize != ivar_t::GetSize(process))
403 return false;
404
405 ivar_t ivar;
406
407 for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) {
408 ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize));
409
410 if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(),
411 ivar.m_offset_ptr, ivar.m_size))
412 break;
413 }
414 }
415 }
416
417 return true;
418 }
419
GetClassName()420 ConstString ClassDescriptorV2::GetClassName() {
421 if (!m_name) {
422 lldb_private::Process *process = m_runtime.GetProcess();
423
424 if (process) {
425 std::unique_ptr<objc_class_t> objc_class;
426 std::unique_ptr<class_ro_t> class_ro;
427 std::unique_ptr<class_rw_t> class_rw;
428
429 if (!Read_objc_class(process, objc_class))
430 return m_name;
431 if (!Read_class_row(process, *objc_class, class_ro, class_rw))
432 return m_name;
433
434 m_name = ConstString(class_ro->m_name.c_str());
435 }
436 }
437 return m_name;
438 }
439
GetSuperclass()440 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() {
441 lldb_private::Process *process = m_runtime.GetProcess();
442
443 if (!process)
444 return ObjCLanguageRuntime::ClassDescriptorSP();
445
446 std::unique_ptr<objc_class_t> objc_class;
447
448 if (!Read_objc_class(process, objc_class))
449 return ObjCLanguageRuntime::ClassDescriptorSP();
450
451 return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(
452 objc_class->m_superclass);
453 }
454
GetMetaclass() const455 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const {
456 lldb_private::Process *process = m_runtime.GetProcess();
457
458 if (!process)
459 return ObjCLanguageRuntime::ClassDescriptorSP();
460
461 std::unique_ptr<objc_class_t> objc_class;
462
463 if (!Read_objc_class(process, objc_class))
464 return ObjCLanguageRuntime::ClassDescriptorSP();
465
466 lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa);
467
468 return ObjCLanguageRuntime::ClassDescriptorSP(
469 new ClassDescriptorV2(m_runtime, candidate_isa, nullptr));
470 }
471
GetInstanceSize()472 uint64_t ClassDescriptorV2::GetInstanceSize() {
473 lldb_private::Process *process = m_runtime.GetProcess();
474
475 if (process) {
476 std::unique_ptr<objc_class_t> objc_class;
477 std::unique_ptr<class_ro_t> class_ro;
478 std::unique_ptr<class_rw_t> class_rw;
479
480 if (!Read_objc_class(process, objc_class))
481 return 0;
482 if (!Read_class_row(process, *objc_class, class_ro, class_rw))
483 return 0;
484
485 return class_ro->m_instanceSize;
486 }
487
488 return 0;
489 }
490
iVarsStorage()491 ClassDescriptorV2::iVarsStorage::iVarsStorage()
492 : m_filled(false), m_ivars(), m_mutex() {}
493
size()494 size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); }
495
496 ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage::
operator [](size_t idx)497 operator[](size_t idx) {
498 return m_ivars[idx];
499 }
500
fill(AppleObjCRuntimeV2 & runtime,ClassDescriptorV2 & descriptor)501 void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime,
502 ClassDescriptorV2 &descriptor) {
503 if (m_filled)
504 return;
505 std::lock_guard<std::recursive_mutex> guard(m_mutex);
506 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
507 LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName());
508 m_filled = true;
509 ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp(
510 runtime.GetEncodingToType());
511 Process *process(runtime.GetProcess());
512 if (!encoding_to_type_sp)
513 return;
514 descriptor.Describe(nullptr, nullptr, nullptr, [this, process,
515 encoding_to_type_sp,
516 log](const char *name,
517 const char *type,
518 lldb::addr_t offset_ptr,
519 uint64_t size) -> bool {
520 const bool for_expression = false;
521 const bool stop_loop = false;
522 LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}",
523 name, type, offset_ptr, size);
524 CompilerType ivar_type =
525 encoding_to_type_sp->RealizeType(type, for_expression);
526 if (ivar_type) {
527 LLDB_LOGV(log,
528 "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = "
529 "{3}, type_size = {4}",
530 name, type, offset_ptr, size,
531 ivar_type.GetByteSize(nullptr).getValueOr(0));
532 Scalar offset_scalar;
533 Status error;
534 const int offset_ptr_size = 4;
535 const bool is_signed = false;
536 size_t read = process->ReadScalarIntegerFromMemory(
537 offset_ptr, offset_ptr_size, is_signed, offset_scalar, error);
538 if (error.Success() && 4 == read) {
539 LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr,
540 offset_scalar.SInt());
541 m_ivars.push_back(
542 {ConstString(name), ivar_type, size, offset_scalar.SInt()});
543 } else
544 LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}",
545 offset_ptr, read);
546 }
547 return stop_loop;
548 });
549 }
550
GetIVarInformation()551 void ClassDescriptorV2::GetIVarInformation() {
552 m_ivars_storage.fill(m_runtime, *this);
553 }
554