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/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" 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::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()); 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 BreakpointResolverSP 97 AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP &bkpt, 98 bool catch_bp, bool throw_bp) { 99 BreakpointResolverSP resolver_sp; 100 101 if (throw_bp) 102 resolver_sp = std::make_shared<BreakpointResolverName>( 103 bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), 104 eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, 105 eLazyBoolNo); 106 // FIXME: don't do catch yet. 107 return resolver_sp; 108 } 109 110 struct BufStruct { 111 char contents[2048]; 112 }; 113 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); 118 119 int strformatsize = 120 snprintf(&buf->contents[0], sizeof(buf->contents), 121 "struct __objc_class " 122 " \n" 123 "{ " 124 " \n" 125 " struct __objc_class *isa; " 126 " \n" 127 " struct __objc_class *super_class; " 128 " \n" 129 " const char *name; " 130 " \n" 131 " // rest of struct elided because unused " 132 " \n" 133 "}; " 134 " \n" 135 " " 136 " \n" 137 "struct __objc_object " 138 " \n" 139 "{ " 140 " \n" 141 " struct __objc_class *isa; " 142 " \n" 143 "}; " 144 " \n" 145 " " 146 " \n" 147 "extern \"C\" void " 148 " \n" 149 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) " 150 " \n" 151 "{ " 152 " \n" 153 " struct __objc_object *obj = (struct " 154 "__objc_object*)$__lldb_arg_obj; \n" 155 " if ($__lldb_arg_obj == (void *)0) " 156 " \n" 157 " return; // nil is ok " 158 " (int)strlen(obj->isa->name); " 159 " \n" 160 "} " 161 " \n", 162 name.c_str()); 163 assert(strformatsize < (int)sizeof(buf->contents)); 164 UNUSED_IF_ASSERT_DISABLED(strformatsize); 165 166 return GetTargetRef().CreateUtilityFunction(buf->contents, std::move(name), 167 eLanguageTypeC, exe_ctx); 168 } 169 170 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 171 ValueObject &isa_pointer) { 172 Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP()); 173 } 174 175 AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( 176 ObjCISA isa, lldb::ProcessSP process_sp) { 177 Initialize(isa, process_sp); 178 } 179 180 void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize( 181 ObjCISA isa, lldb::ProcessSP process_sp) { 182 if (!isa || !process_sp) { 183 m_valid = false; 184 return; 185 } 186 187 m_valid = true; 188 189 Status error; 190 191 m_isa = process_sp->ReadPointerFromMemory(isa, error); 192 193 if (error.Fail()) { 194 m_valid = false; 195 return; 196 } 197 198 uint32_t ptr_size = process_sp->GetAddressByteSize(); 199 200 if (!IsPointerValid(m_isa, ptr_size)) { 201 m_valid = false; 202 return; 203 } 204 205 m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error); 206 207 if (error.Fail()) { 208 m_valid = false; 209 return; 210 } 211 212 if (!IsPointerValid(m_parent_isa, ptr_size, true)) { 213 m_valid = false; 214 return; 215 } 216 217 lldb::addr_t name_ptr = 218 process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error); 219 220 if (error.Fail()) { 221 m_valid = false; 222 return; 223 } 224 225 lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); 226 227 size_t count = process_sp->ReadCStringFromMemory( 228 name_ptr, (char *)buffer_sp->GetBytes(), 1024, error); 229 230 if (error.Fail()) { 231 m_valid = false; 232 return; 233 } 234 235 if (count) 236 m_name = ConstString(reinterpret_cast<const char *>(buffer_sp->GetBytes())); 237 else 238 m_name = ConstString(); 239 240 m_instance_size = process_sp->ReadUnsignedIntegerFromMemory( 241 m_isa + 5 * ptr_size, ptr_size, 0, error); 242 243 if (error.Fail()) { 244 m_valid = false; 245 return; 246 } 247 248 m_process_wp = lldb::ProcessWP(process_sp); 249 } 250 251 AppleObjCRuntime::ClassDescriptorSP 252 AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() { 253 if (!m_valid) 254 return AppleObjCRuntime::ClassDescriptorSP(); 255 ProcessSP process_sp = m_process_wp.lock(); 256 if (!process_sp) 257 return AppleObjCRuntime::ClassDescriptorSP(); 258 return ObjCLanguageRuntime::ClassDescriptorSP( 259 new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp)); 260 } 261 262 AppleObjCRuntime::ClassDescriptorSP 263 AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const { 264 return ClassDescriptorSP(); 265 } 266 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 { 273 return false; 274 } 275 276 lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() { 277 return 0; 278 } 279 280 lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() { 281 if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) { 282 ModuleSP objc_module_sp(GetObjCModule()); 283 284 if (!objc_module_sp) 285 return LLDB_INVALID_ADDRESS; 286 287 static ConstString g_objc_debug_class_hash("_objc_debug_class_hash"); 288 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(); 293 if (process) { 294 295 lldb::addr_t objc_debug_class_hash_addr = 296 symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); 297 298 if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) { 299 Status error; 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; 305 } 306 } 307 } 308 } 309 } 310 return m_isa_hash_table_ptr; 311 } 312 313 void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() { 314 // TODO: implement HashTableSignature... 315 Process *process = GetProcess(); 316 317 if (process) { 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(); 321 322 Log *log = GetLog(LLDBLog::Process); 323 324 ProcessSP process_sp = process->shared_from_this(); 325 326 ModuleSP objc_module_sp(GetObjCModule()); 327 328 if (!objc_module_sp) 329 return; 330 331 lldb::addr_t hash_table_ptr = GetISAHashTablePointer(); 332 if (hash_table_ptr != LLDB_INVALID_ADDRESS) { 333 // Read the NXHashTable struct: 334 // 335 // typedef struct { 336 // const NXHashTablePrototype *prototype; 337 // unsigned count; 338 // unsigned nbBuckets; 339 // void *buckets; 340 // const void *info; 341 // } NXHashTable; 342 343 Status error; 344 DataBufferHeap buffer(1024, 0); 345 if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 346 20) { 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, 350 addr_size); 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); 357 358 const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t); 359 buffer.SetByteSize(data_size); 360 361 if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, 362 error) == data_size) { 363 data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order); 364 offset = 0; 365 for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; 366 ++bucket_idx) { 367 const uint32_t bucket_isa_count = data.GetU32(&offset); 368 const lldb::addr_t bucket_data = data.GetU32(&offset); 369 370 if (bucket_isa_count == 0) 371 continue; 372 373 ObjCISA isa; 374 if (bucket_isa_count == 1) { 375 // When we only have one entry in the bucket, the bucket data 376 // is the "isa" 377 isa = bucket_data; 378 if (isa) { 379 if (!ISAIsCached(isa)) { 380 ClassDescriptorSP descriptor_sp( 381 new ClassDescriptorV1(isa, process_sp)); 382 383 if (log && log->GetVerbose()) 384 LLDB_LOGF(log, 385 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 386 " from _objc_debug_class_hash to " 387 "isa->descriptor cache", 388 isa); 389 390 AddClass(isa, descriptor_sp); 391 } 392 } 393 } else { 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); 400 401 if (isa && isa != LLDB_INVALID_ADDRESS) { 402 if (!ISAIsCached(isa)) { 403 ClassDescriptorSP descriptor_sp( 404 new ClassDescriptorV1(isa, process_sp)); 405 406 if (log && log->GetVerbose()) 407 LLDB_LOGF( 408 log, 409 "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 410 " from _objc_debug_class_hash to isa->descriptor " 411 "cache", 412 isa); 413 414 AddClass(isa, descriptor_sp); 415 } 416 } 417 } 418 } 419 } 420 } 421 } 422 } 423 } 424 } else { 425 m_isa_to_descriptor_stop_id = UINT32_MAX; 426 } 427 } 428 429 DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() { 430 return nullptr; 431 } 432