1 //===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++ 2 //-*-===// 3 // 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 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "AppleObjCRuntimeV1.h" 11 #include "AppleObjCDeclVendor.h" 12 #include "AppleObjCTrampolineHandler.h" 13 14 #include "clang/AST/Type.h" 15 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" 33 34 #include <memory> 35 #include <vector> 36 37 using namespace lldb; 38 using namespace lldb_private; 39 40 char AppleObjCRuntimeV1::ID = 0; 41 42 AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process) 43 : AppleObjCRuntime(process), m_hash_signature(), 44 m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {} 45 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()); 61 } 62 } 63 return !class_type_or_name.IsEmpty(); 64 } 65 66 // Static Functions 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; 75 76 if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) == 77 ObjCRuntimeVersions::eAppleObjC_V1) 78 return new AppleObjCRuntimeV1(process); 79 else 80 return nullptr; 81 } else 82 return nullptr; 83 } 84 85 void AppleObjCRuntimeV1::Initialize() { 86 PluginManager::RegisterPlugin( 87 GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1", 88 CreateInstance, 89 /*command_callback = */ nullptr, GetBreakpointExceptionPrecondition); 90 } 91 92 void AppleObjCRuntimeV1::Terminate() { 93 PluginManager::UnregisterPlugin(CreateInstance); 94 } 95 96 lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() { 97 static ConstString g_name("apple-objc-v1"); 98 return g_name; 99 } 100 101 // PluginInterface protocol 102 ConstString AppleObjCRuntimeV1::GetPluginName() { 103 return GetPluginNameStatic(); 104 } 105 106 uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; } 107 108 BreakpointResolverSP 109 AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, 110 bool throw_bp) { 111 BreakpointResolverSP resolver_sp; 112 113 if (throw_bp) 114 resolver_sp = std::make_shared<BreakpointResolverName>( 115 bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), 116 eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, 117 eLazyBoolNo); 118 // FIXME: don't do catch yet. 119 return resolver_sp; 120 } 121 122 struct BufStruct { 123 char contents[2048]; 124 }; 125 126 UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) { 127 std::unique_ptr<BufStruct> buf(new BufStruct); 128 129 int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents), 130 "struct __objc_class " 131 " \n" 132 "{ " 133 " \n" 134 " struct __objc_class *isa; " 135 " \n" 136 " struct __objc_class *super_class; " 137 " \n" 138 " const char *name; " 139 " \n" 140 " // rest of struct elided because unused " 141 " \n" 142 "}; " 143 " \n" 144 " " 145 " \n" 146 "struct __objc_object " 147 " \n" 148 "{ " 149 " \n" 150 " struct __objc_class *isa; " 151 " \n" 152 "}; " 153 " \n" 154 " " 155 " \n" 156 "extern \"C\" void " 157 " \n" 158 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) " 159 " \n" 160 "{ " 161 " \n" 162 " struct __objc_object *obj = (struct " 163 "__objc_object*)$__lldb_arg_obj; \n" 164 " if ($__lldb_arg_obj == (void *)0) " 165 " \n" 166 " return; // nil is ok " 167 " (int)strlen(obj->isa->name); " 168 " \n" 169 "} " 170 " \n", 171 name); 172 assert(strformatsize < (int)sizeof(buf->contents)); 173 (void)strformatsize; 174 175 Status error; 176 return GetTargetRef().GetUtilityFunctionForLanguage( 177 buf->contents, eLanguageTypeObjC, name, error); 178 } 179 180 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 181 ValueObject &isa_pointer) { 182 Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP()); 183 } 184 185 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 186 ObjCISA isa, lldb::ProcessSP process_sp) { 187 Initialize(isa, process_sp); 188 } 189 190 void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize( 191 ObjCISA isa, lldb::ProcessSP process_sp) { 192 if (!isa || !process_sp) { 193 m_valid = false; 194 return; 195 } 196 197 m_valid = true; 198 199 Status error; 200 201 m_isa = process_sp->ReadPointerFromMemory(isa, error); 202 203 if (error.Fail()) { 204 m_valid = false; 205 return; 206 } 207 208 uint32_t ptr_size = process_sp->GetAddressByteSize(); 209 210 if (!IsPointerValid(m_isa, ptr_size)) { 211 m_valid = false; 212 return; 213 } 214 215 m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error); 216 217 if (error.Fail()) { 218 m_valid = false; 219 return; 220 } 221 222 if (!IsPointerValid(m_parent_isa, ptr_size, true)) { 223 m_valid = false; 224 return; 225 } 226 227 lldb::addr_t name_ptr = 228 process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error); 229 230 if (error.Fail()) { 231 m_valid = false; 232 return; 233 } 234 235 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); 236 237 size_t count = process_sp->ReadCStringFromMemory( 238 name_ptr, (char *)buffer_sp->GetBytes(), 1024, error); 239 240 if (error.Fail()) { 241 m_valid = false; 242 return; 243 } 244 245 if (count) 246 m_name = ConstString((char *)buffer_sp->GetBytes()); 247 else 248 m_name = ConstString(); 249 250 m_instance_size = process_sp->ReadUnsignedIntegerFromMemory( 251 m_isa + 5 * ptr_size, ptr_size, 0, error); 252 253 if (error.Fail()) { 254 m_valid = false; 255 return; 256 } 257 258 m_process_wp = lldb::ProcessWP(process_sp); 259 } 260 261 AppleObjCRuntime::ClassDescriptorSP 262 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() { 263 if (!m_valid) 264 return AppleObjCRuntime::ClassDescriptorSP(); 265 ProcessSP process_sp = m_process_wp.lock(); 266 if (!process_sp) 267 return AppleObjCRuntime::ClassDescriptorSP(); 268 return ObjCLanguageRuntime::ClassDescriptorSP( 269 new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp)); 270 } 271 272 AppleObjCRuntime::ClassDescriptorSP 273 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const { 274 return ClassDescriptorSP(); 275 } 276 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 { 283 return false; 284 } 285 286 lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() { 287 return 0; 288 } 289 290 lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() { 291 if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) { 292 ModuleSP objc_module_sp(GetObjCModule()); 293 294 if (!objc_module_sp) 295 return LLDB_INVALID_ADDRESS; 296 297 static ConstString g_objc_debug_class_hash("_objc_debug_class_hash"); 298 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(); 303 if (process) { 304 305 lldb::addr_t objc_debug_class_hash_addr = 306 symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); 307 308 if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) { 309 Status error; 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; 315 } 316 } 317 } 318 } 319 } 320 return m_isa_hash_table_ptr; 321 } 322 323 void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() { 324 // TODO: implement HashTableSignature... 325 Process *process = GetProcess(); 326 327 if (process) { 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(); 331 332 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); 333 334 ProcessSP process_sp = process->shared_from_this(); 335 336 ModuleSP objc_module_sp(GetObjCModule()); 337 338 if (!objc_module_sp) 339 return; 340 341 uint32_t isa_count = 0; 342 343 lldb::addr_t hash_table_ptr = GetISAHashTablePointer(); 344 if (hash_table_ptr != LLDB_INVALID_ADDRESS) { 345 // Read the NXHashTable struct: 346 // 347 // typedef struct { 348 // const NXHashTablePrototype *prototype; 349 // unsigned count; 350 // unsigned nbBuckets; 351 // void *buckets; 352 // const void *info; 353 // } NXHashTable; 354 355 Status error; 356 DataBufferHeap buffer(1024, 0); 357 if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 358 20) { 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, 362 addr_size); 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); 369 370 const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t); 371 buffer.SetByteSize(data_size); 372 373 if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, 374 error) == data_size) { 375 data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order); 376 offset = 0; 377 for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; 378 ++bucket_idx) { 379 const uint32_t bucket_isa_count = data.GetU32(&offset); 380 const lldb::addr_t bucket_data = data.GetU32(&offset); 381 382 if (bucket_isa_count == 0) 383 continue; 384 385 isa_count += bucket_isa_count; 386 387 ObjCISA isa; 388 if (bucket_isa_count == 1) { 389 // When we only have one entry in the bucket, the bucket data 390 // is the "isa" 391 isa = bucket_data; 392 if (isa) { 393 if (!ISAIsCached(isa)) { 394 ClassDescriptorSP descriptor_sp( 395 new ClassDescriptorV1(isa, process_sp)); 396 397 if (log && log->GetVerbose()) 398 log->Printf("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 log->Printf( 421 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 422 " from _objc_debug_class_hash to isa->descriptor " 423 "cache", 424 isa); 425 426 AddClass(isa, descriptor_sp); 427 } 428 } 429 } 430 } 431 } 432 } 433 } 434 } 435 } 436 } else { 437 m_isa_to_descriptor_stop_id = UINT32_MAX; 438 } 439 } 440 441 DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() { 442 return nullptr; 443 } 444