AppleObjCRuntimeV2.cpp revision 0c3d6d6eb941a60b44fbf49e879601d4e5ccebba
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/StopInfo.h" 37#include "lldb/Target/Target.h" 38#include "lldb/Target/Thread.h" 39 40#include "AppleObjCRuntimeV2.h" 41#include "AppleObjCTrampolineHandler.h" 42 43 44#include <vector> 45 46using namespace lldb; 47using namespace lldb_private; 48 49 50static const char *pluginName = "AppleObjCRuntimeV2"; 51static const char *pluginDesc = "Apple Objective C Language Runtime - Version 2"; 52static const char *pluginShort = "language.apple.objc.v2"; 53 54 55const char *AppleObjCRuntimeV2::g_find_class_name_function_name = "__lldb_apple_objc_v2_find_class_name"; 56const char *AppleObjCRuntimeV2::g_find_class_name_function_body = " \n\ 57extern \"C\" \n\ 58{ \n\ 59 extern void *gdb_class_getClass (void *objc_class); \n\ 60 extern void *class_getName(void *objc_class); \n\ 61 extern int printf(const char *format, ...); \n\ 62} \n\ 63 \n\ 64struct __lldb_objc_object { \n\ 65 void *isa; \n\ 66}; \n\ 67 \n\ 68extern \"C\" void *__lldb_apple_objc_v2_find_class_name ( \n\ 69 __lldb_objc_object *object_ptr, \n\ 70 int debug) \n\ 71{ \n\ 72 void *name = 0; \n\ 73 if (debug) \n\ 74 printf (\"\\n*** Called in v2_find_class_name with object: 0x%p\\n\", object_ptr); \n\ 75 // Call gdb_class_getClass so we can tell if the class is good. \n\ 76 void *objc_class = gdb_class_getClass (object_ptr->isa); \n\ 77 if (objc_class) \n\ 78 { \n\ 79 void *actual_class = (void *) [(id) object_ptr class]; \n\ 80 if (actual_class != 0) \n\ 81 name = class_getName((void *) actual_class); \n\ 82 if (debug) \n\ 83 printf (\"\\n*** Found name: %s\\n\", name ? name : \"<NOT FOUND>\"); \n\ 84 } \n\ 85 else if (debug) \n\ 86 printf (\"\\n*** gdb_class_getClass returned NULL\\n\"); \n\ 87 return name; \n\ 88} \n\ 89"; 90 91const char *AppleObjCRuntimeV2::g_objc_class_symbol_prefix = "OBJC_CLASS_$_"; 92const char *AppleObjCRuntimeV2::g_objc_class_data_section_name = "__objc_data"; 93 94AppleObjCRuntimeV2::AppleObjCRuntimeV2 (Process *process, ModuleSP &objc_module_sp) : 95 lldb_private::AppleObjCRuntime (process), 96 m_get_class_name_args(LLDB_INVALID_ADDRESS), 97 m_get_class_name_args_mutex(Mutex::eMutexTypeNormal) 98{ 99 m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(ConstString("gdb_object_getClass")) != NULL); 100} 101 102bool 103AppleObjCRuntimeV2::RunFunctionToFindClassName(lldb::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(lldb_private::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 lldb::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 lldb::clang_type_t clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::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 lldb::addr_t result_ptr = void_ptr_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); 222 size_t chars_read = m_process->ReadCStringFromMemory (result_ptr, name_dst, max_name_len); 223 224 // If we exhausted our buffer before finding a NULL we're probably off in the weeds somewhere... 225 if (chars_read == max_name_len) 226 return false; 227 else 228 return true; 229 230} 231 232bool 233AppleObjCRuntimeV2::GetDynamicTypeAndAddress (ValueObject &in_value, 234 lldb::DynamicValueType use_dynamic, 235 TypeAndOrName &class_type_or_name, 236 Address &address) 237{ 238 // The Runtime is attached to a particular process, you shouldn't pass in a value from another process. 239 assert (in_value.GetUpdatePoint().GetProcessSP().get() == m_process); 240 241 // Make sure we can have a dynamic value before starting... 242 if (CouldHaveDynamicValue (in_value)) 243 { 244 // First job, pull out the address at 0 offset from the object That will be the ISA pointer. 245 AddressType address_type; 246 lldb::addr_t original_ptr = in_value.GetPointerValue(address_type, true); 247 248 // ObjC only has single inheritance, so the objects all start at the same pointer value. 249 address.SetSection (NULL); 250 address.SetOffset (original_ptr); 251 252 if (original_ptr == LLDB_INVALID_ADDRESS) 253 return false; 254 255 Target *target = m_process->CalculateTarget(); 256 257 char memory_buffer[16]; 258 DataExtractor data (memory_buffer, 259 sizeof(memory_buffer), 260 m_process->GetByteOrder(), 261 m_process->GetAddressByteSize()); 262 263 size_t address_byte_size = m_process->GetAddressByteSize(); 264 Error error; 265 size_t bytes_read = m_process->ReadMemory (original_ptr, 266 memory_buffer, 267 address_byte_size, 268 error); 269 if (!error.Success() || (bytes_read != address_byte_size)) 270 { 271 return false; 272 } 273 274 uint32_t offset = 0; 275 lldb::addr_t isa_addr = data.GetAddress (&offset); 276 277 if (offset == 0) 278 return false; 279 280 // Make sure the class address is readable, otherwise this is not a good object: 281 bytes_read = m_process->ReadMemory (isa_addr, 282 memory_buffer, 283 address_byte_size, 284 error); 285 if (bytes_read != address_byte_size) 286 return false; 287 288 // First check the cache... 289 290 SymbolContext sc; 291 292 class_type_or_name = LookupInClassNameCache (isa_addr); 293 294 if (!class_type_or_name.IsEmpty()) 295 { 296 if (class_type_or_name.GetTypeSP() != NULL) 297 return true; 298 else 299 return false; 300 } 301 302 const char *class_name = NULL; 303 Address isa_address; 304 target->GetSectionLoadList().ResolveLoadAddress (isa_addr, isa_address); 305 306 if (isa_address.IsValid()) 307 { 308 // If the ISA pointer points to one of the sections in the binary, then see if we can 309 // get the class name from the symbols. 310 311 const Section *section = isa_address.GetSection(); 312 313 if (section) 314 { 315 // If this points to a section that we know about, then this is 316 // some static class or nothing. See if it is in the right section 317 // and if its name is the right form. 318 ConstString section_name = section->GetName(); 319 if (section_name == ConstString(g_objc_class_data_section_name)) 320 { 321 isa_address.CalculateSymbolContext(&sc); 322 if (sc.symbol) 323 { 324 class_name = sc.symbol->GetName().AsCString(); 325 if (strstr (class_name, g_objc_class_symbol_prefix) == class_name) 326 class_name += strlen (g_objc_class_symbol_prefix); 327 else 328 return false; 329 } 330 } 331 } 332 } 333 334 char class_buffer[1024]; 335 if (class_name == NULL && use_dynamic != lldb::eDynamicDontRunTarget) 336 { 337 // If the class address didn't point into the binary, or 338 // it points into the right section but there wasn't a symbol 339 // there, try to look it up by calling the class method in the target. 340 ExecutionContextScope *exe_scope = in_value.GetUpdatePoint().GetExecutionContextScope(); 341 Thread *thread_to_use; 342 if (exe_scope) 343 thread_to_use = exe_scope->CalculateThread(); 344 345 if (thread_to_use == NULL) 346 thread_to_use = m_process->GetThreadList().GetSelectedThread().get(); 347 348 if (thread_to_use == NULL) 349 return false; 350 351 if (!RunFunctionToFindClassName (original_ptr, thread_to_use, class_buffer, 1024)) 352 return false; 353 354 class_name = class_buffer; 355 356 } 357 358 if (class_name != NULL && *class_name != '\0') 359 { 360 class_type_or_name.SetName (class_name); 361 362 TypeList class_types; 363 uint32_t num_matches = target->GetImages().FindTypes (sc, 364 class_type_or_name.GetName(), 365 true, 366 UINT32_MAX, 367 class_types); 368 if (num_matches == 1) 369 { 370 class_type_or_name.SetTypeSP (class_types.GetTypeAtIndex(0)); 371 return true; 372 } 373 else 374 { 375 for (size_t i = 0; i < num_matches; i++) 376 { 377 lldb::TypeSP this_type(class_types.GetTypeAtIndex(i)); 378 if (this_type) 379 { 380 if (ClangASTContext::IsObjCClassType(this_type->GetClangFullType())) 381 { 382 // There can only be one type with a given name, 383 // so we've just found duplicate definitions, and this 384 // one will do as well as any other. 385 // We don't consider something to have a dynamic type if 386 // it is the same as the static type. So compare against 387 // the value we were handed: 388 389 clang::ASTContext *in_ast_ctx = in_value.GetClangAST (); 390 clang::ASTContext *this_ast_ctx = this_type->GetClangAST (); 391 if (in_ast_ctx != this_ast_ctx 392 || !ClangASTContext::AreTypesSame (in_ast_ctx, 393 in_value.GetClangType(), 394 this_type->GetClangFullType())) 395 { 396 class_type_or_name.SetTypeSP (this_type); 397 } 398 break; 399 } 400 } 401 } 402 } 403 404 AddToClassNameCache (isa_addr, class_type_or_name); 405 if (class_type_or_name.GetTypeSP()) 406 return true; 407 else 408 return false; 409 } 410 } 411 412 return false; 413} 414 415//------------------------------------------------------------------ 416// Static Functions 417//------------------------------------------------------------------ 418lldb_private::LanguageRuntime * 419AppleObjCRuntimeV2::CreateInstance (Process *process, lldb::LanguageType language) 420{ 421 // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make 422 // sure we aren't using the V1 runtime. 423 if (language == eLanguageTypeObjC) 424 { 425 ModuleSP objc_module_sp; 426 427 if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == eAppleObjC_V2) 428 return new AppleObjCRuntimeV2 (process, objc_module_sp); 429 else 430 return NULL; 431 } 432 else 433 return NULL; 434} 435 436void 437AppleObjCRuntimeV2::Initialize() 438{ 439 PluginManager::RegisterPlugin (pluginName, 440 pluginDesc, 441 CreateInstance); 442} 443 444void 445AppleObjCRuntimeV2::Terminate() 446{ 447 PluginManager::UnregisterPlugin (CreateInstance); 448} 449 450//------------------------------------------------------------------ 451// PluginInterface protocol 452//------------------------------------------------------------------ 453const char * 454AppleObjCRuntimeV2::GetPluginName() 455{ 456 return pluginName; 457} 458 459const char * 460AppleObjCRuntimeV2::GetShortPluginName() 461{ 462 return pluginShort; 463} 464 465uint32_t 466AppleObjCRuntimeV2::GetPluginVersion() 467{ 468 return 1; 469} 470 471void 472AppleObjCRuntimeV2::SetExceptionBreakpoints () 473{ 474 if (!m_process) 475 return; 476 477 if (!m_objc_exception_bp_sp) 478 { 479 m_objc_exception_bp_sp = m_process->GetTarget().CreateBreakpoint (NULL, 480 "__cxa_throw", 481 eFunctionNameTypeBase, 482 true); 483 } 484 else 485 m_objc_exception_bp_sp->SetEnabled (true); 486} 487 488ClangUtilityFunction * 489AppleObjCRuntimeV2::CreateObjectChecker(const char *name) 490{ 491 char check_function_code[1024]; 492 493 int len = 0; 494 if (m_has_object_getClass) 495 { 496 len = ::snprintf (check_function_code, 497 sizeof(check_function_code), 498 "extern \"C\" void *gdb_object_getClass(void *); \n" 499 "extern \"C\" void \n" 500 "%s(void *$__lldb_arg_obj) \n" 501 "{ \n" 502 " if ($__lldb_arg_obj == (void *)0) \n" 503 " return; // nil is ok \n" 504 " if (!gdb_object_getClass($__lldb_arg_obj)) \n" 505 " *((volatile int *)0) = 'ocgc'; \n" 506 "} \n", 507 name); 508 } 509 else 510 { 511 len = ::snprintf (check_function_code, 512 sizeof(check_function_code), 513 "extern \"C\" void *gdb_class_getClass(void *); \n" 514 "extern \"C\" void \n" 515 "%s(void *$__lldb_arg_obj) \n" 516 "{ \n" 517 " if ($__lldb_arg_obj == (void *)0) \n" 518 " return; // nil is ok \n" 519 " void **$isa_ptr = (void **)$__lldb_arg_obj; \n" 520 " if (*$isa_ptr == (void *)0 || !gdb_class_getClass(*$isa_ptr)) \n" 521 " *((volatile int *)0) = 'ocgc'; \n" 522 "} \n", 523 name); 524 } 525 526 assert (len < sizeof(check_function_code)); 527 528 return new ClangUtilityFunction(check_function_code, name); 529} 530 531size_t 532AppleObjCRuntimeV2::GetByteOffsetForIvar (ClangASTType &parent_ast_type, const char *ivar_name) 533{ 534 const char *class_name = parent_ast_type.GetConstTypeName().AsCString(); 535 536 if (!class_name || *class_name == '\0' || !ivar_name || *ivar_name == '\0') 537 return LLDB_INVALID_IVAR_OFFSET; 538 539 std::string buffer("OBJC_IVAR_$_"); 540 buffer.append (class_name); 541 buffer.push_back ('.'); 542 buffer.append (ivar_name); 543 ConstString ivar_const_str (buffer.c_str()); 544 545 SymbolContextList sc_list; 546 Target *target = &(m_process->GetTarget()); 547 548 target->GetImages().FindSymbolsWithNameAndType(ivar_const_str, eSymbolTypeRuntime, sc_list); 549 550 SymbolContext ivar_offset_symbol; 551 if (sc_list.GetSize() != 1 552 || !sc_list.GetContextAtIndex(0, ivar_offset_symbol) 553 || ivar_offset_symbol.symbol == NULL) 554 return LLDB_INVALID_IVAR_OFFSET; 555 556 lldb::addr_t ivar_offset_address = ivar_offset_symbol.symbol->GetValue().GetLoadAddress(target); 557 558 Error error; 559 560 uint32_t ivar_offset = m_process->ReadUnsignedIntegerFromMemory (ivar_offset_address, 561 4, 562 LLDB_INVALID_IVAR_OFFSET, 563 error); 564 return ivar_offset; 565} 566 567// tagged pointers are marked by having their least-significant bit 568// set. this makes them "invalid" as pointers because they violate 569// the alignment requirements. this way, we can always know when 570// we are dealing with a tagged pointer, and use the lookup approach 571// that the runtime would 572bool 573AppleObjCRuntimeV2::IsTaggedPointer(lldb::addr_t ptr) 574{ 575 return (ptr & 0x01); 576} 577 578 579lldb_private::ObjCLanguageRuntime::ObjCISA 580AppleObjCRuntimeV2::GetISA(ValueObject& valobj) 581{ 582 583 if (valobj.GetIsExpressionResult() && 584 valobj.GetValue().GetValueType() == Value::eValueTypeHostAddress) 585 { 586 // when using the expression parser, an additional layer of "frozen data" 587 // can be created, which is basically a byte-exact copy of the data returned 588 // by the expression, but in host memory. because Python code might need to read 589 // into the object memory in non-obvious ways, we need to hand it the target version 590 // of the expression output 591 lldb::addr_t tgt_address = valobj.GetValueAsUnsigned(); 592 ValueObjectSP target_object = ValueObjectConstResult::Create (valobj.GetExecutionContextScope(), 593 valobj.GetClangAST(), 594 valobj.GetClangType(), 595 valobj.GetName(), 596 tgt_address, 597 eAddressTypeLoad, 598 valobj.GetUpdatePoint().GetProcessSP()->GetAddressByteSize()); 599 return GetISA(*target_object); 600 } 601 602 if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != lldb::eLanguageTypeObjC) 603 return 0; 604 605 // if we get an invalid VO (which might still happen when playing around 606 // with pointers returned by the expression parser, don't consider this 607 // a valid ObjC object) 608 if (valobj.GetValue().GetContextType() == Value::eContextTypeInvalid) 609 return 0; 610 611 uint32_t offset = 0; 612 uint64_t isa_pointer = valobj.GetDataExtractor().GetPointer(&offset); 613 614 // tagged pointer 615 if (IsTaggedPointer(isa_pointer)) 616 return g_objc_Tagged_ISA; 617 618 uint8_t pointer_size = valobj.GetUpdatePoint().GetProcessSP()->GetAddressByteSize(); 619 620 Error error; 621 lldb_private::ObjCLanguageRuntime::ObjCISA isa = 622 valobj.GetUpdatePoint().GetProcessSP()->ReadUnsignedIntegerFromMemory(isa_pointer, 623 pointer_size, 624 0, 625 error); 626 return isa; 627} 628 629// TODO: should we have a transparent_kvo parameter here to say if we 630// want to replace the KVO swizzled class with the actual user-level type? 631ConstString 632AppleObjCRuntimeV2::GetActualTypeName(lldb_private::ObjCLanguageRuntime::ObjCISA isa) 633{ 634 if (!IsValidISA(isa)) 635 return ConstString(NULL); 636 637 if (isa == g_objc_Tagged_ISA) 638 return ConstString("_lldb_Tagged_ObjC_ISA"); 639 640 uint8_t pointer_size = m_process->GetAddressByteSize(); 641 Error error; 642 lldb::addr_t rw_pointer = isa + (4 * pointer_size); 643 //printf("rw_pointer: %llx\n", rw_pointer); 644 645 uint64_t data_pointer = m_process->ReadUnsignedIntegerFromMemory(rw_pointer, 646 pointer_size, 647 0, 648 error); 649 if (error.Fail()) 650 return ConstString("unknown"); 651 652 data_pointer += 8; 653 //printf("data_pointer: %llx\n", data_pointer); 654 uint64_t ro_pointer = m_process->ReadUnsignedIntegerFromMemory(data_pointer, 655 pointer_size, 656 0, 657 error); 658 if (error.Fail()) 659 return ConstString("unknown"); 660 661 ro_pointer += 12; 662 if (pointer_size == 8) 663 ro_pointer += 4; 664 ro_pointer += pointer_size; 665 //printf("ro_pointer: %llx\n", ro_pointer); 666 uint64_t name_pointer = m_process->ReadUnsignedIntegerFromMemory(ro_pointer, 667 pointer_size, 668 0, 669 error); 670 if (error.Fail()) 671 return ConstString("unknown"); 672 673 //printf("name_pointer: %llx\n", name_pointer); 674 char* cstr = new char[512]; 675 if (m_process->ReadCStringFromMemory(name_pointer, cstr, 512) > 0) 676 { 677 if (::strstr(cstr, "NSKVONotify") == cstr) 678 { 679 // the ObjC runtime implements KVO by replacing the isa with a special 680 // NSKVONotifying_className that overrides the relevant methods 681 // the side effect on us is that getting the typename for a KVO-ed object 682 // will return the swizzled class instead of the actual one 683 // this swizzled class is a descendant of the real class, so just 684 // return the parent type and all should be fine 685 return GetActualTypeName(GetParentClass(isa)); 686 } 687 else 688 return ConstString(cstr); 689 } 690 else 691 return ConstString("unknown"); 692} 693 694lldb_private::ObjCLanguageRuntime::ObjCISA 695AppleObjCRuntimeV2::GetParentClass(lldb_private::ObjCLanguageRuntime::ObjCISA isa) 696{ 697 if (!IsValidISA(isa)) 698 return 0; 699 700 if (isa == g_objc_Tagged_ISA) 701 return 0; 702 703 uint8_t pointer_size = m_process->GetAddressByteSize(); 704 Error error; 705 lldb::addr_t parent_pointer = isa + pointer_size; 706 //printf("rw_pointer: %llx\n", rw_pointer); 707 708 uint64_t parent_isa = m_process->ReadUnsignedIntegerFromMemory(parent_pointer, 709 pointer_size, 710 0, 711 error); 712 if (error.Fail()) 713 return 0; 714 return parent_isa; 715} 716 717