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 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 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 * 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 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 91 void AppleObjCRuntimeV1::Terminate() { 92 PluginManager::UnregisterPlugin(CreateInstance); 93 } 94 95 lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() { 96 static ConstString g_name("apple-objc-v1"); 97 return g_name; 98 } 99 100 // PluginInterface protocol 101 ConstString AppleObjCRuntimeV1::GetPluginName() { 102 return GetPluginNameStatic(); 103 } 104 105 uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; } 106 107 BreakpointResolverSP 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 UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) { 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; 175 return GetTargetRef().GetUtilityFunctionForLanguage( 176 buf->contents, eLanguageTypeObjC, name, error); 177 } 178 179 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 180 ValueObject &isa_pointer) { 181 Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP()); 182 } 183 184 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 185 ObjCISA isa, lldb::ProcessSP process_sp) { 186 Initialize(isa, process_sp); 187 } 188 189 void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize( 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 260 AppleObjCRuntime::ClassDescriptorSP 261 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() { 262 if (!m_valid) 263 return AppleObjCRuntime::ClassDescriptorSP(); 264 ProcessSP process_sp = m_process_wp.lock(); 265 if (!process_sp) 266 return AppleObjCRuntime::ClassDescriptorSP(); 267 return ObjCLanguageRuntime::ClassDescriptorSP( 268 new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp)); 269 } 270 271 AppleObjCRuntime::ClassDescriptorSP 272 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const { 273 return ClassDescriptorSP(); 274 } 275 276 bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe( 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 285 lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() { 286 return 0; 287 } 288 289 lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() { 290 if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) { 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 322 void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() { 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. 329 m_isa_to_descriptor_stop_id = process->GetStopID(); 330 331 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); 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 { 438 m_isa_to_descriptor_stop_id = UINT32_MAX; 439 } 440 } 441 442 DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() { 443 return nullptr; 444 } 445