1 //===-- AppleObjCClassDescriptorV2.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 "AppleObjCClassDescriptorV2.h" 10 11 #include "lldb/Expression/FunctionCaller.h" 12 #include "lldb/Utility/Log.h" 13 14 using namespace lldb; 15 using namespace lldb_private; 16 17 bool ClassDescriptorV2::Read_objc_class( 18 Process *process, std::unique_ptr<objc_class_t> &objc_class) const { 19 objc_class.reset(new objc_class_t); 20 21 bool ret = objc_class->Read(process, m_objc_class_ptr); 22 23 if (!ret) 24 objc_class.reset(); 25 26 return ret; 27 } 28 29 static lldb::addr_t GetClassDataMask(Process *process) { 30 switch (process->GetAddressByteSize()) { 31 case 4: 32 return 0xfffffffcUL; 33 case 8: 34 return 0x00007ffffffffff8UL; 35 default: 36 break; 37 } 38 39 return LLDB_INVALID_ADDRESS; 40 } 41 42 bool ClassDescriptorV2::objc_class_t::Read(Process *process, 43 lldb::addr_t addr) { 44 size_t ptr_size = process->GetAddressByteSize(); 45 46 size_t objc_class_size = ptr_size // uintptr_t isa; 47 + ptr_size // Class superclass; 48 + ptr_size // void *cache; 49 + ptr_size // IMP *vtable; 50 + ptr_size; // uintptr_t data_NEVER_USE; 51 52 DataBufferHeap objc_class_buf(objc_class_size, '\0'); 53 Status error; 54 55 process->ReadMemory(addr, objc_class_buf.GetBytes(), objc_class_size, error); 56 if (error.Fail()) { 57 return false; 58 } 59 60 DataExtractor extractor(objc_class_buf.GetBytes(), objc_class_size, 61 process->GetByteOrder(), 62 process->GetAddressByteSize()); 63 64 lldb::offset_t cursor = 0; 65 66 m_isa = extractor.GetAddress_unchecked(&cursor); // uintptr_t isa; 67 m_superclass = extractor.GetAddress_unchecked(&cursor); // Class superclass; 68 m_cache_ptr = extractor.GetAddress_unchecked(&cursor); // void *cache; 69 m_vtable_ptr = extractor.GetAddress_unchecked(&cursor); // IMP *vtable; 70 lldb::addr_t data_NEVER_USE = 71 extractor.GetAddress_unchecked(&cursor); // uintptr_t data_NEVER_USE; 72 73 m_flags = (uint8_t)(data_NEVER_USE & (lldb::addr_t)3); 74 m_data_ptr = data_NEVER_USE & GetClassDataMask(process); 75 76 return true; 77 } 78 79 bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) { 80 size_t ptr_size = process->GetAddressByteSize(); 81 82 size_t size = sizeof(uint32_t) // uint32_t flags; 83 + sizeof(uint32_t) // uint32_t version; 84 + ptr_size // const class_ro_t *ro; 85 + ptr_size // union { method_list_t **method_lists; 86 // method_list_t *method_list; }; 87 + ptr_size // struct chained_property_list *properties; 88 + ptr_size // const protocol_list_t **protocols; 89 + ptr_size // Class firstSubclass; 90 + ptr_size; // Class nextSiblingClass; 91 92 DataBufferHeap buffer(size, '\0'); 93 Status error; 94 95 process->ReadMemory(addr, buffer.GetBytes(), size, error); 96 if (error.Fail()) { 97 return false; 98 } 99 100 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 101 process->GetAddressByteSize()); 102 103 lldb::offset_t cursor = 0; 104 105 m_flags = extractor.GetU32_unchecked(&cursor); 106 m_version = extractor.GetU32_unchecked(&cursor); 107 m_ro_ptr = extractor.GetAddress_unchecked(&cursor); 108 m_method_list_ptr = extractor.GetAddress_unchecked(&cursor); 109 m_properties_ptr = extractor.GetAddress_unchecked(&cursor); 110 m_firstSubclass = extractor.GetAddress_unchecked(&cursor); 111 m_nextSiblingClass = extractor.GetAddress_unchecked(&cursor); 112 113 if (m_ro_ptr & 1) { 114 DataBufferHeap buffer(ptr_size, '\0'); 115 process->ReadMemory(m_ro_ptr ^ 1, buffer.GetBytes(), ptr_size, error); 116 if (error.Fail()) 117 return false; 118 cursor = 0; 119 DataExtractor extractor(buffer.GetBytes(), ptr_size, 120 process->GetByteOrder(), 121 process->GetAddressByteSize()); 122 m_ro_ptr = extractor.GetAddress_unchecked(&cursor); 123 } 124 125 return true; 126 } 127 128 bool ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) { 129 size_t ptr_size = process->GetAddressByteSize(); 130 131 size_t size = sizeof(uint32_t) // uint32_t flags; 132 + sizeof(uint32_t) // uint32_t instanceStart; 133 + sizeof(uint32_t) // uint32_t instanceSize; 134 + (ptr_size == 8 ? sizeof(uint32_t) 135 : 0) // uint32_t reserved; // __LP64__ only 136 + ptr_size // const uint8_t *ivarLayout; 137 + ptr_size // const char *name; 138 + ptr_size // const method_list_t *baseMethods; 139 + ptr_size // const protocol_list_t *baseProtocols; 140 + ptr_size // const ivar_list_t *ivars; 141 + ptr_size // const uint8_t *weakIvarLayout; 142 + ptr_size; // const property_list_t *baseProperties; 143 144 DataBufferHeap buffer(size, '\0'); 145 Status error; 146 147 process->ReadMemory(addr, buffer.GetBytes(), size, error); 148 if (error.Fail()) { 149 return false; 150 } 151 152 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 153 process->GetAddressByteSize()); 154 155 lldb::offset_t cursor = 0; 156 157 m_flags = extractor.GetU32_unchecked(&cursor); 158 m_instanceStart = extractor.GetU32_unchecked(&cursor); 159 m_instanceSize = extractor.GetU32_unchecked(&cursor); 160 if (ptr_size == 8) 161 m_reserved = extractor.GetU32_unchecked(&cursor); 162 else 163 m_reserved = 0; 164 m_ivarLayout_ptr = extractor.GetAddress_unchecked(&cursor); 165 m_name_ptr = extractor.GetAddress_unchecked(&cursor); 166 m_baseMethods_ptr = extractor.GetAddress_unchecked(&cursor); 167 m_baseProtocols_ptr = extractor.GetAddress_unchecked(&cursor); 168 m_ivars_ptr = extractor.GetAddress_unchecked(&cursor); 169 m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor); 170 m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor); 171 172 DataBufferHeap name_buf(1024, '\0'); 173 174 process->ReadCStringFromMemory(m_name_ptr, (char *)name_buf.GetBytes(), 175 name_buf.GetByteSize(), error); 176 177 if (error.Fail()) { 178 return false; 179 } 180 181 m_name.assign((char *)name_buf.GetBytes()); 182 183 return true; 184 } 185 186 bool ClassDescriptorV2::Read_class_row( 187 Process *process, const objc_class_t &objc_class, 188 std::unique_ptr<class_ro_t> &class_ro, 189 std::unique_ptr<class_rw_t> &class_rw) const { 190 class_ro.reset(); 191 class_rw.reset(); 192 193 Status error; 194 uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory( 195 objc_class.m_data_ptr, sizeof(uint32_t), 0, error); 196 if (!error.Success()) 197 return false; 198 199 if (class_row_t_flags & RW_REALIZED) { 200 class_rw.reset(new class_rw_t); 201 202 if (!class_rw->Read(process, objc_class.m_data_ptr)) { 203 class_rw.reset(); 204 return false; 205 } 206 207 class_ro.reset(new class_ro_t); 208 209 if (!class_ro->Read(process, class_rw->m_ro_ptr)) { 210 class_rw.reset(); 211 class_ro.reset(); 212 return false; 213 } 214 } else { 215 class_ro.reset(new class_ro_t); 216 217 if (!class_ro->Read(process, objc_class.m_data_ptr)) { 218 class_ro.reset(); 219 return false; 220 } 221 } 222 223 return true; 224 } 225 226 bool ClassDescriptorV2::method_list_t::Read(Process *process, 227 lldb::addr_t addr) { 228 size_t size = sizeof(uint32_t) // uint32_t entsize_NEVER_USE; 229 + sizeof(uint32_t); // uint32_t count; 230 231 DataBufferHeap buffer(size, '\0'); 232 Status error; 233 234 process->ReadMemory(addr, buffer.GetBytes(), size, error); 235 if (error.Fail()) { 236 return false; 237 } 238 239 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 240 process->GetAddressByteSize()); 241 242 lldb::offset_t cursor = 0; 243 244 m_entsize = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3; 245 m_count = extractor.GetU32_unchecked(&cursor); 246 m_first_ptr = addr + cursor; 247 248 return true; 249 } 250 251 bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) { 252 size_t size = GetSize(process); 253 254 DataBufferHeap buffer(size, '\0'); 255 Status error; 256 257 process->ReadMemory(addr, buffer.GetBytes(), size, error); 258 if (error.Fail()) { 259 return false; 260 } 261 262 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 263 process->GetAddressByteSize()); 264 265 lldb::offset_t cursor = 0; 266 267 m_name_ptr = extractor.GetAddress_unchecked(&cursor); 268 m_types_ptr = extractor.GetAddress_unchecked(&cursor); 269 m_imp_ptr = extractor.GetAddress_unchecked(&cursor); 270 271 process->ReadCStringFromMemory(m_name_ptr, m_name, error); 272 if (error.Fail()) { 273 return false; 274 } 275 276 process->ReadCStringFromMemory(m_types_ptr, m_types, error); 277 return !error.Fail(); 278 } 279 280 bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) { 281 size_t size = sizeof(uint32_t) // uint32_t entsize; 282 + sizeof(uint32_t); // uint32_t count; 283 284 DataBufferHeap buffer(size, '\0'); 285 Status error; 286 287 process->ReadMemory(addr, buffer.GetBytes(), size, error); 288 if (error.Fail()) { 289 return false; 290 } 291 292 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 293 process->GetAddressByteSize()); 294 295 lldb::offset_t cursor = 0; 296 297 m_entsize = extractor.GetU32_unchecked(&cursor); 298 m_count = extractor.GetU32_unchecked(&cursor); 299 m_first_ptr = addr + cursor; 300 301 return true; 302 } 303 304 bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) { 305 size_t size = GetSize(process); 306 307 DataBufferHeap buffer(size, '\0'); 308 Status error; 309 310 process->ReadMemory(addr, buffer.GetBytes(), size, error); 311 if (error.Fail()) { 312 return false; 313 } 314 315 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 316 process->GetAddressByteSize()); 317 318 lldb::offset_t cursor = 0; 319 320 m_offset_ptr = extractor.GetAddress_unchecked(&cursor); 321 m_name_ptr = extractor.GetAddress_unchecked(&cursor); 322 m_type_ptr = extractor.GetAddress_unchecked(&cursor); 323 m_alignment = extractor.GetU32_unchecked(&cursor); 324 m_size = extractor.GetU32_unchecked(&cursor); 325 326 process->ReadCStringFromMemory(m_name_ptr, m_name, error); 327 if (error.Fail()) { 328 return false; 329 } 330 331 process->ReadCStringFromMemory(m_type_ptr, m_type, error); 332 return !error.Fail(); 333 } 334 335 bool ClassDescriptorV2::Describe( 336 std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func, 337 std::function<bool(const char *, const char *)> const &instance_method_func, 338 std::function<bool(const char *, const char *)> const &class_method_func, 339 std::function<bool(const char *, const char *, lldb::addr_t, 340 uint64_t)> const &ivar_func) const { 341 lldb_private::Process *process = m_runtime.GetProcess(); 342 343 std::unique_ptr<objc_class_t> objc_class; 344 std::unique_ptr<class_ro_t> class_ro; 345 std::unique_ptr<class_rw_t> class_rw; 346 347 if (!Read_objc_class(process, objc_class)) 348 return false; 349 if (!Read_class_row(process, *objc_class, class_ro, class_rw)) 350 return false; 351 352 static ConstString NSObject_name("NSObject"); 353 354 if (m_name != NSObject_name && superclass_func) 355 superclass_func(objc_class->m_superclass); 356 357 if (instance_method_func) { 358 std::unique_ptr<method_list_t> base_method_list; 359 360 base_method_list.reset(new method_list_t); 361 if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr)) 362 return false; 363 364 if (base_method_list->m_entsize != method_t::GetSize(process)) 365 return false; 366 367 std::unique_ptr<method_t> method; 368 method.reset(new method_t); 369 370 for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) { 371 method->Read(process, base_method_list->m_first_ptr + 372 (i * base_method_list->m_entsize)); 373 374 if (instance_method_func(method->m_name.c_str(), method->m_types.c_str())) 375 break; 376 } 377 } 378 379 if (class_method_func) { 380 AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass()); 381 382 // We don't care about the metaclass's superclass, or its class methods. 383 // Its instance methods are our class methods. 384 385 if (metaclass) { 386 metaclass->Describe( 387 std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr), 388 class_method_func, 389 std::function<bool(const char *, const char *)>(nullptr), 390 std::function<bool(const char *, const char *, lldb::addr_t, 391 uint64_t)>(nullptr)); 392 } 393 } 394 395 if (ivar_func) { 396 if (class_ro->m_ivars_ptr != 0) { 397 ivar_list_t ivar_list; 398 if (!ivar_list.Read(process, class_ro->m_ivars_ptr)) 399 return false; 400 401 if (ivar_list.m_entsize != ivar_t::GetSize(process)) 402 return false; 403 404 ivar_t ivar; 405 406 for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) { 407 ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize)); 408 409 if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(), 410 ivar.m_offset_ptr, ivar.m_size)) 411 break; 412 } 413 } 414 } 415 416 return true; 417 } 418 419 ConstString ClassDescriptorV2::GetClassName() { 420 if (!m_name) { 421 lldb_private::Process *process = m_runtime.GetProcess(); 422 423 if (process) { 424 std::unique_ptr<objc_class_t> objc_class; 425 std::unique_ptr<class_ro_t> class_ro; 426 std::unique_ptr<class_rw_t> class_rw; 427 428 if (!Read_objc_class(process, objc_class)) 429 return m_name; 430 if (!Read_class_row(process, *objc_class, class_ro, class_rw)) 431 return m_name; 432 433 m_name = ConstString(class_ro->m_name.c_str()); 434 } 435 } 436 return m_name; 437 } 438 439 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() { 440 lldb_private::Process *process = m_runtime.GetProcess(); 441 442 if (!process) 443 return ObjCLanguageRuntime::ClassDescriptorSP(); 444 445 std::unique_ptr<objc_class_t> objc_class; 446 447 if (!Read_objc_class(process, objc_class)) 448 return ObjCLanguageRuntime::ClassDescriptorSP(); 449 450 return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA( 451 objc_class->m_superclass); 452 } 453 454 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const { 455 lldb_private::Process *process = m_runtime.GetProcess(); 456 457 if (!process) 458 return ObjCLanguageRuntime::ClassDescriptorSP(); 459 460 std::unique_ptr<objc_class_t> objc_class; 461 462 if (!Read_objc_class(process, objc_class)) 463 return ObjCLanguageRuntime::ClassDescriptorSP(); 464 465 lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa); 466 467 return ObjCLanguageRuntime::ClassDescriptorSP( 468 new ClassDescriptorV2(m_runtime, candidate_isa, nullptr)); 469 } 470 471 uint64_t ClassDescriptorV2::GetInstanceSize() { 472 lldb_private::Process *process = m_runtime.GetProcess(); 473 474 if (process) { 475 std::unique_ptr<objc_class_t> objc_class; 476 std::unique_ptr<class_ro_t> class_ro; 477 std::unique_ptr<class_rw_t> class_rw; 478 479 if (!Read_objc_class(process, objc_class)) 480 return 0; 481 if (!Read_class_row(process, *objc_class, class_ro, class_rw)) 482 return 0; 483 484 return class_ro->m_instanceSize; 485 } 486 487 return 0; 488 } 489 490 ClassDescriptorV2::iVarsStorage::iVarsStorage() 491 : m_filled(false), m_ivars(), m_mutex() {} 492 493 size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); } 494 495 ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage:: 496 operator[](size_t idx) { 497 return m_ivars[idx]; 498 } 499 500 void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime, 501 ClassDescriptorV2 &descriptor) { 502 if (m_filled) 503 return; 504 std::lock_guard<std::recursive_mutex> guard(m_mutex); 505 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES)); 506 LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName()); 507 m_filled = true; 508 ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp( 509 runtime.GetEncodingToType()); 510 Process *process(runtime.GetProcess()); 511 if (!encoding_to_type_sp) 512 return; 513 descriptor.Describe(nullptr, nullptr, nullptr, [this, process, 514 encoding_to_type_sp, 515 log](const char *name, 516 const char *type, 517 lldb::addr_t offset_ptr, 518 uint64_t size) -> bool { 519 const bool for_expression = false; 520 const bool stop_loop = false; 521 LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}", 522 name, type, offset_ptr, size); 523 CompilerType ivar_type = 524 encoding_to_type_sp->RealizeType(type, for_expression); 525 if (ivar_type) { 526 LLDB_LOGV(log, 527 "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = " 528 "{3}, type_size = {4}", 529 name, type, offset_ptr, size, 530 ivar_type.GetByteSize(nullptr).getValueOr(0)); 531 Scalar offset_scalar; 532 Status error; 533 const int offset_ptr_size = 4; 534 const bool is_signed = false; 535 size_t read = process->ReadScalarIntegerFromMemory( 536 offset_ptr, offset_ptr_size, is_signed, offset_scalar, error); 537 if (error.Success() && 4 == read) { 538 LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr, 539 offset_scalar.SInt()); 540 m_ivars.push_back( 541 {ConstString(name), ivar_type, size, offset_scalar.SInt()}); 542 } else 543 LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}", 544 offset_ptr, read); 545 } 546 return stop_loop; 547 }); 548 } 549 550 void ClassDescriptorV2::GetIVarInformation() { 551 m_ivars_storage.fill(m_runtime, *this); 552 } 553