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"
11 #include "AppleObjCTrampolineHandler.h"
12
13 #include "clang/AST/Type.h"
14
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/Log.h"
29 #include "lldb/Utility/Scalar.h"
30 #include "lldb/Utility/Status.h"
31 #include "lldb/Utility/StreamString.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
AppleObjCRuntimeV1(Process * process)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
GetDynamicTypeAndAddress(ValueObject & in_value,lldb::DynamicValueType use_dynamic,TypeAndOrName & class_type_or_name,Address & address,Value::ValueType & value_type)47 bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(
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
66 lldb_private::LanguageRuntime *
CreateInstance(Process * process,lldb::LanguageType language)67 AppleObjCRuntimeV1::CreateInstance(Process *process,
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) ==
76 ObjCRuntimeVersions::eAppleObjC_V1)
77 return new AppleObjCRuntimeV1(process);
78 else
79 return nullptr;
80 } else
81 return nullptr;
82 }
83
Initialize()84 void AppleObjCRuntimeV1::Initialize() {
85 PluginManager::RegisterPlugin(
86 GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1",
87 CreateInstance,
88 /*command_callback = */ nullptr, GetBreakpointExceptionPrecondition);
89 }
90
Terminate()91 void AppleObjCRuntimeV1::Terminate() {
92 PluginManager::UnregisterPlugin(CreateInstance);
93 }
94
GetPluginNameStatic()95 lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() {
96 static ConstString g_name("apple-objc-v1");
97 return g_name;
98 }
99
100 // PluginInterface protocol
GetPluginName()101 ConstString AppleObjCRuntimeV1::GetPluginName() {
102 return GetPluginNameStatic();
103 }
104
GetPluginVersion()105 uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; }
106
107 BreakpointResolverSP
CreateExceptionResolver(const BreakpointSP & bkpt,bool catch_bp,bool throw_bp)108 AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP &bkpt,
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
125 llvm::Expected<std::unique_ptr<UtilityFunction>>
CreateObjectChecker(std::string name,ExecutionContext & exe_ctx)126 AppleObjCRuntimeV1::CreateObjectChecker(std::string name,
127 ExecutionContext &exe_ctx) {
128 std::unique_ptr<BufStruct> buf(new BufStruct);
129
130 int strformatsize =
131 snprintf(&buf->contents[0], sizeof(buf->contents),
132 "struct __objc_class "
133 " \n"
134 "{ "
135 " \n"
136 " struct __objc_class *isa; "
137 " \n"
138 " struct __objc_class *super_class; "
139 " \n"
140 " const char *name; "
141 " \n"
142 " // rest of struct elided because unused "
143 " \n"
144 "}; "
145 " \n"
146 " "
147 " \n"
148 "struct __objc_object "
149 " \n"
150 "{ "
151 " \n"
152 " struct __objc_class *isa; "
153 " \n"
154 "}; "
155 " \n"
156 " "
157 " \n"
158 "extern \"C\" void "
159 " \n"
160 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
161 " \n"
162 "{ "
163 " \n"
164 " struct __objc_object *obj = (struct "
165 "__objc_object*)$__lldb_arg_obj; \n"
166 " if ($__lldb_arg_obj == (void *)0) "
167 " \n"
168 " return; // nil is ok "
169 " (int)strlen(obj->isa->name); "
170 " \n"
171 "} "
172 " \n",
173 name.c_str());
174 assert(strformatsize < (int)sizeof(buf->contents));
175 (void)strformatsize;
176
177 return GetTargetRef().CreateUtilityFunction(buf->contents, std::move(name),
178 eLanguageTypeC, exe_ctx);
179 }
180
ClassDescriptorV1(ValueObject & isa_pointer)181 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
182 ValueObject &isa_pointer) {
183 Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
184 }
185
ClassDescriptorV1(ObjCISA isa,lldb::ProcessSP process_sp)186 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
187 ObjCISA isa, lldb::ProcessSP process_sp) {
188 Initialize(isa, process_sp);
189 }
190
Initialize(ObjCISA isa,lldb::ProcessSP process_sp)191 void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(
192 ObjCISA isa, lldb::ProcessSP process_sp) {
193 if (!isa || !process_sp) {
194 m_valid = false;
195 return;
196 }
197
198 m_valid = true;
199
200 Status error;
201
202 m_isa = process_sp->ReadPointerFromMemory(isa, error);
203
204 if (error.Fail()) {
205 m_valid = false;
206 return;
207 }
208
209 uint32_t ptr_size = process_sp->GetAddressByteSize();
210
211 if (!IsPointerValid(m_isa, ptr_size)) {
212 m_valid = false;
213 return;
214 }
215
216 m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);
217
218 if (error.Fail()) {
219 m_valid = false;
220 return;
221 }
222
223 if (!IsPointerValid(m_parent_isa, ptr_size, true)) {
224 m_valid = false;
225 return;
226 }
227
228 lldb::addr_t name_ptr =
229 process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);
230
231 if (error.Fail()) {
232 m_valid = false;
233 return;
234 }
235
236 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
237
238 size_t count = process_sp->ReadCStringFromMemory(
239 name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);
240
241 if (error.Fail()) {
242 m_valid = false;
243 return;
244 }
245
246 if (count)
247 m_name = ConstString((char *)buffer_sp->GetBytes());
248 else
249 m_name = ConstString();
250
251 m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(
252 m_isa + 5 * ptr_size, ptr_size, 0, error);
253
254 if (error.Fail()) {
255 m_valid = false;
256 return;
257 }
258
259 m_process_wp = lldb::ProcessWP(process_sp);
260 }
261
262 AppleObjCRuntime::ClassDescriptorSP
GetSuperclass()263 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {
264 if (!m_valid)
265 return AppleObjCRuntime::ClassDescriptorSP();
266 ProcessSP process_sp = m_process_wp.lock();
267 if (!process_sp)
268 return AppleObjCRuntime::ClassDescriptorSP();
269 return ObjCLanguageRuntime::ClassDescriptorSP(
270 new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));
271 }
272
273 AppleObjCRuntime::ClassDescriptorSP
GetMetaclass() const274 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
275 return ClassDescriptorSP();
276 }
277
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) const278 bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(
279 std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
280 std::function<bool(const char *, const char *)> const &instance_method_func,
281 std::function<bool(const char *, const char *)> const &class_method_func,
282 std::function<bool(const char *, const char *, lldb::addr_t,
283 uint64_t)> const &ivar_func) const {
284 return false;
285 }
286
GetTaggedPointerObfuscator()287 lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {
288 return 0;
289 }
290
GetISAHashTablePointer()291 lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() {
292 if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
293 ModuleSP objc_module_sp(GetObjCModule());
294
295 if (!objc_module_sp)
296 return LLDB_INVALID_ADDRESS;
297
298 static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
299
300 const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
301 g_objc_debug_class_hash, lldb::eSymbolTypeData);
302 if (symbol && symbol->ValueIsAddress()) {
303 Process *process = GetProcess();
304 if (process) {
305
306 lldb::addr_t objc_debug_class_hash_addr =
307 symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
308
309 if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {
310 Status error;
311 lldb::addr_t objc_debug_class_hash_ptr =
312 process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
313 if (objc_debug_class_hash_ptr != 0 &&
314 objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {
315 m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
316 }
317 }
318 }
319 }
320 }
321 return m_isa_hash_table_ptr;
322 }
323
UpdateISAToDescriptorMapIfNeeded()324 void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {
325 // TODO: implement HashTableSignature...
326 Process *process = GetProcess();
327
328 if (process) {
329 // Update the process stop ID that indicates the last time we updated the
330 // map, whether it was successful or not.
331 m_isa_to_descriptor_stop_id = process->GetStopID();
332
333 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
334
335 ProcessSP process_sp = process->shared_from_this();
336
337 ModuleSP objc_module_sp(GetObjCModule());
338
339 if (!objc_module_sp)
340 return;
341
342 uint32_t isa_count = 0;
343
344 lldb::addr_t hash_table_ptr = GetISAHashTablePointer();
345 if (hash_table_ptr != LLDB_INVALID_ADDRESS) {
346 // Read the NXHashTable struct:
347 //
348 // typedef struct {
349 // const NXHashTablePrototype *prototype;
350 // unsigned count;
351 // unsigned nbBuckets;
352 // void *buckets;
353 // const void *info;
354 // } NXHashTable;
355
356 Status error;
357 DataBufferHeap buffer(1024, 0);
358 if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==
359 20) {
360 const uint32_t addr_size = m_process->GetAddressByteSize();
361 const ByteOrder byte_order = m_process->GetByteOrder();
362 DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,
363 addr_size);
364 lldb::offset_t offset = addr_size; // Skip prototype
365 const uint32_t count = data.GetU32(&offset);
366 const uint32_t num_buckets = data.GetU32(&offset);
367 const addr_t buckets_ptr = data.GetAddress(&offset);
368 if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {
369 m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);
370
371 const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
372 buffer.SetByteSize(data_size);
373
374 if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,
375 error) == data_size) {
376 data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
377 offset = 0;
378 for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;
379 ++bucket_idx) {
380 const uint32_t bucket_isa_count = data.GetU32(&offset);
381 const lldb::addr_t bucket_data = data.GetU32(&offset);
382
383 if (bucket_isa_count == 0)
384 continue;
385
386 isa_count += bucket_isa_count;
387
388 ObjCISA isa;
389 if (bucket_isa_count == 1) {
390 // When we only have one entry in the bucket, the bucket data
391 // is the "isa"
392 isa = bucket_data;
393 if (isa) {
394 if (!ISAIsCached(isa)) {
395 ClassDescriptorSP descriptor_sp(
396 new ClassDescriptorV1(isa, process_sp));
397
398 if (log && log->GetVerbose())
399 LLDB_LOGF(log,
400 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
401 " from _objc_debug_class_hash to "
402 "isa->descriptor cache",
403 isa);
404
405 AddClass(isa, descriptor_sp);
406 }
407 }
408 } else {
409 // When we have more than one entry in the bucket, the bucket
410 // data is a pointer to an array of "isa" values
411 addr_t isa_addr = bucket_data;
412 for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count;
413 ++isa_idx, isa_addr += addr_size) {
414 isa = m_process->ReadPointerFromMemory(isa_addr, error);
415
416 if (isa && isa != LLDB_INVALID_ADDRESS) {
417 if (!ISAIsCached(isa)) {
418 ClassDescriptorSP descriptor_sp(
419 new ClassDescriptorV1(isa, process_sp));
420
421 if (log && log->GetVerbose())
422 LLDB_LOGF(
423 log,
424 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
425 " from _objc_debug_class_hash to isa->descriptor "
426 "cache",
427 isa);
428
429 AddClass(isa, descriptor_sp);
430 }
431 }
432 }
433 }
434 }
435 }
436 }
437 }
438 }
439 } else {
440 m_isa_to_descriptor_stop_id = UINT32_MAX;
441 }
442 }
443
GetDeclVendor()444 DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {
445 return nullptr;
446 }
447