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