1 //===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++
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
8 //===----------------------------------------------------------------------===//
10 #include "AppleObjCRuntimeV1.h"
11 #include "AppleObjCDeclVendor.h"
12 #include "AppleObjCTrampolineHandler.h"
14 #include "clang/AST/Type.h"
16 #include "lldb/Breakpoint/BreakpointLocation.h"
17 #include "lldb/Core/Module.h"
18 #include "lldb/Core/PluginManager.h"
19 #include "lldb/Expression/FunctionCaller.h"
20 #include "lldb/Expression/UtilityFunction.h"
21 #include "lldb/Symbol/ClangASTContext.h"
22 #include "lldb/Symbol/Symbol.h"
23 #include "lldb/Target/ExecutionContext.h"
24 #include "lldb/Target/Process.h"
25 #include "lldb/Target/RegisterContext.h"
26 #include "lldb/Target/Target.h"
27 #include "lldb/Target/Thread.h"
28 #include "lldb/Utility/ConstString.h"
29 #include "lldb/Utility/Log.h"
30 #include "lldb/Utility/Scalar.h"
31 #include "lldb/Utility/Status.h"
32 #include "lldb/Utility/StreamString.h"
38 using namespace lldb_private;
40 char AppleObjCRuntimeV1::ID = 0;
42 AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)
43 : AppleObjCRuntime(process), m_hash_signature(),
44 m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {}
46 // for V1 runtime we just try to return a class name as that is the minimum
47 // level of support required for the data formatters to work
48 bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(
49 ValueObject &in_value, lldb::DynamicValueType use_dynamic,
50 TypeAndOrName &class_type_or_name, Address &address,
51 Value::ValueType &value_type) {
52 class_type_or_name.Clear();
53 value_type = Value::ValueType::eValueTypeScalar;
54 if (CouldHaveDynamicValue(in_value)) {
55 auto class_descriptor(GetClassDescriptor(in_value));
56 if (class_descriptor && class_descriptor->IsValid() &&
57 class_descriptor->GetClassName()) {
58 const addr_t object_ptr = in_value.GetPointerValue();
59 address.SetRawAddress(object_ptr);
60 class_type_or_name.SetName(class_descriptor->GetClassName());
63 return !class_type_or_name.IsEmpty();
67 lldb_private::LanguageRuntime *
68 AppleObjCRuntimeV1::CreateInstance(Process *process,
69 lldb::LanguageType language) {
70 // FIXME: This should be a MacOS or iOS process, and we need to look for the
71 // OBJC section to make
72 // sure we aren't using the V1 runtime.
73 if (language == eLanguageTypeObjC) {
74 ModuleSP objc_module_sp;
76 if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
77 ObjCRuntimeVersions::eAppleObjC_V1)
78 return new AppleObjCRuntimeV1(process);
85 void AppleObjCRuntimeV1::Initialize() {
86 PluginManager::RegisterPlugin(
87 GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1",
89 /*command_callback = */ nullptr, GetBreakpointExceptionPrecondition);
92 void AppleObjCRuntimeV1::Terminate() {
93 PluginManager::UnregisterPlugin(CreateInstance);
96 lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() {
97 static ConstString g_name("apple-objc-v1");
101 // PluginInterface protocol
102 ConstString AppleObjCRuntimeV1::GetPluginName() {
103 return GetPluginNameStatic();
106 uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; }
109 AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp,
111 BreakpointResolverSP resolver_sp;
114 resolver_sp = std::make_shared<BreakpointResolverName>(
115 bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
116 eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
118 // FIXME: don't do catch yet.
126 UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) {
127 std::unique_ptr<BufStruct> buf(new BufStruct);
129 int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents),
130 "struct __objc_class "
134 " struct __objc_class *isa; "
136 " struct __objc_class *super_class; "
138 " const char *name; "
140 " // rest of struct elided because unused "
146 "struct __objc_object "
150 " struct __objc_class *isa; "
158 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
162 " struct __objc_object *obj = (struct "
163 "__objc_object*)$__lldb_arg_obj; \n"
164 " if ($__lldb_arg_obj == (void *)0) "
166 " return; // nil is ok "
167 " (int)strlen(obj->isa->name); "
172 assert(strformatsize < (int)sizeof(buf->contents));
176 return GetTargetRef().GetUtilityFunctionForLanguage(
177 buf->contents, eLanguageTypeObjC, name, error);
180 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
181 ValueObject &isa_pointer) {
182 Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
185 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
186 ObjCISA isa, lldb::ProcessSP process_sp) {
187 Initialize(isa, process_sp);
190 void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(
191 ObjCISA isa, lldb::ProcessSP process_sp) {
192 if (!isa || !process_sp) {
201 m_isa = process_sp->ReadPointerFromMemory(isa, error);
208 uint32_t ptr_size = process_sp->GetAddressByteSize();
210 if (!IsPointerValid(m_isa, ptr_size)) {
215 m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);
222 if (!IsPointerValid(m_parent_isa, ptr_size, true)) {
227 lldb::addr_t name_ptr =
228 process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);
235 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
237 size_t count = process_sp->ReadCStringFromMemory(
238 name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);
246 m_name = ConstString((char *)buffer_sp->GetBytes());
248 m_name = ConstString();
250 m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(
251 m_isa + 5 * ptr_size, ptr_size, 0, error);
258 m_process_wp = lldb::ProcessWP(process_sp);
261 AppleObjCRuntime::ClassDescriptorSP
262 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {
264 return AppleObjCRuntime::ClassDescriptorSP();
265 ProcessSP process_sp = m_process_wp.lock();
267 return AppleObjCRuntime::ClassDescriptorSP();
268 return ObjCLanguageRuntime::ClassDescriptorSP(
269 new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));
272 AppleObjCRuntime::ClassDescriptorSP
273 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
274 return ClassDescriptorSP();
277 bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(
278 std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
279 std::function<bool(const char *, const char *)> const &instance_method_func,
280 std::function<bool(const char *, const char *)> const &class_method_func,
281 std::function<bool(const char *, const char *, lldb::addr_t,
282 uint64_t)> const &ivar_func) const {
286 lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {
290 lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() {
291 if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
292 ModuleSP objc_module_sp(GetObjCModule());
295 return LLDB_INVALID_ADDRESS;
297 static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
299 const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
300 g_objc_debug_class_hash, lldb::eSymbolTypeData);
301 if (symbol && symbol->ValueIsAddress()) {
302 Process *process = GetProcess();
305 lldb::addr_t objc_debug_class_hash_addr =
306 symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
308 if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {
310 lldb::addr_t objc_debug_class_hash_ptr =
311 process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
312 if (objc_debug_class_hash_ptr != 0 &&
313 objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {
314 m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
320 return m_isa_hash_table_ptr;
323 void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {
324 // TODO: implement HashTableSignature...
325 Process *process = GetProcess();
328 // Update the process stop ID that indicates the last time we updated the
329 // map, whether it was successful or not.
330 m_isa_to_descriptor_stop_id = process->GetStopID();
332 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
334 ProcessSP process_sp = process->shared_from_this();
336 ModuleSP objc_module_sp(GetObjCModule());
341 uint32_t isa_count = 0;
343 lldb::addr_t hash_table_ptr = GetISAHashTablePointer();
344 if (hash_table_ptr != LLDB_INVALID_ADDRESS) {
345 // Read the NXHashTable struct:
348 // const NXHashTablePrototype *prototype;
350 // unsigned nbBuckets;
356 DataBufferHeap buffer(1024, 0);
357 if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==
359 const uint32_t addr_size = m_process->GetAddressByteSize();
360 const ByteOrder byte_order = m_process->GetByteOrder();
361 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,
363 lldb::offset_t offset = addr_size; // Skip prototype
364 const uint32_t count = data.GetU32(&offset);
365 const uint32_t num_buckets = data.GetU32(&offset);
366 const addr_t buckets_ptr = data.GetPointer(&offset);
367 if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {
368 m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);
370 const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
371 buffer.SetByteSize(data_size);
373 if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,
374 error) == data_size) {
375 data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
377 for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;
379 const uint32_t bucket_isa_count = data.GetU32(&offset);
380 const lldb::addr_t bucket_data = data.GetU32(&offset);
382 if (bucket_isa_count == 0)
385 isa_count += bucket_isa_count;
388 if (bucket_isa_count == 1) {
389 // When we only have one entry in the bucket, the bucket data
393 if (!ISAIsCached(isa)) {
394 ClassDescriptorSP descriptor_sp(
395 new ClassDescriptorV1(isa, process_sp));
397 if (log && log->GetVerbose())
398 log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
399 " from _objc_debug_class_hash to "
400 "isa->descriptor cache",
403 AddClass(isa, descriptor_sp);
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);
414 if (isa && isa != LLDB_INVALID_ADDRESS) {
415 if (!ISAIsCached(isa)) {
416 ClassDescriptorSP descriptor_sp(
417 new ClassDescriptorV1(isa, process_sp));
419 if (log && log->GetVerbose())
421 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
422 " from _objc_debug_class_hash to isa->descriptor "
426 AddClass(isa, descriptor_sp);
437 m_isa_to_descriptor_stop_id = UINT32_MAX;
441 DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {