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 = std::make_unique<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 = std::make_unique<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 = std::make_unique<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 = std::make_unique<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 uint32_t entsize = extractor.GetU32_unchecked(&cursor); 245 m_is_small = (entsize & 0x80000000) != 0; 246 m_has_direct_selector = (entsize & 0x40000000) != 0; 247 m_entsize = entsize & 0xfffc; 248 m_count = extractor.GetU32_unchecked(&cursor); 249 m_first_ptr = addr + cursor; 250 251 return true; 252 } 253 254 bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr, 255 bool is_small, bool has_direct_sel) { 256 size_t ptr_size = process->GetAddressByteSize(); 257 size_t size = GetSize(process, is_small); 258 259 DataBufferHeap buffer(size, '\0'); 260 Status error; 261 262 process->ReadMemory(addr, buffer.GetBytes(), size, error); 263 if (error.Fail()) { 264 return false; 265 } 266 267 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 268 ptr_size); 269 lldb::offset_t cursor = 0; 270 271 if (is_small) { 272 uint32_t nameref_offset = extractor.GetU32_unchecked(&cursor); 273 uint32_t types_offset = extractor.GetU32_unchecked(&cursor); 274 uint32_t imp_offset = extractor.GetU32_unchecked(&cursor); 275 276 m_name_ptr = addr + nameref_offset; 277 278 if (!has_direct_sel) { 279 // The SEL offset points to a SELRef. We need to dereference twice. 280 m_name_ptr = process->ReadUnsignedIntegerFromMemory(m_name_ptr, ptr_size, 281 0, error); 282 if (!error.Success()) 283 return false; 284 } 285 m_types_ptr = addr + 4 + types_offset; 286 m_imp_ptr = addr + 8 + imp_offset; 287 } else { 288 m_name_ptr = extractor.GetAddress_unchecked(&cursor); 289 m_types_ptr = extractor.GetAddress_unchecked(&cursor); 290 m_imp_ptr = extractor.GetAddress_unchecked(&cursor); 291 } 292 293 process->ReadCStringFromMemory(m_name_ptr, m_name, error); 294 if (error.Fail()) { 295 return false; 296 } 297 298 process->ReadCStringFromMemory(m_types_ptr, m_types, error); 299 return !error.Fail(); 300 } 301 302 bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) { 303 size_t size = sizeof(uint32_t) // uint32_t entsize; 304 + sizeof(uint32_t); // uint32_t count; 305 306 DataBufferHeap buffer(size, '\0'); 307 Status error; 308 309 process->ReadMemory(addr, buffer.GetBytes(), size, error); 310 if (error.Fail()) { 311 return false; 312 } 313 314 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 315 process->GetAddressByteSize()); 316 317 lldb::offset_t cursor = 0; 318 319 m_entsize = extractor.GetU32_unchecked(&cursor); 320 m_count = extractor.GetU32_unchecked(&cursor); 321 m_first_ptr = addr + cursor; 322 323 return true; 324 } 325 326 bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) { 327 size_t size = GetSize(process); 328 329 DataBufferHeap buffer(size, '\0'); 330 Status error; 331 332 process->ReadMemory(addr, buffer.GetBytes(), size, error); 333 if (error.Fail()) { 334 return false; 335 } 336 337 DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), 338 process->GetAddressByteSize()); 339 340 lldb::offset_t cursor = 0; 341 342 m_offset_ptr = extractor.GetAddress_unchecked(&cursor); 343 m_name_ptr = extractor.GetAddress_unchecked(&cursor); 344 m_type_ptr = extractor.GetAddress_unchecked(&cursor); 345 m_alignment = extractor.GetU32_unchecked(&cursor); 346 m_size = extractor.GetU32_unchecked(&cursor); 347 348 process->ReadCStringFromMemory(m_name_ptr, m_name, error); 349 if (error.Fail()) { 350 return false; 351 } 352 353 process->ReadCStringFromMemory(m_type_ptr, m_type, error); 354 return !error.Fail(); 355 } 356 357 bool ClassDescriptorV2::Describe( 358 std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func, 359 std::function<bool(const char *, const char *)> const &instance_method_func, 360 std::function<bool(const char *, const char *)> const &class_method_func, 361 std::function<bool(const char *, const char *, lldb::addr_t, 362 uint64_t)> const &ivar_func) const { 363 lldb_private::Process *process = m_runtime.GetProcess(); 364 365 std::unique_ptr<objc_class_t> objc_class; 366 std::unique_ptr<class_ro_t> class_ro; 367 std::unique_ptr<class_rw_t> class_rw; 368 369 if (!Read_objc_class(process, objc_class)) 370 return false; 371 if (!Read_class_row(process, *objc_class, class_ro, class_rw)) 372 return false; 373 374 static ConstString NSObject_name("NSObject"); 375 376 if (m_name != NSObject_name && superclass_func) 377 superclass_func(objc_class->m_superclass); 378 379 if (instance_method_func) { 380 std::unique_ptr<method_list_t> base_method_list; 381 382 base_method_list = std::make_unique<method_list_t>(); 383 if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr)) 384 return false; 385 386 bool is_small = base_method_list->m_is_small; 387 bool has_direct_selector = base_method_list->m_has_direct_selector; 388 389 if (base_method_list->m_entsize != method_t::GetSize(process, is_small)) 390 return false; 391 392 std::unique_ptr<method_t> method; 393 method = std::make_unique<method_t>(); 394 395 for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) { 396 method->Read(process, 397 base_method_list->m_first_ptr + 398 (i * base_method_list->m_entsize), 399 is_small, has_direct_selector); 400 401 if (instance_method_func(method->m_name.c_str(), method->m_types.c_str())) 402 break; 403 } 404 } 405 406 if (class_method_func) { 407 AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass()); 408 409 // We don't care about the metaclass's superclass, or its class methods. 410 // Its instance methods are our class methods. 411 412 if (metaclass) { 413 metaclass->Describe( 414 std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr), 415 class_method_func, 416 std::function<bool(const char *, const char *)>(nullptr), 417 std::function<bool(const char *, const char *, lldb::addr_t, 418 uint64_t)>(nullptr)); 419 } 420 } 421 422 if (ivar_func) { 423 if (class_ro->m_ivars_ptr != 0) { 424 ivar_list_t ivar_list; 425 if (!ivar_list.Read(process, class_ro->m_ivars_ptr)) 426 return false; 427 428 if (ivar_list.m_entsize != ivar_t::GetSize(process)) 429 return false; 430 431 ivar_t ivar; 432 433 for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) { 434 ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize)); 435 436 if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(), 437 ivar.m_offset_ptr, ivar.m_size)) 438 break; 439 } 440 } 441 } 442 443 return true; 444 } 445 446 ConstString ClassDescriptorV2::GetClassName() { 447 if (!m_name) { 448 lldb_private::Process *process = m_runtime.GetProcess(); 449 450 if (process) { 451 std::unique_ptr<objc_class_t> objc_class; 452 std::unique_ptr<class_ro_t> class_ro; 453 std::unique_ptr<class_rw_t> class_rw; 454 455 if (!Read_objc_class(process, objc_class)) 456 return m_name; 457 if (!Read_class_row(process, *objc_class, class_ro, class_rw)) 458 return m_name; 459 460 m_name = ConstString(class_ro->m_name.c_str()); 461 } 462 } 463 return m_name; 464 } 465 466 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() { 467 lldb_private::Process *process = m_runtime.GetProcess(); 468 469 if (!process) 470 return ObjCLanguageRuntime::ClassDescriptorSP(); 471 472 std::unique_ptr<objc_class_t> objc_class; 473 474 if (!Read_objc_class(process, objc_class)) 475 return ObjCLanguageRuntime::ClassDescriptorSP(); 476 477 return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA( 478 objc_class->m_superclass); 479 } 480 481 ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const { 482 lldb_private::Process *process = m_runtime.GetProcess(); 483 484 if (!process) 485 return ObjCLanguageRuntime::ClassDescriptorSP(); 486 487 std::unique_ptr<objc_class_t> objc_class; 488 489 if (!Read_objc_class(process, objc_class)) 490 return ObjCLanguageRuntime::ClassDescriptorSP(); 491 492 lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa); 493 494 return ObjCLanguageRuntime::ClassDescriptorSP( 495 new ClassDescriptorV2(m_runtime, candidate_isa, nullptr)); 496 } 497 498 uint64_t ClassDescriptorV2::GetInstanceSize() { 499 lldb_private::Process *process = m_runtime.GetProcess(); 500 501 if (process) { 502 std::unique_ptr<objc_class_t> objc_class; 503 std::unique_ptr<class_ro_t> class_ro; 504 std::unique_ptr<class_rw_t> class_rw; 505 506 if (!Read_objc_class(process, objc_class)) 507 return 0; 508 if (!Read_class_row(process, *objc_class, class_ro, class_rw)) 509 return 0; 510 511 return class_ro->m_instanceSize; 512 } 513 514 return 0; 515 } 516 517 ClassDescriptorV2::iVarsStorage::iVarsStorage() 518 : m_filled(false), m_ivars(), m_mutex() {} 519 520 size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); } 521 522 ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage:: 523 operator[](size_t idx) { 524 return m_ivars[idx]; 525 } 526 527 void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime, 528 ClassDescriptorV2 &descriptor) { 529 if (m_filled) 530 return; 531 std::lock_guard<std::recursive_mutex> guard(m_mutex); 532 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES)); 533 LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName()); 534 m_filled = true; 535 ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp( 536 runtime.GetEncodingToType()); 537 Process *process(runtime.GetProcess()); 538 if (!encoding_to_type_sp) 539 return; 540 descriptor.Describe(nullptr, nullptr, nullptr, [this, process, 541 encoding_to_type_sp, 542 log](const char *name, 543 const char *type, 544 lldb::addr_t offset_ptr, 545 uint64_t size) -> bool { 546 const bool for_expression = false; 547 const bool stop_loop = false; 548 LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}", 549 name, type, offset_ptr, size); 550 CompilerType ivar_type = 551 encoding_to_type_sp->RealizeType(type, for_expression); 552 if (ivar_type) { 553 LLDB_LOGV(log, 554 "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = " 555 "{3}, type_size = {4}", 556 name, type, offset_ptr, size, 557 ivar_type.GetByteSize(nullptr).getValueOr(0)); 558 Scalar offset_scalar; 559 Status error; 560 const int offset_ptr_size = 4; 561 const bool is_signed = false; 562 size_t read = process->ReadScalarIntegerFromMemory( 563 offset_ptr, offset_ptr_size, is_signed, offset_scalar, error); 564 if (error.Success() && 4 == read) { 565 LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr, 566 offset_scalar.SInt()); 567 m_ivars.push_back( 568 {ConstString(name), ivar_type, size, offset_scalar.SInt()}); 569 } else 570 LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}", 571 offset_ptr, read); 572 } 573 return stop_loop; 574 }); 575 } 576 577 void ClassDescriptorV2::GetIVarInformation() { 578 m_ivars_storage.fill(m_runtime, *this); 579 } 580