AppleObjCRuntimeV2.cpp revision 4722b10307668368bf0f12fa6b8691e4f4cb5488
1//===-- AppleObjCRuntimeV2.cpp --------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10 11#include <string> 12#include <vector> 13#include <memory> 14#include <stdint.h> 15 16#include "lldb/lldb-enumerations.h" 17#include "lldb/Core/ClangForward.h" 18#include "lldb/Symbol/ClangASTType.h" 19 20#include "lldb/Breakpoint/BreakpointLocation.h" 21#include "lldb/Core/ClangForward.h" 22#include "lldb/Core/ConstString.h" 23#include "lldb/Core/Error.h" 24#include "lldb/Core/Log.h" 25#include "lldb/Core/Module.h" 26#include "lldb/Core/PluginManager.h" 27#include "lldb/Core/Scalar.h" 28#include "lldb/Core/StreamString.h" 29#include "lldb/Core/ValueObjectConstResult.h" 30#include "lldb/Expression/ClangFunction.h" 31#include "lldb/Expression/ClangUtilityFunction.h" 32#include "lldb/Symbol/ClangASTContext.h" 33#include "lldb/Target/ExecutionContext.h" 34#include "lldb/Target/Process.h" 35#include "lldb/Target/RegisterContext.h" 36#include "lldb/Target/Target.h" 37#include "lldb/Target/Thread.h" 38 39#include "AppleObjCRuntimeV2.h" 40#include "AppleObjCSymbolVendor.h" 41#include "AppleObjCTrampolineHandler.h" 42 43#include <vector> 44 45using namespace lldb; 46using namespace lldb_private; 47 48 49static const char *pluginName = "AppleObjCRuntimeV2"; 50static const char *pluginDesc = "Apple Objective C Language Runtime - Version 2"; 51static const char *pluginShort = "language.apple.objc.v2"; 52 53 54const char *AppleObjCRuntimeV2::g_find_class_name_function_name = "__lldb_apple_objc_v2_find_class_name"; 55const char *AppleObjCRuntimeV2::g_find_class_name_function_body = " \n\ 56extern \"C\" \n\ 57{ \n\ 58 extern void *gdb_class_getClass (void *objc_class); \n\ 59 extern void *class_getName(void *objc_class); \n\ 60 extern int printf(const char *format, ...); \n\ 61} \n\ 62 \n\ 63struct __lldb_objc_object { \n\ 64 void *isa; \n\ 65}; \n\ 66 \n\ 67extern \"C\" void *__lldb_apple_objc_v2_find_class_name ( \n\ 68 __lldb_objc_object *object_ptr, \n\ 69 int debug) \n\ 70{ \n\ 71 void *name = 0; \n\ 72 if (debug) \n\ 73 printf (\"\\n*** Called in v2_find_class_name with object: 0x%p\\n\", object_ptr); \n\ 74 // Call gdb_class_getClass so we can tell if the class is good. \n\ 75 void *objc_class = gdb_class_getClass (object_ptr->isa); \n\ 76 if (objc_class) \n\ 77 { \n\ 78 void *actual_class = (void *) [(id) object_ptr class]; \n\ 79 if (actual_class != 0) \n\ 80 name = class_getName((void *) actual_class); \n\ 81 if (debug) \n\ 82 printf (\"\\n*** Found name: %s\\n\", name ? name : \"<NOT FOUND>\"); \n\ 83 } \n\ 84 else if (debug) \n\ 85 printf (\"\\n*** gdb_class_getClass returned NULL\\n\"); \n\ 86 return name; \n\ 87} \n\ 88"; 89 90AppleObjCRuntimeV2::AppleObjCRuntimeV2 (Process *process, 91 const ModuleSP &objc_module_sp) : 92 AppleObjCRuntime (process), 93 m_get_class_name_args(LLDB_INVALID_ADDRESS), 94 m_get_class_name_args_mutex(Mutex::eMutexTypeNormal), 95 m_isa_to_name_cache(), 96 m_isa_to_parent_cache() 97{ 98 static const ConstString g_gdb_object_getClass("gdb_object_getClass"); 99 m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(g_gdb_object_getClass, eSymbolTypeCode) != NULL); 100} 101 102bool 103AppleObjCRuntimeV2::RunFunctionToFindClassName(addr_t object_addr, Thread *thread, char *name_dst, size_t max_name_len) 104{ 105 // Since we are going to run code we have to make sure only one thread at a time gets to try this. 106 Mutex::Locker (m_get_class_name_args_mutex); 107 108 StreamString errors; 109 110 LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); // FIXME - a more appropriate log channel? 111 112 int32_t debug; 113 if (log) 114 debug = 1; 115 else 116 debug = 0; 117 118 ValueList dispatch_values; 119 120 Value void_ptr_value; 121 ClangASTContext *clang_ast_context = m_process->GetTarget().GetScratchClangASTContext(); 122 123 clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false); 124 void_ptr_value.SetValueType (Value::eValueTypeScalar); 125 void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); 126 void_ptr_value.GetScalar() = object_addr; 127 128 dispatch_values.PushValue (void_ptr_value); 129 130 Value int_value; 131 clang_type_t clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 32); 132 int_value.SetValueType (Value::eValueTypeScalar); 133 int_value.SetContext (Value::eContextTypeClangType, clang_int_type); 134 int_value.GetScalar() = debug; 135 136 dispatch_values.PushValue (int_value); 137 138 ExecutionContext exe_ctx; 139 thread->CalculateExecutionContext(exe_ctx); 140 141 Address find_class_name_address; 142 143 if (!m_get_class_name_code.get()) 144 { 145 m_get_class_name_code.reset (new ClangUtilityFunction (g_find_class_name_function_body, 146 g_find_class_name_function_name)); 147 148 if (!m_get_class_name_code->Install(errors, exe_ctx)) 149 { 150 if (log) 151 log->Printf ("Failed to install implementation lookup: %s.", errors.GetData()); 152 m_get_class_name_code.reset(); 153 return false; 154 } 155 find_class_name_address.Clear(); 156 find_class_name_address.SetOffset(m_get_class_name_code->StartAddress()); 157 } 158 else 159 { 160 find_class_name_address.Clear(); 161 find_class_name_address.SetOffset(m_get_class_name_code->StartAddress()); 162 } 163 164 // Next make the runner function for our implementation utility function. 165 if (!m_get_class_name_function.get()) 166 { 167 m_get_class_name_function.reset(new ClangFunction (*m_process, 168 clang_ast_context, 169 clang_void_ptr_type, 170 find_class_name_address, 171 dispatch_values)); 172 173 errors.Clear(); 174 unsigned num_errors = m_get_class_name_function->CompileFunction(errors); 175 if (num_errors) 176 { 177 if (log) 178 log->Printf ("Error compiling function: \"%s\".", errors.GetData()); 179 return false; 180 } 181 182 errors.Clear(); 183 if (!m_get_class_name_function->WriteFunctionWrapper(exe_ctx, errors)) 184 { 185 if (log) 186 log->Printf ("Error Inserting function: \"%s\".", errors.GetData()); 187 return false; 188 } 189 } 190 191 if (m_get_class_name_code.get() == NULL || m_get_class_name_function.get() == NULL) 192 return false; 193 194 // Finally, write down the arguments, and call the function. Note that we will re-use the same space in the target 195 // for the args. We're locking this to ensure that only one thread at a time gets to call this function, so we don't 196 // have to worry about overwriting the arguments. 197 198 if (!m_get_class_name_function->WriteFunctionArguments (exe_ctx, m_get_class_name_args, find_class_name_address, dispatch_values, errors)) 199 return false; 200 201 bool stop_others = true; 202 bool try_all_threads = true; 203 bool unwind_on_error = true; 204 205 ExecutionResults results = m_get_class_name_function->ExecuteFunction (exe_ctx, 206 &m_get_class_name_args, 207 errors, 208 stop_others, 209 1000000, 210 try_all_threads, 211 unwind_on_error, 212 void_ptr_value); 213 214 if (results != eExecutionCompleted) 215 { 216 if (log) 217 log->Printf("Error evaluating our find class name function: %d.\n", results); 218 return false; 219 } 220 221 addr_t result_ptr = void_ptr_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); 222 Error error; 223 size_t chars_read = m_process->ReadCStringFromMemory (result_ptr, name_dst, max_name_len, error); 224 225 // If we exhausted our buffer before finding a NULL we're probably off in the weeds somewhere... 226 if (error.Fail() || chars_read == max_name_len) 227 return false; 228 else 229 return true; 230 231} 232 233bool 234AppleObjCRuntimeV2::GetDynamicTypeAndAddress (ValueObject &in_value, 235 DynamicValueType use_dynamic, 236 TypeAndOrName &class_type_or_name, 237 Address &address) 238{ 239 // The Runtime is attached to a particular process, you shouldn't pass in a value from another process. 240 assert (in_value.GetProcessSP().get() == m_process); 241 assert (m_process != NULL); 242 243 // Make sure we can have a dynamic value before starting... 244 if (CouldHaveDynamicValue (in_value)) 245 { 246 // First job, pull out the address at 0 offset from the object That will be the ISA pointer. 247 Error error; 248 const addr_t object_ptr = in_value.GetPointerValue(); 249 const addr_t isa_addr = m_process->ReadPointerFromMemory (object_ptr, error); 250 251 if (error.Fail()) 252 return false; 253 254 address.SetRawAddress(object_ptr); 255 256 // First check the cache... 257 SymbolContext sc; 258 class_type_or_name = LookupInClassNameCache (isa_addr); 259 260 if (!class_type_or_name.IsEmpty()) 261 { 262 if (class_type_or_name.GetTypeSP()) 263 return true; 264 else 265 return false; 266 } 267 268 // We don't have the object cached, so make sure the class 269 // address is readable, otherwise this is not a good object: 270 m_process->ReadPointerFromMemory (isa_addr, error); 271 272 if (error.Fail()) 273 return false; 274 275 const char *class_name = NULL; 276 Address isa_address; 277 Target &target = m_process->GetTarget(); 278 target.GetSectionLoadList().ResolveLoadAddress (isa_addr, isa_address); 279 280 if (isa_address.IsValid()) 281 { 282 // If the ISA pointer points to one of the sections in the binary, then see if we can 283 // get the class name from the symbols. 284 285 SectionSP section_sp (isa_address.GetSection()); 286 287 if (section_sp) 288 { 289 // If this points to a section that we know about, then this is 290 // some static class or nothing. See if it is in the right section 291 // and if its name is the right form. 292 ConstString section_name = section_sp->GetName(); 293 static ConstString g_objc_class_section_name ("__objc_data"); 294 if (section_name == g_objc_class_section_name) 295 { 296 isa_address.CalculateSymbolContext(&sc); 297 if (sc.symbol) 298 { 299 if (sc.symbol->GetType() == eSymbolTypeObjCClass) 300 class_name = sc.symbol->GetName().GetCString(); 301 } 302 } 303 } 304 } 305 306 char class_buffer[1024]; 307 if (class_name == NULL && use_dynamic == eDynamicCanRunTarget) 308 { 309 // If the class address didn't point into the binary, or 310 // it points into the right section but there wasn't a symbol 311 // there, try to look it up by calling the class method in the target. 312 313 ExecutionContext exe_ctx (in_value.GetExecutionContextRef()); 314 315 Thread *thread_to_use = exe_ctx.GetThreadPtr(); 316 317 if (thread_to_use == NULL) 318 thread_to_use = m_process->GetThreadList().GetSelectedThread().get(); 319 320 if (thread_to_use == NULL) 321 return false; 322 323 if (!RunFunctionToFindClassName (object_ptr, thread_to_use, class_buffer, 1024)) 324 return false; 325 326 class_name = class_buffer; 327 328 } 329 330 if (class_name && class_name[0]) 331 { 332 class_type_or_name.SetName (class_name); 333 334 TypeList class_types; 335 SymbolContext sc; 336 uint32_t num_matches = target.GetImages().FindTypes (sc, 337 class_type_or_name.GetName(), 338 true, 339 UINT32_MAX, 340 class_types); 341 if (num_matches == 1) 342 { 343 class_type_or_name.SetTypeSP (class_types.GetTypeAtIndex(0)); 344 return true; 345 } 346 else 347 { 348 for (size_t i = 0; i < num_matches; i++) 349 { 350 TypeSP this_type(class_types.GetTypeAtIndex(i)); 351 if (this_type) 352 { 353 // Only consider "real" ObjC classes. For now this means avoiding 354 // the Type objects that are made up from the OBJC_CLASS_$_<NAME> symbols. 355 // we don't want to use them since they are empty and useless. 356 if (this_type->IsRealObjCClass()) 357 { 358 // There can only be one type with a given name, 359 // so we've just found duplicate definitions, and this 360 // one will do as well as any other. 361 // We don't consider something to have a dynamic type if 362 // it is the same as the static type. So compare against 363 // the value we were handed: 364 365 clang::ASTContext *in_ast_ctx = in_value.GetClangAST (); 366 clang::ASTContext *this_ast_ctx = this_type->GetClangAST (); 367 if (in_ast_ctx != this_ast_ctx 368 || !ClangASTContext::AreTypesSame (in_ast_ctx, 369 in_value.GetClangType(), 370 this_type->GetClangFullType())) 371 { 372 class_type_or_name.SetTypeSP (this_type); 373 } 374 break; 375 } 376 } 377 } 378 } 379 380 AddToClassNameCache (isa_addr, class_type_or_name); 381 if (class_type_or_name.GetTypeSP()) 382 return true; 383 else 384 return false; 385 } 386 } 387 388 return false; 389} 390 391//------------------------------------------------------------------ 392// Static Functions 393//------------------------------------------------------------------ 394LanguageRuntime * 395AppleObjCRuntimeV2::CreateInstance (Process *process, LanguageType language) 396{ 397 // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make 398 // sure we aren't using the V1 runtime. 399 if (language == eLanguageTypeObjC) 400 { 401 ModuleSP objc_module_sp; 402 403 if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == eAppleObjC_V2) 404 return new AppleObjCRuntimeV2 (process, objc_module_sp); 405 else 406 return NULL; 407 } 408 else 409 return NULL; 410} 411 412void 413AppleObjCRuntimeV2::Initialize() 414{ 415 PluginManager::RegisterPlugin (pluginName, 416 pluginDesc, 417 CreateInstance); 418} 419 420void 421AppleObjCRuntimeV2::Terminate() 422{ 423 PluginManager::UnregisterPlugin (CreateInstance); 424} 425 426//------------------------------------------------------------------ 427// PluginInterface protocol 428//------------------------------------------------------------------ 429const char * 430AppleObjCRuntimeV2::GetPluginName() 431{ 432 return pluginName; 433} 434 435const char * 436AppleObjCRuntimeV2::GetShortPluginName() 437{ 438 return pluginShort; 439} 440 441uint32_t 442AppleObjCRuntimeV2::GetPluginVersion() 443{ 444 return 1; 445} 446 447BreakpointResolverSP 448AppleObjCRuntimeV2::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) 449{ 450 BreakpointResolverSP resolver_sp; 451 452 if (throw_bp) 453 resolver_sp.reset (new BreakpointResolverName (bkpt, 454 "objc_exception_throw", 455 eFunctionNameTypeBase, 456 Breakpoint::Exact, 457 eLazyBoolNo)); 458 // FIXME: We don't do catch breakpoints for ObjC yet. 459 // Should there be some way for the runtime to specify what it can do in this regard? 460 return resolver_sp; 461} 462 463ClangUtilityFunction * 464AppleObjCRuntimeV2::CreateObjectChecker(const char *name) 465{ 466 char check_function_code[2048]; 467 468 int len = 0; 469 if (m_has_object_getClass) 470 { 471 len = ::snprintf (check_function_code, 472 sizeof(check_function_code), 473 "extern \"C\" void *gdb_object_getClass(void *); \n" 474 "extern \"C\" int printf(const char *format, ...); \n" 475 "extern \"C\" void \n" 476 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" 477 "{ \n" 478 " if ($__lldb_arg_obj == (void *)0) \n" 479 " return; // nil is ok \n" 480 " if (!gdb_object_getClass($__lldb_arg_obj)) \n" 481 " *((volatile int *)0) = 'ocgc'; \n" 482 " else if ($__lldb_arg_selector != (void *)0) \n" 483 " { \n" 484 " signed char responds = (signed char) [(id) $__lldb_arg_obj \n" 485 " respondsToSelector: \n" 486 " (struct objc_selector *) $__lldb_arg_selector]; \n" 487 " if (responds == (signed char) 0) \n" 488 " *((volatile int *)0) = 'ocgc'; \n" 489 " } \n" 490 "} \n", 491 name); 492 } 493 else 494 { 495 len = ::snprintf (check_function_code, 496 sizeof(check_function_code), 497 "extern \"C\" void *gdb_class_getClass(void *); \n" 498 "extern \"C\" int printf(const char *format, ...); \n" 499 "extern \"C\" void \n" 500 "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n" 501 "{ \n" 502 " if ($__lldb_arg_obj == (void *)0) \n" 503 " return; // nil is ok \n" 504 " void **$isa_ptr = (void **)$__lldb_arg_obj; \n" 505 " if (*$isa_ptr == (void *)0 || !gdb_class_getClass(*$isa_ptr)) \n" 506 " *((volatile int *)0) = 'ocgc'; \n" 507 " else if ($__lldb_arg_selector != (void *)0) \n" 508 " { \n" 509 " signed char responds = (signed char) [(id) $__lldb_arg_obj \n" 510 " respondsToSelector: \n" 511 " (struct objc_selector *) $__lldb_arg_selector]; \n" 512 " if (responds == (signed char) 0) \n" 513 " *((volatile int *)0) = 'ocgc'; \n" 514 " } \n" 515 "} \n", 516 name); 517 } 518 519 assert (len < sizeof(check_function_code)); 520 521 return new ClangUtilityFunction(check_function_code, name); 522} 523 524size_t 525AppleObjCRuntimeV2::GetByteOffsetForIvar (ClangASTType &parent_ast_type, const char *ivar_name) 526{ 527 const char *class_name = parent_ast_type.GetConstTypeName().AsCString(); 528 529 if (!class_name || *class_name == '\0' || !ivar_name || *ivar_name == '\0') 530 return LLDB_INVALID_IVAR_OFFSET; 531 532 std::string buffer("OBJC_IVAR_$_"); 533 buffer.append (class_name); 534 buffer.push_back ('.'); 535 buffer.append (ivar_name); 536 ConstString ivar_const_str (buffer.c_str()); 537 538 SymbolContextList sc_list; 539 Target &target = m_process->GetTarget(); 540 541 target.GetImages().FindSymbolsWithNameAndType(ivar_const_str, eSymbolTypeRuntime, sc_list); 542 543 SymbolContext ivar_offset_symbol; 544 if (sc_list.GetSize() != 1 545 || !sc_list.GetContextAtIndex(0, ivar_offset_symbol) 546 || ivar_offset_symbol.symbol == NULL) 547 return LLDB_INVALID_IVAR_OFFSET; 548 549 addr_t ivar_offset_address = ivar_offset_symbol.symbol->GetValue().GetLoadAddress (&target); 550 551 Error error; 552 553 uint32_t ivar_offset = m_process->ReadUnsignedIntegerFromMemory (ivar_offset_address, 554 4, 555 LLDB_INVALID_IVAR_OFFSET, 556 error); 557 return ivar_offset; 558} 559 560// tagged pointers are marked by having their least-significant bit 561// set. this makes them "invalid" as pointers because they violate 562// the alignment requirements. of course, this detection algorithm 563// is not accurate (it might become better by incorporating further 564// knowledge about the internals of tagged pointers) 565bool 566AppleObjCRuntimeV2::IsTaggedPointer(addr_t ptr) 567{ 568 return (ptr & 0x01); 569} 570 571 572// this code relies on the assumption that an Objective-C object always starts 573// with an ISA at offset 0. an ISA is effectively a pointer to an instance of 574// struct class_t in the ObjCv2 runtime 575ObjCLanguageRuntime::ObjCISA 576AppleObjCRuntimeV2::GetISA(ValueObject& valobj) 577{ 578 if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != eLanguageTypeObjC) 579 return 0; 580 581 // if we get an invalid VO (which might still happen when playing around 582 // with pointers returned by the expression parser, don't consider this 583 // a valid ObjC object) 584 if (valobj.GetValue().GetContextType() == Value::eContextTypeInvalid) 585 return 0; 586 587 addr_t isa_pointer = valobj.GetPointerValue(); 588 589 // tagged pointer 590 if (IsTaggedPointer(isa_pointer)) 591 return g_objc_Tagged_ISA; 592 593 ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); 594 595 Process *process = exe_ctx.GetProcessPtr(); 596 if (process) 597 { 598 uint8_t pointer_size = process->GetAddressByteSize(); 599 600 Error error; 601 return process->ReadUnsignedIntegerFromMemory (isa_pointer, 602 pointer_size, 603 0, 604 error); 605 } 606 return 0; 607} 608 609// TODO: should we have a transparent_kvo parameter here to say if we 610// want to replace the KVO swizzled class with the actual user-level type? 611ConstString 612AppleObjCRuntimeV2::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) 613{ 614 static const ConstString g_unknown ("unknown"); 615 616 if (!IsValidISA(isa)) 617 return ConstString(); 618 619 if (isa == g_objc_Tagged_ISA) 620 { 621 static const ConstString g_objc_tagged_isa_name ("_lldb_Tagged_ObjC_ISA"); 622 return g_objc_tagged_isa_name; 623 } 624 625 ISAToNameIterator found = m_isa_to_name_cache.find(isa); 626 ISAToNameIterator end = m_isa_to_name_cache.end(); 627 628 if (found != end) 629 return found->second; 630 631 uint8_t pointer_size = m_process->GetAddressByteSize(); 632 Error error; 633 634 /* 635 struct class_t *isa; 636 struct class_t *superclass; 637 Cache cache; 638 IMP *vtable; 639--> class_rw_t data; 640 */ 641 642 addr_t rw_pointer = isa + (4 * pointer_size); 643 //printf("rw_pointer: %llx\n", rw_pointer); 644 uint64_t data_pointer = m_process->ReadUnsignedIntegerFromMemory(rw_pointer, 645 pointer_size, 646 0, 647 error); 648 if (error.Fail()) 649 { 650 return g_unknown; 651 652 } 653 654 /* 655 uint32_t flags; 656 uint32_t version; 657 658--> const class_ro_t *ro; 659 */ 660 data_pointer += 8; 661 //printf("data_pointer: %llx\n", data_pointer); 662 uint64_t ro_pointer = m_process->ReadUnsignedIntegerFromMemory(data_pointer, 663 pointer_size, 664 0, 665 error); 666 if (error.Fail()) 667 return g_unknown; 668 669 /* 670 uint32_t flags; 671 uint32_t instanceStart; 672 uint32_t instanceSize; 673 #ifdef __LP64__ 674 uint32_t reserved; 675 #endif 676 677 const uint8_t * ivarLayout; 678 679--> const char * name; 680 */ 681 ro_pointer += 12; 682 if (pointer_size == 8) 683 ro_pointer += 4; 684 ro_pointer += pointer_size; 685 //printf("ro_pointer: %llx\n", ro_pointer); 686 uint64_t name_pointer = m_process->ReadUnsignedIntegerFromMemory(ro_pointer, 687 pointer_size, 688 0, 689 error); 690 if (error.Fail()) 691 return g_unknown; 692 693 //printf("name_pointer: %llx\n", name_pointer); 694 char cstr[512]; 695 if (m_process->ReadCStringFromMemory(name_pointer, cstr, sizeof(cstr), error) > 0) 696 { 697 if (::strstr(cstr, "NSKVONotify") == cstr) 698 { 699 // the ObjC runtime implements KVO by replacing the isa with a special 700 // NSKVONotifying_className that overrides the relevant methods 701 // the side effect on us is that getting the typename for a KVO-ed object 702 // will return the swizzled class instead of the actual one 703 // this swizzled class is a descendant of the real class, so just 704 // return the parent type and all should be fine 705 ConstString class_name = GetActualTypeName(GetParentClass(isa)); 706 m_isa_to_name_cache[isa] = class_name; 707 return class_name; 708 } 709 else 710 { 711 ConstString class_name = ConstString(cstr); 712 m_isa_to_name_cache[isa] = class_name; 713 return class_name; 714 } 715 } 716 else 717 return g_unknown; 718} 719 720ObjCLanguageRuntime::ObjCISA 721AppleObjCRuntimeV2::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) 722{ 723 if (!IsValidISA(isa)) 724 return 0; 725 726 if (isa == g_objc_Tagged_ISA) 727 return 0; 728 729 ISAToParentIterator found = m_isa_to_parent_cache.find(isa); 730 ISAToParentIterator end = m_isa_to_parent_cache.end(); 731 732 if (found != end) 733 return found->second; 734 735 uint8_t pointer_size = m_process->GetAddressByteSize(); 736 Error error; 737 /* 738 struct class_t *isa; 739--> struct class_t *superclass; 740 */ 741 addr_t parent_pointer = isa + pointer_size; 742 //printf("rw_pointer: %llx\n", rw_pointer); 743 744 uint64_t parent_isa = m_process->ReadUnsignedIntegerFromMemory(parent_pointer, 745 pointer_size, 746 0, 747 error); 748 if (error.Fail()) 749 return 0; 750 751 m_isa_to_parent_cache[isa] = parent_isa; 752 753 return parent_isa; 754} 755 756SymbolVendor * 757AppleObjCRuntimeV2::GetSymbolVendor() 758{ 759 if (!m_symbol_vendor_ap.get()) 760 m_symbol_vendor_ap.reset(new AppleObjCSymbolVendor(m_process)); 761 762 return m_symbol_vendor_ap.get(); 763} 764