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