LLDB  mainline
AppleObjCClassDescriptorV2.cpp
Go to the documentation of this file.
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 
11 
13 #include "lldb/Utility/Log.h"
14 
15 using namespace lldb;
16 using namespace lldb_private;
17 
18 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 
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 
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 
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  return true;
115 }
116 
117 bool ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) {
118  size_t ptr_size = process->GetAddressByteSize();
119 
120  size_t size = sizeof(uint32_t) // uint32_t flags;
121  + sizeof(uint32_t) // uint32_t instanceStart;
122  + sizeof(uint32_t) // uint32_t instanceSize;
123  + (ptr_size == 8 ? sizeof(uint32_t)
124  : 0) // uint32_t reserved; // __LP64__ only
125  + ptr_size // const uint8_t *ivarLayout;
126  + ptr_size // const char *name;
127  + ptr_size // const method_list_t *baseMethods;
128  + ptr_size // const protocol_list_t *baseProtocols;
129  + ptr_size // const ivar_list_t *ivars;
130  + ptr_size // const uint8_t *weakIvarLayout;
131  + ptr_size; // const property_list_t *baseProperties;
132 
133  DataBufferHeap buffer(size, '\0');
134  Status error;
135 
136  process->ReadMemory(addr, buffer.GetBytes(), size, error);
137  if (error.Fail()) {
138  return false;
139  }
140 
141  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
142  process->GetAddressByteSize());
143 
144  lldb::offset_t cursor = 0;
145 
146  m_flags = extractor.GetU32_unchecked(&cursor);
147  m_instanceStart = extractor.GetU32_unchecked(&cursor);
148  m_instanceSize = extractor.GetU32_unchecked(&cursor);
149  if (ptr_size == 8)
150  m_reserved = extractor.GetU32_unchecked(&cursor);
151  else
152  m_reserved = 0;
153  m_ivarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
154  m_name_ptr = extractor.GetAddress_unchecked(&cursor);
155  m_baseMethods_ptr = extractor.GetAddress_unchecked(&cursor);
156  m_baseProtocols_ptr = extractor.GetAddress_unchecked(&cursor);
157  m_ivars_ptr = extractor.GetAddress_unchecked(&cursor);
158  m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
159  m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor);
160 
161  DataBufferHeap name_buf(1024, '\0');
162 
163  process->ReadCStringFromMemory(m_name_ptr, (char *)name_buf.GetBytes(),
164  name_buf.GetByteSize(), error);
165 
166  if (error.Fail()) {
167  return false;
168  }
169 
170  m_name.assign((char *)name_buf.GetBytes());
171 
172  return true;
173 }
174 
175 bool ClassDescriptorV2::Read_class_row(
176  Process *process, const objc_class_t &objc_class,
177  std::unique_ptr<class_ro_t> &class_ro,
178  std::unique_ptr<class_rw_t> &class_rw) const {
179  class_ro.reset();
180  class_rw.reset();
181 
182  Status error;
183  uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory(
184  objc_class.m_data_ptr, sizeof(uint32_t), 0, error);
185  if (!error.Success())
186  return false;
187 
188  if (class_row_t_flags & RW_REALIZED) {
189  class_rw.reset(new class_rw_t);
190 
191  if (!class_rw->Read(process, objc_class.m_data_ptr)) {
192  class_rw.reset();
193  return false;
194  }
195 
196  class_ro.reset(new class_ro_t);
197 
198  if (!class_ro->Read(process, class_rw->m_ro_ptr)) {
199  class_rw.reset();
200  class_ro.reset();
201  return false;
202  }
203  } else {
204  class_ro.reset(new class_ro_t);
205 
206  if (!class_ro->Read(process, objc_class.m_data_ptr)) {
207  class_ro.reset();
208  return false;
209  }
210  }
211 
212  return true;
213 }
214 
215 bool ClassDescriptorV2::method_list_t::Read(Process *process,
216  lldb::addr_t addr) {
217  size_t size = sizeof(uint32_t) // uint32_t entsize_NEVER_USE;
218  + sizeof(uint32_t); // uint32_t count;
219 
220  DataBufferHeap buffer(size, '\0');
221  Status error;
222 
223  process->ReadMemory(addr, buffer.GetBytes(), size, error);
224  if (error.Fail()) {
225  return false;
226  }
227 
228  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
229  process->GetAddressByteSize());
230 
231  lldb::offset_t cursor = 0;
232 
233  m_entsize = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3;
234  m_count = extractor.GetU32_unchecked(&cursor);
235  m_first_ptr = addr + cursor;
236 
237  return true;
238 }
239 
240 bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) {
241  size_t size = GetSize(process);
242 
243  DataBufferHeap buffer(size, '\0');
244  Status error;
245 
246  process->ReadMemory(addr, buffer.GetBytes(), size, error);
247  if (error.Fail()) {
248  return false;
249  }
250 
251  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
252  process->GetAddressByteSize());
253 
254  lldb::offset_t cursor = 0;
255 
256  m_name_ptr = extractor.GetAddress_unchecked(&cursor);
257  m_types_ptr = extractor.GetAddress_unchecked(&cursor);
258  m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
259 
260  process->ReadCStringFromMemory(m_name_ptr, m_name, error);
261  if (error.Fail()) {
262  return false;
263  }
264 
265  process->ReadCStringFromMemory(m_types_ptr, m_types, error);
266  return !error.Fail();
267 }
268 
269 bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) {
270  size_t size = sizeof(uint32_t) // uint32_t entsize;
271  + sizeof(uint32_t); // uint32_t count;
272 
273  DataBufferHeap buffer(size, '\0');
274  Status error;
275 
276  process->ReadMemory(addr, buffer.GetBytes(), size, error);
277  if (error.Fail()) {
278  return false;
279  }
280 
281  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
282  process->GetAddressByteSize());
283 
284  lldb::offset_t cursor = 0;
285 
286  m_entsize = extractor.GetU32_unchecked(&cursor);
287  m_count = extractor.GetU32_unchecked(&cursor);
288  m_first_ptr = addr + cursor;
289 
290  return true;
291 }
292 
293 bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) {
294  size_t size = GetSize(process);
295 
296  DataBufferHeap buffer(size, '\0');
297  Status error;
298 
299  process->ReadMemory(addr, buffer.GetBytes(), size, error);
300  if (error.Fail()) {
301  return false;
302  }
303 
304  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
305  process->GetAddressByteSize());
306 
307  lldb::offset_t cursor = 0;
308 
309  m_offset_ptr = extractor.GetAddress_unchecked(&cursor);
310  m_name_ptr = extractor.GetAddress_unchecked(&cursor);
311  m_type_ptr = extractor.GetAddress_unchecked(&cursor);
312  m_alignment = extractor.GetU32_unchecked(&cursor);
313  m_size = extractor.GetU32_unchecked(&cursor);
314 
315  process->ReadCStringFromMemory(m_name_ptr, m_name, error);
316  if (error.Fail()) {
317  return false;
318  }
319 
320  process->ReadCStringFromMemory(m_type_ptr, m_type, error);
321  return !error.Fail();
322 }
323 
324 bool ClassDescriptorV2::Describe(
325  std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
326  std::function<bool(const char *, const char *)> const &instance_method_func,
327  std::function<bool(const char *, const char *)> const &class_method_func,
328  std::function<bool(const char *, const char *, lldb::addr_t,
329  uint64_t)> const &ivar_func) const {
330  lldb_private::Process *process = m_runtime.GetProcess();
331 
332  std::unique_ptr<objc_class_t> objc_class;
333  std::unique_ptr<class_ro_t> class_ro;
334  std::unique_ptr<class_rw_t> class_rw;
335 
336  if (!Read_objc_class(process, objc_class))
337  return 0;
338  if (!Read_class_row(process, *objc_class, class_ro, class_rw))
339  return 0;
340 
341  static ConstString NSObject_name("NSObject");
342 
343  if (m_name != NSObject_name && superclass_func)
344  superclass_func(objc_class->m_superclass);
345 
346  if (instance_method_func) {
347  std::unique_ptr<method_list_t> base_method_list;
348 
349  base_method_list.reset(new method_list_t);
350  if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
351  return false;
352 
353  if (base_method_list->m_entsize != method_t::GetSize(process))
354  return false;
355 
356  std::unique_ptr<method_t> method;
357  method.reset(new method_t);
358 
359  for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
360  method->Read(process, base_method_list->m_first_ptr +
361  (i * base_method_list->m_entsize));
362 
363  if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
364  break;
365  }
366  }
367 
368  if (class_method_func) {
369  AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass());
370 
371  // We don't care about the metaclass's superclass, or its class methods.
372  // Its instance methods are our class methods.
373 
374  if (metaclass) {
375  metaclass->Describe(
376  std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr),
377  class_method_func,
378  std::function<bool(const char *, const char *)>(nullptr),
379  std::function<bool(const char *, const char *, lldb::addr_t,
380  uint64_t)>(nullptr));
381  }
382  }
383 
384  if (ivar_func) {
385  if (class_ro->m_ivars_ptr != 0) {
386  ivar_list_t ivar_list;
387  if (!ivar_list.Read(process, class_ro->m_ivars_ptr))
388  return false;
389 
390  if (ivar_list.m_entsize != ivar_t::GetSize(process))
391  return false;
392 
393  ivar_t ivar;
394 
395  for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) {
396  ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize));
397 
398  if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(),
399  ivar.m_offset_ptr, ivar.m_size))
400  break;
401  }
402  }
403  }
404 
405  return true;
406 }
407 
408 ConstString ClassDescriptorV2::GetClassName() {
409  if (!m_name) {
410  lldb_private::Process *process = m_runtime.GetProcess();
411 
412  if (process) {
413  std::unique_ptr<objc_class_t> objc_class;
414  std::unique_ptr<class_ro_t> class_ro;
415  std::unique_ptr<class_rw_t> class_rw;
416 
417  if (!Read_objc_class(process, objc_class))
418  return m_name;
419  if (!Read_class_row(process, *objc_class, class_ro, class_rw))
420  return m_name;
421 
422  m_name = ConstString(class_ro->m_name.c_str());
423  }
424  }
425  return m_name;
426 }
427 
428 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() {
429  lldb_private::Process *process = m_runtime.GetProcess();
430 
431  if (!process)
433 
434  std::unique_ptr<objc_class_t> objc_class;
435 
436  if (!Read_objc_class(process, objc_class))
438 
439  return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(
440  objc_class->m_superclass);
441 }
442 
443 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const {
444  lldb_private::Process *process = m_runtime.GetProcess();
445 
446  if (!process)
448 
449  std::unique_ptr<objc_class_t> objc_class;
450 
451  if (!Read_objc_class(process, objc_class))
453 
454  lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa);
455 
457  new ClassDescriptorV2(m_runtime, candidate_isa, nullptr));
458 }
459 
460 uint64_t ClassDescriptorV2::GetInstanceSize() {
461  lldb_private::Process *process = m_runtime.GetProcess();
462 
463  if (process) {
464  std::unique_ptr<objc_class_t> objc_class;
465  std::unique_ptr<class_ro_t> class_ro;
466  std::unique_ptr<class_rw_t> class_rw;
467 
468  if (!Read_objc_class(process, objc_class))
469  return 0;
470  if (!Read_class_row(process, *objc_class, class_ro, class_rw))
471  return 0;
472 
473  return class_ro->m_instanceSize;
474  }
475 
476  return 0;
477 }
478 
479 ClassDescriptorV2::iVarsStorage::iVarsStorage()
480  : m_filled(false), m_ivars(), m_mutex() {}
481 
482 size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); }
483 
484 ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage::
485 operator[](size_t idx) {
486  return m_ivars[idx];
487 }
488 
489 void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime,
490  ClassDescriptorV2 &descriptor) {
491  if (m_filled)
492  return;
493  std::lock_guard<std::recursive_mutex> guard(m_mutex);
495  LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName());
496  m_filled = true;
497  ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp(
498  runtime.GetEncodingToType());
499  Process *process(runtime.GetProcess());
500  if (!encoding_to_type_sp)
501  return;
502  descriptor.Describe(nullptr, nullptr, nullptr, [this, process,
503  encoding_to_type_sp,
504  log](const char *name,
505  const char *type,
506  lldb::addr_t offset_ptr,
507  uint64_t size) -> bool {
508  const bool for_expression = false;
509  const bool stop_loop = false;
510  LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}",
511  name, type, offset_ptr, size);
512  CompilerType ivar_type =
513  encoding_to_type_sp->RealizeType(type, for_expression);
514  if (ivar_type) {
515  LLDB_LOGV(log,
516  "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = "
517  "{3}, type_size = {4}",
518  name, type, offset_ptr, size,
519  ivar_type.GetByteSize(nullptr).getValueOr(0));
520  Scalar offset_scalar;
521  Status error;
522  const int offset_ptr_size = 4;
523  const bool is_signed = false;
524  size_t read = process->ReadScalarIntegerFromMemory(
525  offset_ptr, offset_ptr_size, is_signed, offset_scalar, error);
526  if (error.Success() && 4 == read) {
527  LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr,
528  offset_scalar.SInt());
529  m_ivars.push_back(
530  {ConstString(name), ivar_type, size, offset_scalar.SInt()});
531  } else
532  LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}",
533  offset_ptr, read);
534  }
535  return stop_loop;
536  });
537 }
538 
539 void ClassDescriptorV2::GetIVarInformation() {
540  m_ivars_storage.fill(m_runtime, *this);
541 }
An data extractor class.
Definition: DataExtractor.h:47
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
size_t ReadCStringFromMemory(lldb::addr_t vm_addr, char *cstr, size_t cstr_max_len, Status &error)
Read a NULL terminated C string from memory.
Definition: Process.cpp:2080
lldb::ByteOrder GetByteOrder() const
Definition: Process.cpp:3366
std::shared_ptr< ClassDescriptor > ClassDescriptorSP
EncodingToTypeSP GetEncodingToType() override
llvm::Optional< uint64_t > GetByteSize(ExecutionContextScope *exe_scope) const
Return the size of the type in bytes.
A subclass of DataBuffer that stores a data buffer on the heap.
static lldb::addr_t GetClassDataMask(Process *process)
#define LLDB_INVALID_ADDRESS
Invalid value definitions.
Definition: lldb-defines.h:85
uint64_t offset_t
Definition: lldb-types.h:87
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:57
uint64_t GetAddress_unchecked(lldb::offset_t *offset_ptr) const
uint32_t GetAddressByteSize() const
Definition: Process.cpp:3370
A plug-in interface definition class for debugging a process.
Definition: Process.h:353
bool Success() const
Test for success condition.
Definition: Status.cpp:287
bool 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) const override
uint64_t addr_t
Definition: lldb-types.h:83
A uniqued constant string class.
Definition: ConstString.h:38
bool Fail() const
Test for error condition.
Definition: Status.cpp:181
Definition: SBAddress.h:15
uint8_t * GetBytes() override
#define LIBLLDB_LOG_TYPES
Definition: Logging.h:33
uint32_t GetU32_unchecked(lldb::offset_t *offset_ptr) const
size_t ReadScalarIntegerFromMemory(lldb::addr_t addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Status &error)
Definition: Process.cpp:2302
lldb::offset_t GetByteSize() const override
uint64_t ReadUnsignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, uint64_t fail_value, Status &error)
Reads an unsigned integer of the specified byte size from process memory.
Definition: Process.cpp:2150
std::shared_ptr< EncodingToType > EncodingToTypeSP
#define LLDB_LOGV(log,...)
Definition: Log.h:216
An error handling class.
Definition: Status.h:44
virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error)
Read of memory from a process.
Definition: Process.cpp:1966