LLDB  mainline
AppleObjCRuntimeV1.cpp
Go to the documentation of this file.
1 //===-- AppleObjCRuntimeV1.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 "AppleObjCRuntimeV1.h"
10 #include "AppleObjCDeclVendor.h"
12 
13 #include "clang/AST/Type.h"
14 
17 #include "lldb/Core/Module.h"
21 #include "lldb/Symbol/Symbol.h"
23 #include "lldb/Target/Process.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Target/Thread.h"
28 #include "lldb/Utility/Log.h"
29 #include "lldb/Utility/Scalar.h"
30 #include "lldb/Utility/Status.h"
32 
33 #include <memory>
34 #include <vector>
35 
36 using namespace lldb;
37 using namespace lldb_private;
38 
39 char AppleObjCRuntimeV1::ID = 0;
40 
41 AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)
42  : AppleObjCRuntime(process), m_hash_signature(),
43  m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {}
44 
45 // for V1 runtime we just try to return a class name as that is the minimum
46 // level of support required for the data formatters to work
48  ValueObject &in_value, lldb::DynamicValueType use_dynamic,
49  TypeAndOrName &class_type_or_name, Address &address,
50  Value::ValueType &value_type) {
51  class_type_or_name.Clear();
52  value_type = Value::ValueType::eValueTypeScalar;
53  if (CouldHaveDynamicValue(in_value)) {
54  auto class_descriptor(GetClassDescriptor(in_value));
55  if (class_descriptor && class_descriptor->IsValid() &&
56  class_descriptor->GetClassName()) {
57  const addr_t object_ptr = in_value.GetPointerValue();
58  address.SetRawAddress(object_ptr);
59  class_type_or_name.SetName(class_descriptor->GetClassName());
60  }
61  }
62  return !class_type_or_name.IsEmpty();
63 }
64 
65 // Static Functions
68  lldb::LanguageType language) {
69  // FIXME: This should be a MacOS or iOS process, and we need to look for the
70  // OBJC section to make
71  // sure we aren't using the V1 runtime.
72  if (language == eLanguageTypeObjC) {
73  ModuleSP objc_module_sp;
74 
75  if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
77  return new AppleObjCRuntimeV1(process);
78  else
79  return nullptr;
80  } else
81  return nullptr;
82 }
83 
86  GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1",
88  /*command_callback = */ nullptr, GetBreakpointExceptionPrecondition);
89 }
90 
93 }
94 
96  static ConstString g_name("apple-objc-v1");
97  return g_name;
98 }
99 
100 // PluginInterface protocol
102  return GetPluginNameStatic();
103 }
104 
106 
107 BreakpointResolverSP
109  bool catch_bp, bool throw_bp) {
110  BreakpointResolverSP resolver_sp;
111 
112  if (throw_bp)
113  resolver_sp = std::make_shared<BreakpointResolverName>(
114  bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
115  eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
116  eLazyBoolNo);
117  // FIXME: don't do catch yet.
118  return resolver_sp;
119 }
120 
121 struct BufStruct {
122  char contents[2048];
123 };
124 
126  std::unique_ptr<BufStruct> buf(new BufStruct);
127 
128  int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents),
129  "struct __objc_class "
130  " \n"
131  "{ "
132  " \n"
133  " struct __objc_class *isa; "
134  " \n"
135  " struct __objc_class *super_class; "
136  " \n"
137  " const char *name; "
138  " \n"
139  " // rest of struct elided because unused "
140  " \n"
141  "}; "
142  " \n"
143  " "
144  " \n"
145  "struct __objc_object "
146  " \n"
147  "{ "
148  " \n"
149  " struct __objc_class *isa; "
150  " \n"
151  "}; "
152  " \n"
153  " "
154  " \n"
155  "extern \"C\" void "
156  " \n"
157  "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
158  " \n"
159  "{ "
160  " \n"
161  " struct __objc_object *obj = (struct "
162  "__objc_object*)$__lldb_arg_obj; \n"
163  " if ($__lldb_arg_obj == (void *)0) "
164  " \n"
165  " return; // nil is ok "
166  " (int)strlen(obj->isa->name); "
167  " \n"
168  "} "
169  " \n",
170  name);
171  assert(strformatsize < (int)sizeof(buf->contents));
172  (void)strformatsize;
173 
174  Status error;
176  buf->contents, eLanguageTypeObjC, name, error);
177 }
178 
180  ValueObject &isa_pointer) {
181  Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
182 }
183 
185  ObjCISA isa, lldb::ProcessSP process_sp) {
186  Initialize(isa, process_sp);
187 }
188 
190  ObjCISA isa, lldb::ProcessSP process_sp) {
191  if (!isa || !process_sp) {
192  m_valid = false;
193  return;
194  }
195 
196  m_valid = true;
197 
198  Status error;
199 
200  m_isa = process_sp->ReadPointerFromMemory(isa, error);
201 
202  if (error.Fail()) {
203  m_valid = false;
204  return;
205  }
206 
207  uint32_t ptr_size = process_sp->GetAddressByteSize();
208 
209  if (!IsPointerValid(m_isa, ptr_size)) {
210  m_valid = false;
211  return;
212  }
213 
214  m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);
215 
216  if (error.Fail()) {
217  m_valid = false;
218  return;
219  }
220 
221  if (!IsPointerValid(m_parent_isa, ptr_size, true)) {
222  m_valid = false;
223  return;
224  }
225 
226  lldb::addr_t name_ptr =
227  process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);
228 
229  if (error.Fail()) {
230  m_valid = false;
231  return;
232  }
233 
234  lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
235 
236  size_t count = process_sp->ReadCStringFromMemory(
237  name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);
238 
239  if (error.Fail()) {
240  m_valid = false;
241  return;
242  }
243 
244  if (count)
245  m_name = ConstString((char *)buffer_sp->GetBytes());
246  else
247  m_name = ConstString();
248 
249  m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(
250  m_isa + 5 * ptr_size, ptr_size, 0, error);
251 
252  if (error.Fail()) {
253  m_valid = false;
254  return;
255  }
256 
257  m_process_wp = lldb::ProcessWP(process_sp);
258 }
259 
262  if (!m_valid)
264  ProcessSP process_sp = m_process_wp.lock();
265  if (!process_sp)
268  new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));
269 }
270 
273  return ClassDescriptorSP();
274 }
275 
277  std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
278  std::function<bool(const char *, const char *)> const &instance_method_func,
279  std::function<bool(const char *, const char *)> const &class_method_func,
280  std::function<bool(const char *, const char *, lldb::addr_t,
281  uint64_t)> const &ivar_func) const {
282  return false;
283 }
284 
286  return 0;
287 }
288 
291  ModuleSP objc_module_sp(GetObjCModule());
292 
293  if (!objc_module_sp)
294  return LLDB_INVALID_ADDRESS;
295 
296  static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
297 
298  const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
299  g_objc_debug_class_hash, lldb::eSymbolTypeData);
300  if (symbol && symbol->ValueIsAddress()) {
301  Process *process = GetProcess();
302  if (process) {
303 
304  lldb::addr_t objc_debug_class_hash_addr =
305  symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
306 
307  if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {
308  Status error;
309  lldb::addr_t objc_debug_class_hash_ptr =
310  process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
311  if (objc_debug_class_hash_ptr != 0 &&
312  objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {
313  m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
314  }
315  }
316  }
317  }
318  }
319  return m_isa_hash_table_ptr;
320 }
321 
323  // TODO: implement HashTableSignature...
324  Process *process = GetProcess();
325 
326  if (process) {
327  // Update the process stop ID that indicates the last time we updated the
328  // map, whether it was successful or not.
330 
332 
333  ProcessSP process_sp = process->shared_from_this();
334 
335  ModuleSP objc_module_sp(GetObjCModule());
336 
337  if (!objc_module_sp)
338  return;
339 
340  uint32_t isa_count = 0;
341 
342  lldb::addr_t hash_table_ptr = GetISAHashTablePointer();
343  if (hash_table_ptr != LLDB_INVALID_ADDRESS) {
344  // Read the NXHashTable struct:
345  //
346  // typedef struct {
347  // const NXHashTablePrototype *prototype;
348  // unsigned count;
349  // unsigned nbBuckets;
350  // void *buckets;
351  // const void *info;
352  // } NXHashTable;
353 
354  Status error;
355  DataBufferHeap buffer(1024, 0);
356  if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==
357  20) {
358  const uint32_t addr_size = m_process->GetAddressByteSize();
359  const ByteOrder byte_order = m_process->GetByteOrder();
360  DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,
361  addr_size);
362  lldb::offset_t offset = addr_size; // Skip prototype
363  const uint32_t count = data.GetU32(&offset);
364  const uint32_t num_buckets = data.GetU32(&offset);
365  const addr_t buckets_ptr = data.GetAddress(&offset);
366  if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {
367  m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);
368 
369  const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
370  buffer.SetByteSize(data_size);
371 
372  if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,
373  error) == data_size) {
374  data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
375  offset = 0;
376  for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;
377  ++bucket_idx) {
378  const uint32_t bucket_isa_count = data.GetU32(&offset);
379  const lldb::addr_t bucket_data = data.GetU32(&offset);
380 
381  if (bucket_isa_count == 0)
382  continue;
383 
384  isa_count += bucket_isa_count;
385 
386  ObjCISA isa;
387  if (bucket_isa_count == 1) {
388  // When we only have one entry in the bucket, the bucket data
389  // is the "isa"
390  isa = bucket_data;
391  if (isa) {
392  if (!ISAIsCached(isa)) {
393  ClassDescriptorSP descriptor_sp(
394  new ClassDescriptorV1(isa, process_sp));
395 
396  if (log && log->GetVerbose())
397  LLDB_LOGF(log,
398  "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
399  " from _objc_debug_class_hash to "
400  "isa->descriptor cache",
401  isa);
402 
403  AddClass(isa, descriptor_sp);
404  }
405  }
406  } else {
407  // When we have more than one entry in the bucket, the bucket
408  // data is a pointer to an array of "isa" values
409  addr_t isa_addr = bucket_data;
410  for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count;
411  ++isa_idx, isa_addr += addr_size) {
412  isa = m_process->ReadPointerFromMemory(isa_addr, error);
413 
414  if (isa && isa != LLDB_INVALID_ADDRESS) {
415  if (!ISAIsCached(isa)) {
416  ClassDescriptorSP descriptor_sp(
417  new ClassDescriptorV1(isa, process_sp));
418 
419  if (log && log->GetVerbose())
420  LLDB_LOGF(
421  log,
422  "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
423  " from _objc_debug_class_hash to isa->descriptor "
424  "cache",
425  isa);
426 
427  AddClass(isa, descriptor_sp);
428  }
429  }
430  }
431  }
432  }
433  }
434  }
435  }
436  }
437  } else {
439  }
440 }
441 
443  return nullptr;
444 }
Address & GetAddressRef()
Definition: Symbol.h:57
bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp)
An data extractor class.
Definition: DataExtractor.h:46
bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, Value::ValueType &value_type) override
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
lldb::BreakpointResolverSP CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp, bool throw_bp) override
#define LIBLLDB_LOG_PROCESS
Definition: Logging.h:15
A class that represents a running process on the host machine.
static lldb::BreakpointPreconditionSP GetBreakpointExceptionPrecondition(lldb::LanguageType language, bool throw_bp)
Sometimes you can find the name of the type corresponding to an object, but we don&#39;t have debug infor...
Definition: Type.h:381
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition: Address.cpp:310
void Initialize(ObjCISA isa, lldb::ProcessSP process_sp)
DeclVendor * GetDeclVendor() override
static bool RegisterPlugin(ConstString name, const char *description, ABICreateInstance create_callback)
lldb::offset_t SetByteSize(lldb::offset_t byte_size)
Set the number of bytes in the data buffer.
static std::tuple< FileSpec, ConstString > GetExceptionThrowLocation()
lldb::ByteOrder GetByteOrder() const
Definition: Process.cpp:3428
std::shared_ptr< ClassDescriptor > ClassDescriptorSP
void SetName(ConstString type_name)
Definition: Type.cpp:758
UtilityFunction * CreateObjectChecker(const char *) override
lldb::addr_t GetPointerValue(AddressType *address_type=nullptr)
A subclass of DataBuffer that stores a data buffer on the heap.
static lldb_private::LanguageRuntime * CreateInstance(Process *process, lldb::LanguageType language)
#define UINT32_MAX
Definition: lldb-defines.h:31
#define LLDB_INVALID_ADDRESS
Invalid value definitions.
Definition: lldb-defines.h:85
LanguageType
Programming language type.
uint32_t GetStopID() const
Definition: Process.h:1358
uint64_t offset_t
Definition: lldb-types.h:87
static llvm::raw_ostream & error(Stream &strm)
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:58
ByteOrder
Byte ordering definitions.
uint32_t GetAddressByteSize() const
Definition: Process.cpp:3432
bool ValueIsAddress() const
Definition: Symbol.cpp:118
A plug-in interface definition class for debugging a process.
Definition: Process.h:362
bool CouldHaveDynamicValue(ValueObject &in_value) override
bool GetVerbose() const
Definition: Log.cpp:275
static char ID
void SetRawAddress(lldb::addr_t addr)
Definition: Address.h:431
A section + offset based address class.
Definition: Address.h:59
bool IsEmpty() const
Definition: Type.cpp:780
lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error)
Definition: Process.cpp:2227
#define LLDB_LOGF(log,...)
Definition: Log.h:249
Target & GetTarget()
Get the target object pointer for this module.
Definition: Process.h:1210
uint64_t addr_t
Definition: lldb-types.h:83
static bool UnregisterPlugin(ABICreateInstance create_callback)
A uniqued constant string class.
Definition: ConstString.h:40
Unknown or invalid language value.
bool Fail() const
Test for error condition.
Definition: Status.cpp:182
lldb::ProcessSP GetProcessSP() const
Definition: ValueObject.h:339
virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success=nullptr)
Definition: SBAddress.h:15
bool NeedsUpdate(uint32_t count, uint32_t num_buckets, lldb::addr_t buckets_ptr)
static lldb_private::ConstString GetPluginNameStatic()
uint8_t * GetBytes() override
virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value)
void UpdateSignature(uint32_t count, uint32_t num_buckets, lldb::addr_t buckets_ptr)
static ObjCRuntimeVersions GetObjCVersion(Process *process, lldb::ModuleSP &objc_module_sp)
"lldb/Expression/UtilityFunction.h" Encapsulates a bit of source code that provides a function that i...
lldb::offset_t GetByteSize() const override
UtilityFunction * GetUtilityFunctionForLanguage(const char *expr, lldb::LanguageType language, const char *name, Status &error)
Definition: Target.cpp:2240
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:2021