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 llvm::Expected<std::unique_ptr<UtilityFunction>> 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 181 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 182 ValueObject &isa_pointer) { 183 Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP()); 184 } 185 186 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 187 ObjCISA isa, lldb::ProcessSP process_sp) { 188 Initialize(isa, process_sp); 189 } 190 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 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 274 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const { 275 return ClassDescriptorSP(); 276 } 277 278 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 287 lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() { 288 return 0; 289 } 290 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 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 444 DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() { 445 return nullptr; 446 } 447