1 //===-- AppleObjCRuntimeV1.cpp --------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "AppleObjCRuntimeV1.h"
10 #include "AppleObjCDeclVendor.h"
11 #include "AppleObjCTrampolineHandler.h"
13 #include "clang/AST/Type.h"
15 #include "Plugins/TypeSystem/Clang/TypeSystemClang.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/Symbol.h"
22 #include "lldb/Target/ExecutionContext.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/RegisterContext.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Target/Thread.h"
27 #include "lldb/Utility/ConstString.h"
28 #include "lldb/Utility/LLDBLog.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::Scalar
;
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
);
97 AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP
&bkpt
,
98 bool catch_bp
, bool throw_bp
) {
99 BreakpointResolverSP resolver_sp
;
102 resolver_sp
= std::make_shared
<BreakpointResolverName
>(
103 bkpt
, std::get
<1>(GetExceptionThrowLocation()).AsCString(),
104 eFunctionNameTypeBase
, eLanguageTypeUnknown
, Breakpoint::Exact
, 0,
106 // FIXME: don't do catch yet.
114 llvm::Expected
<std::unique_ptr
<UtilityFunction
>>
115 AppleObjCRuntimeV1::CreateObjectChecker(std::string name
,
116 ExecutionContext
&exe_ctx
) {
117 std::unique_ptr
<BufStruct
> buf(new BufStruct
);
120 snprintf(&buf
->contents
[0], sizeof(buf
->contents
),
121 "struct __objc_class "
125 " struct __objc_class *isa; "
127 " struct __objc_class *super_class; "
129 " const char *name; "
131 " // rest of struct elided because unused "
137 "struct __objc_object "
141 " struct __objc_class *isa; "
149 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
153 " struct __objc_object *obj = (struct "
154 "__objc_object*)$__lldb_arg_obj; \n"
155 " if ($__lldb_arg_obj == (void *)0) "
157 " return; // nil is ok "
158 " (int)strlen(obj->isa->name); "
163 assert(strformatsize
< (int)sizeof(buf
->contents
));
164 UNUSED_IF_ASSERT_DISABLED(strformatsize
);
166 return GetTargetRef().CreateUtilityFunction(buf
->contents
, std::move(name
),
167 eLanguageTypeC
, exe_ctx
);
170 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
171 ValueObject
&isa_pointer
) {
172 Initialize(isa_pointer
.GetValueAsUnsigned(0), isa_pointer
.GetProcessSP());
175 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
176 ObjCISA isa
, lldb::ProcessSP process_sp
) {
177 Initialize(isa
, process_sp
);
180 void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(
181 ObjCISA isa
, lldb::ProcessSP process_sp
) {
182 if (!isa
|| !process_sp
) {
191 m_isa
= process_sp
->ReadPointerFromMemory(isa
, error
);
198 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
200 if (!IsPointerValid(m_isa
, ptr_size
)) {
205 m_parent_isa
= process_sp
->ReadPointerFromMemory(m_isa
+ ptr_size
, error
);
212 if (!IsPointerValid(m_parent_isa
, ptr_size
, true)) {
217 lldb::addr_t name_ptr
=
218 process_sp
->ReadPointerFromMemory(m_isa
+ 2 * ptr_size
, error
);
225 lldb::WritableDataBufferSP
buffer_sp(new DataBufferHeap(1024, 0));
227 size_t count
= process_sp
->ReadCStringFromMemory(
228 name_ptr
, (char *)buffer_sp
->GetBytes(), 1024, error
);
236 m_name
= ConstString(reinterpret_cast<const char *>(buffer_sp
->GetBytes()));
238 m_name
= ConstString();
240 m_instance_size
= process_sp
->ReadUnsignedIntegerFromMemory(
241 m_isa
+ 5 * ptr_size
, ptr_size
, 0, error
);
248 m_process_wp
= lldb::ProcessWP(process_sp
);
251 AppleObjCRuntime::ClassDescriptorSP
252 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {
254 return AppleObjCRuntime::ClassDescriptorSP();
255 ProcessSP process_sp
= m_process_wp
.lock();
257 return AppleObjCRuntime::ClassDescriptorSP();
258 return ObjCLanguageRuntime::ClassDescriptorSP(
259 new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa
, process_sp
));
262 AppleObjCRuntime::ClassDescriptorSP
263 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
264 return ClassDescriptorSP();
267 bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(
268 std::function
<void(ObjCLanguageRuntime::ObjCISA
)> const &superclass_func
,
269 std::function
<bool(const char *, const char *)> const &instance_method_func
,
270 std::function
<bool(const char *, const char *)> const &class_method_func
,
271 std::function
<bool(const char *, const char *, lldb::addr_t
,
272 uint64_t)> const &ivar_func
) const {
276 lldb::addr_t
AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {
280 lldb::addr_t
AppleObjCRuntimeV1::GetISAHashTablePointer() {
281 if (m_isa_hash_table_ptr
== LLDB_INVALID_ADDRESS
) {
282 ModuleSP
objc_module_sp(GetObjCModule());
285 return LLDB_INVALID_ADDRESS
;
287 static ConstString
g_objc_debug_class_hash("_objc_debug_class_hash");
289 const Symbol
*symbol
= objc_module_sp
->FindFirstSymbolWithNameAndType(
290 g_objc_debug_class_hash
, lldb::eSymbolTypeData
);
291 if (symbol
&& symbol
->ValueIsAddress()) {
292 Process
*process
= GetProcess();
295 lldb::addr_t objc_debug_class_hash_addr
=
296 symbol
->GetAddressRef().GetLoadAddress(&process
->GetTarget());
298 if (objc_debug_class_hash_addr
!= LLDB_INVALID_ADDRESS
) {
300 lldb::addr_t objc_debug_class_hash_ptr
=
301 process
->ReadPointerFromMemory(objc_debug_class_hash_addr
, error
);
302 if (objc_debug_class_hash_ptr
!= 0 &&
303 objc_debug_class_hash_ptr
!= LLDB_INVALID_ADDRESS
) {
304 m_isa_hash_table_ptr
= objc_debug_class_hash_ptr
;
310 return m_isa_hash_table_ptr
;
313 void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {
314 // TODO: implement HashTableSignature...
315 Process
*process
= GetProcess();
318 // Update the process stop ID that indicates the last time we updated the
319 // map, whether it was successful or not.
320 m_isa_to_descriptor_stop_id
= process
->GetStopID();
322 Log
*log
= GetLog(LLDBLog::Process
);
324 ProcessSP process_sp
= process
->shared_from_this();
326 ModuleSP
objc_module_sp(GetObjCModule());
331 lldb::addr_t hash_table_ptr
= GetISAHashTablePointer();
332 if (hash_table_ptr
!= LLDB_INVALID_ADDRESS
) {
333 // Read the NXHashTable struct:
336 // const NXHashTablePrototype *prototype;
338 // unsigned nbBuckets;
344 DataBufferHeap
buffer(1024, 0);
345 if (process
->ReadMemory(hash_table_ptr
, buffer
.GetBytes(), 20, error
) ==
347 const uint32_t addr_size
= m_process
->GetAddressByteSize();
348 const ByteOrder byte_order
= m_process
->GetByteOrder();
349 DataExtractor
data(buffer
.GetBytes(), buffer
.GetByteSize(), byte_order
,
351 lldb::offset_t offset
= addr_size
; // Skip prototype
352 const uint32_t count
= data
.GetU32(&offset
);
353 const uint32_t num_buckets
= data
.GetU32(&offset
);
354 const addr_t buckets_ptr
= data
.GetAddress(&offset
);
355 if (m_hash_signature
.NeedsUpdate(count
, num_buckets
, buckets_ptr
)) {
356 m_hash_signature
.UpdateSignature(count
, num_buckets
, buckets_ptr
);
358 const uint32_t data_size
= num_buckets
* 2 * sizeof(uint32_t);
359 buffer
.SetByteSize(data_size
);
361 if (process
->ReadMemory(buckets_ptr
, buffer
.GetBytes(), data_size
,
362 error
) == data_size
) {
363 data
.SetData(buffer
.GetBytes(), buffer
.GetByteSize(), byte_order
);
365 for (uint32_t bucket_idx
= 0; bucket_idx
< num_buckets
;
367 const uint32_t bucket_isa_count
= data
.GetU32(&offset
);
368 const lldb::addr_t bucket_data
= data
.GetU32(&offset
);
370 if (bucket_isa_count
== 0)
374 if (bucket_isa_count
== 1) {
375 // When we only have one entry in the bucket, the bucket data
379 if (!ISAIsCached(isa
)) {
380 ClassDescriptorSP
descriptor_sp(
381 new ClassDescriptorV1(isa
, process_sp
));
383 if (log
&& log
->GetVerbose())
385 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
386 " from _objc_debug_class_hash to "
387 "isa->descriptor cache",
390 AddClass(isa
, descriptor_sp
);
394 // When we have more than one entry in the bucket, the bucket
395 // data is a pointer to an array of "isa" values
396 addr_t isa_addr
= bucket_data
;
397 for (uint32_t isa_idx
= 0; isa_idx
< bucket_isa_count
;
398 ++isa_idx
, isa_addr
+= addr_size
) {
399 isa
= m_process
->ReadPointerFromMemory(isa_addr
, error
);
401 if (isa
&& isa
!= LLDB_INVALID_ADDRESS
) {
402 if (!ISAIsCached(isa
)) {
403 ClassDescriptorSP
descriptor_sp(
404 new ClassDescriptorV1(isa
, process_sp
));
406 if (log
&& log
->GetVerbose())
409 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
410 " from _objc_debug_class_hash to isa->descriptor "
414 AddClass(isa
, descriptor_sp
);
425 m_isa_to_descriptor_stop_id
= UINT32_MAX
;
429 DeclVendor
*AppleObjCRuntimeV1::GetDeclVendor() {