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